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