1 //
2 // Copyright (C) 2020 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <chrono>
18 #include <fstream>
19 #include <iostream>
20 #include <random>
21 #include <string>
22
23 #include <string.h>
24 #include <stdlib.h>
25 #include <sys/mman.h>
26
27 #include <android-base/parseint.h>
28
29 static constexpr size_t kBytesPerMb = 1048576;
30 const size_t kMemoryAllocationSize = 2 * 1024 * kBytesPerMb;
31
32 #define USE_MLOCKALL 0
33
GetProcessStatus(const char * key)34 std::string GetProcessStatus(const char* key) {
35 // Build search pattern of key and separator.
36 std::string pattern(key);
37 pattern.push_back(':');
38
39 // Search for status lines starting with pattern.
40 std::ifstream fs("/proc/self/status");
41 std::string line;
42 while (std::getline(fs, line)) {
43 if (strncmp(pattern.c_str(), line.c_str(), pattern.size()) == 0) {
44 // Skip whitespace in matching line (if any).
45 size_t pos = line.find_first_not_of(" \t", pattern.size());
46 if (pos == std::string::npos) {
47 break;
48 }
49 return std::string(line, pos);
50 }
51 }
52 return "<unknown>";
53 }
54
main(int argc,char ** argv)55 int main(int argc, char** argv) {
56 size_t allocationSize = 0;
57 if (argc >= 2) {
58 if (!android::base::ParseUint(argv[1], /*out*/&allocationSize)) {
59 std::cerr << "Failed to parse the allocation size (must be 0,MAX_SIZE_T)" << std::endl;
60 return 1;
61 }
62 } else {
63 allocationSize = kMemoryAllocationSize;
64 }
65
66 void* mem = malloc(allocationSize);
67 if (mem == nullptr) {
68 std::cerr << "Malloc failed" << std::endl;
69 return 1;
70 }
71
72 volatile int* imem = static_cast<int *>(mem); // don't optimize out memory usage
73
74 size_t imemCount = allocationSize / sizeof(int);
75
76 std::cout << "Allocated " << allocationSize << " bytes" << std::endl;
77
78 auto seed = std::chrono::high_resolution_clock::now().time_since_epoch().count();
79 std::mt19937 mt_rand(seed);
80
81 size_t randPrintCount = 10;
82
83 // Write random numbers:
84 // * Ensures each page is resident
85 // * Avoids zeroed out pages (zRAM)
86 // * Avoids same-page merging
87 for (size_t i = 0; i < imemCount; ++i) {
88 imem[i] = mt_rand();
89
90 if (i < randPrintCount) {
91 std::cout << "Generated random value: " << imem[i] << std::endl;
92 }
93 }
94
95 #if USE_MLOCKALL
96 /*
97 * Lock all pages from the address space of this process.
98 */
99 if (mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
100 std::cerr << "Mlockall failed" << std::endl;
101 return 1;
102 }
103 #else
104 // Use mlock because of the predictable VmLck size.
105 // Using mlockall tends to bring in anywhere from 2-2.5GB depending on the device.
106 if (mlock(mem, allocationSize) != 0) {
107 std::cerr << "Mlock failed" << std::endl;
108 return 1;
109 }
110 #endif
111
112 // Validate memory is actually resident and locked with:
113 // $> cat /proc/$(pidof iorap.stress.memory)/status | grep VmLck
114 std::cout << "Locked memory (VmLck) = " << GetProcessStatus("VmLck") << std::endl;
115
116 std::cout << "Press any key to terminate" << std::endl;
117 int any_input;
118 std::cin >> any_input;
119
120 std::cout << "Terminating..." << std::endl;
121
122 munlockall();
123 free(mem);
124
125 return 0;
126 }
127