1 /*
2  * Copyright (C) 2019 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 <memory>
18 
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <inttypes.h>
22 #include <signal.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <unistd.h>
29 
30 namespace {
31 
32 constexpr auto kIdleSize = 10000 * 4096;
33 constexpr auto kNoIdleSize = 1000 * 4096;
34 
35 static bool interrupted = false;
36 
37 volatile unsigned char* __attribute__((noinline)) AllocIdle(size_t bytes);
AllocIdle(size_t bytes)38 volatile unsigned char* __attribute__((noinline)) AllocIdle(size_t bytes) {
39   // This volatile is needed to prevent the compiler from trying to be
40   // helpful and compiling a "useless" malloc + free into a noop.
41   volatile unsigned char* x = static_cast<unsigned char*>(malloc(bytes));
42   if (x) {
43     x[1] = 'x';
44   }
45   return x;
46 }
47 
48 volatile unsigned char* __attribute__((noinline)) AllocNoIdle(size_t bytes);
AllocNoIdle(size_t bytes)49 volatile unsigned char* __attribute__((noinline)) AllocNoIdle(size_t bytes) {
50   // This volatile is needed to prevent the compiler from trying to be
51   // helpful and compiling a "useless" malloc + free into a noop.
52   volatile unsigned char* x = static_cast<unsigned char*>(malloc(bytes));
53   if (x) {
54     x[0] = 'x';
55   }
56   return x;
57 }
58 
59 class MemoryToucher {
60  public:
61   virtual void Touch(volatile unsigned char* nonidle) = 0;
62   virtual ~MemoryToucher() = default;
63 };
64 
65 class ReadDevZeroChunks : public MemoryToucher {
66  public:
ReadDevZeroChunks(size_t chunk_size)67   ReadDevZeroChunks(size_t chunk_size)
68       : chunk_size_(chunk_size), fd_(open("/dev/zero", O_RDONLY)) {
69     if (fd_ == -1) {
70       fprintf(stderr, "Failed to open: %s", strerror(errno));
71       abort();
72     }
73   }
74 
75   ~ReadDevZeroChunks() override = default;
76 
Touch(volatile unsigned char * nonidle)77   void Touch(volatile unsigned char* nonidle) override {
78     size_t total_rd = 0;
79     while (total_rd < kNoIdleSize) {
80       size_t chunk = chunk_size_;
81       if (chunk > kNoIdleSize - total_rd)
82         chunk = kNoIdleSize - total_rd;
83 
84       ssize_t rd =
85           read(fd_, const_cast<unsigned char*>(nonidle) + total_rd, chunk);
86       if (rd == -1) {
87         fprintf(stderr, "Failed to write: %s.", strerror(errno));
88         abort();
89       }
90       total_rd += static_cast<size_t>(rd);
91     }
92   }
93 
94  private:
95   size_t chunk_size_;
96   int fd_;
97 };
98 
99 class ReadDevZeroChunksAndSleep : public ReadDevZeroChunks {
100  public:
ReadDevZeroChunksAndSleep(size_t chunk_size)101   ReadDevZeroChunksAndSleep(size_t chunk_size)
102       : ReadDevZeroChunks(chunk_size) {}
Touch(volatile unsigned char * nonidle)103   void Touch(volatile unsigned char* nonidle) override {
104     ReadDevZeroChunks::Touch(nonidle);
105     sleep(1);
106   }
107 };
108 
109 class SumUp : public MemoryToucher {
110  public:
SumUp()111   SumUp()
112       : sum_(const_cast<volatile uint64_t*>(
113             static_cast<uint64_t*>(malloc(sizeof(uint64_t))))) {}
114   ~SumUp() override = default;
115 
Touch(volatile unsigned char * nonidle)116   void Touch(volatile unsigned char* nonidle) override {
117     for (size_t i = 0; i < kNoIdleSize; ++i)
118       *sum_ += nonidle[i];
119   }
120 
121  private:
122   volatile uint64_t* sum_;
123 };
124 
125 class ReadDevZeroChunksAndSum : public ReadDevZeroChunks {
126  public:
ReadDevZeroChunksAndSum(size_t chunk_size)127   ReadDevZeroChunksAndSum(size_t chunk_size) : ReadDevZeroChunks(chunk_size) {}
Touch(volatile unsigned char * nonidle)128   void Touch(volatile unsigned char* nonidle) override {
129     ReadDevZeroChunks::Touch(nonidle);
130     sum_up_.Touch(nonidle);
131   }
132 
133  private:
134   SumUp sum_up_;
135 };
136 
137 class AssignValues : public MemoryToucher {
138  public:
139   ~AssignValues() override = default;
140 
Touch(volatile unsigned char * nonidle)141   void Touch(volatile unsigned char* nonidle) override {
142     for (size_t i = 0; i < kNoIdleSize; ++i)
143       nonidle[i] = static_cast<unsigned char>(i % 256);
144   }
145 };
146 
147 }  // namespace
148 
main(int argc,char ** argv)149 int main(int argc, char** argv) {
150   volatile auto* idle = AllocIdle(kIdleSize);
151   volatile auto* nonidle = AllocNoIdle(kNoIdleSize);
152 
153   printf("Own PID: %" PRIdMAX "\n", static_cast<intmax_t>(getpid()));
154   printf("Idle: %p\n", static_cast<void*>(const_cast<unsigned char*>(idle)));
155   printf("Nonidle: %p\n",
156          static_cast<void*>(const_cast<unsigned char*>(nonidle)));
157 
158   for (size_t i = 0; i < kIdleSize; ++i)
159     idle[i] = static_cast<unsigned char>(i % 256);
160   for (size_t i = 0; i < kNoIdleSize; ++i)
161     nonidle[i] = static_cast<unsigned char>(i % 256);
162 
163   printf("Allocated everything.\n");
164 
165   struct sigaction action = {};
166   action.sa_handler = [](int) { interrupted = true; };
167   if (sigaction(SIGUSR1, &action, nullptr) != 0) {
168     fprintf(stderr, "Failed to register signal handler.\n");
169     abort();
170   }
171 
172   if (argc < 2) {
173     fprintf(stderr,
174             "Specifiy one of AssignValues / SumUp / ReadDevZeroChunks\n");
175     abort();
176   }
177 
178   std::unique_ptr<MemoryToucher> toucher;
179   if (strcmp(argv[1], "AssignValues") == 0) {
180     toucher.reset(new AssignValues());
181     printf("Using AssignValues.\n");
182   } else if (strcmp(argv[1], "SumUp") == 0) {
183     toucher.reset(new SumUp());
184     printf("Using SumUp.\n");
185   } else if (strcmp(argv[1], "ReadDevZeroChunks") == 0 ||
186              strcmp(argv[1], "ReadDevZeroChunksAndSleep") == 0 ||
187              strcmp(argv[1], "ReadDevZeroChunksAndSum") == 0) {
188     if (argc < 3) {
189       fprintf(stderr, "Specify chunk size.\n");
190       abort();
191     }
192     char* end;
193     long long chunk_arg = strtoll(argv[2], &end, 10);
194     if (*end != '\0' || *argv[2] == '\0') {
195       fprintf(stderr, "Invalid chunk size: %s\n", argv[2]);
196       abort();
197     }
198     if (strcmp(argv[1], "ReadDevZeroChunksAndSleep") == 0) {
199       printf("Using ReadDevZeroChunksAndSleep.\n");
200       toucher.reset(
201           new ReadDevZeroChunksAndSleep(static_cast<size_t>(chunk_arg)));
202     } else if (strcmp(argv[1], "ReadDevZeroChunksAndSum") == 0) {
203       printf("Using ReadDevZeroChunksAndSum.\n");
204       toucher.reset(
205           new ReadDevZeroChunksAndSum(static_cast<size_t>(chunk_arg)));
206     } else {
207       printf("Using ReadDevZeroChunks.\n");
208       toucher.reset(new ReadDevZeroChunks(static_cast<size_t>(chunk_arg)));
209     }
210   } else {
211     fprintf(stderr, "Invalid input.\n");
212     abort();
213   }
214 
215   while (true) {
216     bool report = interrupted;
217     if (report) {
218       printf("Waiting to finish touching everything.\n");
219       interrupted = false;
220       sleep(2);
221     }
222 
223     toucher->Touch(nonidle);
224 
225     if (report)
226       printf("Touched everything.\n");
227   }
228 }
229