• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <err.h>
18 #include <inttypes.h>
19 #include <malloc.h>
20 #include <sched.h>
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/mman.h>
26 #include <unistd.h>
27 
28 #include <algorithm>
29 #include <stack>
30 #include <string>
31 #include <unordered_map>
32 #include <vector>
33 
34 #include <android-base/file.h>
35 #include <android-base/strings.h>
36 #include <benchmark/benchmark.h>
37 
38 #include <memory_trace/MemoryTrace.h>
39 
40 #include "File.h"
41 #include "Utils.h"
42 
43 struct TraceDataType {
44   memory_trace::Entry* entries = nullptr;
45   size_t num_entries = 0;
46   void** ptrs = nullptr;
47   size_t num_ptrs = 0;
48 };
49 
GetIndex(std::stack<size_t> & free_indices,size_t * max_index)50 static size_t GetIndex(std::stack<size_t>& free_indices, size_t* max_index) {
51   if (free_indices.empty()) {
52     return (*max_index)++;
53   }
54   size_t index = free_indices.top();
55   free_indices.pop();
56   return index;
57 }
58 
FreePtrs(TraceDataType * trace_data)59 static void FreePtrs(TraceDataType* trace_data) {
60   for (size_t i = 0; i < trace_data->num_ptrs; i++) {
61     void* ptr = trace_data->ptrs[i];
62     if (ptr != nullptr) {
63       free(ptr);
64       trace_data->ptrs[i] = nullptr;
65     }
66   }
67 }
68 
FreeTraceData(TraceDataType * trace_data)69 static void FreeTraceData(TraceDataType* trace_data) {
70   if (trace_data->ptrs == nullptr) {
71     return;
72   }
73 
74   munmap(trace_data->ptrs, sizeof(void*) * trace_data->num_ptrs);
75   FreeEntries(trace_data->entries, trace_data->num_entries);
76 }
77 
GetTraceData(const std::string & filename,TraceDataType * trace_data)78 static void GetTraceData(const std::string& filename, TraceDataType* trace_data) {
79   // Only keep last trace encountered cached.
80   static std::string cached_filename;
81   static TraceDataType cached_trace_data;
82   if (cached_filename == filename) {
83     *trace_data = cached_trace_data;
84     return;
85   } else {
86     FreeTraceData(&cached_trace_data);
87   }
88 
89   cached_filename = filename;
90   GetUnwindInfo(filename.c_str(), &trace_data->entries, &trace_data->num_entries);
91 
92   // This loop will convert the ptr field into an index into the ptrs array.
93   // Creating this index allows the trace run to quickly store or retrieve the
94   // allocation.
95   // For free, the ptr field will be index + one, where a zero represents
96   // a free(nullptr) call.
97   // For realloc, the old_pointer field will be index + one, where a zero
98   // represents a realloc(nullptr, XX).
99   trace_data->num_ptrs = 0;
100   std::stack<size_t> free_indices;
101   std::unordered_map<uint64_t, size_t> ptr_to_index;
102   for (size_t i = 0; i < trace_data->num_entries; i++) {
103     memory_trace::Entry* entry = &trace_data->entries[i];
104     switch (entry->type) {
105       case memory_trace::MALLOC:
106       case memory_trace::CALLOC:
107       case memory_trace::MEMALIGN: {
108         size_t idx = GetIndex(free_indices, &trace_data->num_ptrs);
109         ptr_to_index[entry->ptr] = idx;
110         entry->ptr = idx;
111         break;
112       }
113       case memory_trace::REALLOC: {
114         if (entry->u.old_ptr != 0) {
115           auto idx_entry = ptr_to_index.find(entry->u.old_ptr);
116           if (idx_entry == ptr_to_index.end()) {
117             errx(1, "File Error: Failed to find realloc pointer %" PRIx64, entry->u.old_ptr);
118           }
119           size_t old_pointer_idx = idx_entry->second;
120           free_indices.push(old_pointer_idx);
121           ptr_to_index.erase(idx_entry);
122           entry->u.old_ptr = old_pointer_idx + 1;
123         }
124         size_t idx = GetIndex(free_indices, &trace_data->num_ptrs);
125         ptr_to_index[entry->ptr] = idx;
126         entry->ptr = idx;
127         break;
128       }
129       case memory_trace::FREE:
130         if (entry->ptr != 0) {
131           auto idx_entry = ptr_to_index.find(entry->ptr);
132           if (idx_entry == ptr_to_index.end()) {
133             errx(1, "File Error: Unable to find free pointer %" PRIx64, entry->ptr);
134           }
135           free_indices.push(idx_entry->second);
136           entry->ptr = idx_entry->second + 1;
137           ptr_to_index.erase(idx_entry);
138         }
139         break;
140       case memory_trace::THREAD_DONE:
141       case memory_trace::UNKNOWN:
142         break;
143     }
144   }
145   void* map = mmap(nullptr, sizeof(void*) * trace_data->num_ptrs, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
146   if (map == MAP_FAILED) {
147     err(1, "mmap failed");
148   }
149   trace_data->ptrs = reinterpret_cast<void**>(map);
150 
151   cached_trace_data = *trace_data;
152 }
153 
RunTrace(benchmark::State & state,TraceDataType * trace_data)154 static void RunTrace(benchmark::State& state, TraceDataType* trace_data) {
155   int pagesize = getpagesize();
156   uint64_t total_ns = 0;
157   uint64_t start_ns;
158   void** ptrs = trace_data->ptrs;
159   for (size_t i = 0; i < trace_data->num_entries; i++) {
160     void* ptr;
161     const memory_trace::Entry& entry = trace_data->entries[i];
162     switch (entry.type) {
163       case memory_trace::MALLOC:
164         start_ns = Nanotime();
165         ptr = malloc(entry.size);
166         if (ptr == nullptr) {
167           errx(1, "malloc returned nullptr");
168         }
169         MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
170         total_ns += Nanotime() - start_ns;
171 
172         if (ptrs[entry.ptr] != nullptr) {
173           errx(1, "Internal Error: malloc pointer being replaced is not nullptr");
174         }
175         ptrs[entry.ptr] = ptr;
176         break;
177 
178       case memory_trace::CALLOC:
179         start_ns = Nanotime();
180         ptr = calloc(entry.u.n_elements, entry.size);
181         if (ptr == nullptr) {
182           errx(1, "calloc returned nullptr");
183         }
184         MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
185         total_ns += Nanotime() - start_ns;
186 
187         if (ptrs[entry.ptr] != nullptr) {
188           errx(1, "Internal Error: calloc pointer being replaced is not nullptr");
189         }
190         ptrs[entry.ptr] = ptr;
191         break;
192 
193       case memory_trace::MEMALIGN:
194         start_ns = Nanotime();
195         ptr = memalign(entry.u.align, entry.size);
196         if (ptr == nullptr) {
197           errx(1, "memalign returned nullptr");
198         }
199         MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
200         total_ns += Nanotime() - start_ns;
201 
202         if (ptrs[entry.ptr] != nullptr) {
203           errx(1, "Internal Error: memalign pointer being replaced is not nullptr");
204         }
205         ptrs[entry.ptr] = ptr;
206         break;
207 
208       case memory_trace::REALLOC:
209         start_ns = Nanotime();
210         if (entry.u.old_ptr == 0) {
211           ptr = realloc(nullptr, entry.size);
212         } else {
213           ptr = realloc(ptrs[entry.u.old_ptr - 1], entry.size);
214           ptrs[entry.u.old_ptr - 1] = nullptr;
215         }
216         if (entry.size > 0) {
217           if (ptr == nullptr) {
218             errx(1, "realloc returned nullptr");
219           }
220           MakeAllocationResident(ptr, entry.size, entry.present_bytes, pagesize);
221         }
222         total_ns += Nanotime() - start_ns;
223 
224         if (ptrs[entry.ptr] != nullptr) {
225           errx(1, "Internal Error: realloc pointer being replaced is not nullptr");
226         }
227         ptrs[entry.ptr] = ptr;
228         break;
229 
230       case memory_trace::FREE:
231         if (entry.ptr != 0) {
232           ptr = ptrs[entry.ptr - 1];
233           ptrs[entry.ptr - 1] = nullptr;
234         } else {
235           ptr = nullptr;
236         }
237         start_ns = Nanotime();
238         free(ptr);
239         total_ns += Nanotime() - start_ns;
240         break;
241 
242       case memory_trace::THREAD_DONE:
243       case memory_trace::UNKNOWN:
244         break;
245     }
246   }
247   state.SetIterationTime(total_ns / double(1000000000.0));
248 
249   FreePtrs(trace_data);
250 }
251 
252 // Run a trace as if all of the allocations occurred in a single thread.
253 // This is not completely realistic, but it is a possible worst case that
254 // could happen in an app.
BenchmarkTrace(benchmark::State & state,const char * filename,bool enable_decay_time)255 static void BenchmarkTrace(benchmark::State& state, const char* filename,
256                            [[maybe_unused]] bool enable_decay_time) {
257 #if defined(__BIONIC__)
258   if (enable_decay_time) {
259     mallopt(M_DECAY_TIME, 1);
260   } else {
261     mallopt(M_DECAY_TIME, 0);
262   }
263 #endif
264   std::string full_filename(android::base::GetExecutableDirectory() + "/traces/" + filename);
265 
266   TraceDataType trace_data;
267   GetTraceData(full_filename, &trace_data);
268 
269   for (auto _ : state) {
270     RunTrace(state, &trace_data);
271   }
272 
273   // Don't free the trace_data, it is cached. The last set of trace data
274   // will be leaked away.
275 }
276 
277 #define BENCH_OPTIONS                 \
278   UseManualTime()                     \
279       ->Unit(benchmark::kMicrosecond) \
280       ->MinTime(15.0)                 \
281       ->Repetitions(4)                \
282       ->ReportAggregatesOnly(true)
283 
BM_angry_birds2_default(benchmark::State & state)284 static void BM_angry_birds2_default(benchmark::State& state) {
285   BenchmarkTrace(state, "angry_birds2.zip", true);
286 }
287 BENCHMARK(BM_angry_birds2_default)->BENCH_OPTIONS;
288 
289 #if defined(__BIONIC__)
BM_angry_birds2_no_decay(benchmark::State & state)290 static void BM_angry_birds2_no_decay(benchmark::State& state) {
291   BenchmarkTrace(state, "angry_birds2.zip", false);
292 }
293 BENCHMARK(BM_angry_birds2_no_decay)->BENCH_OPTIONS;
294 #endif
295 
BM_camera_default(benchmark::State & state)296 static void BM_camera_default(benchmark::State& state) {
297   BenchmarkTrace(state, "camera.zip", true);
298 }
299 BENCHMARK(BM_camera_default)->BENCH_OPTIONS;
300 
301 #if defined(__BIONIC__)
BM_camera_no_decay(benchmark::State & state)302 static void BM_camera_no_decay(benchmark::State& state) {
303   BenchmarkTrace(state, "camera.zip", false);
304 }
305 BENCHMARK(BM_camera_no_decay)->BENCH_OPTIONS;
306 #endif
307 
BM_candy_crush_saga_default(benchmark::State & state)308 static void BM_candy_crush_saga_default(benchmark::State& state) {
309   BenchmarkTrace(state, "candy_crush_saga.zip", true);
310 }
311 BENCHMARK(BM_candy_crush_saga_default)->BENCH_OPTIONS;
312 
313 #if defined(__BIONIC__)
BM_candy_crush_saga_no_decay(benchmark::State & state)314 static void BM_candy_crush_saga_no_decay(benchmark::State& state) {
315   BenchmarkTrace(state, "candy_crush_saga.zip", false);
316 }
317 BENCHMARK(BM_candy_crush_saga_no_decay)->BENCH_OPTIONS;
318 #endif
319 
BM_gmail_default(benchmark::State & state)320 void BM_gmail_default(benchmark::State& state) {
321   BenchmarkTrace(state, "gmail.zip", true);
322 }
323 BENCHMARK(BM_gmail_default)->BENCH_OPTIONS;
324 
325 #if defined(__BIONIC__)
BM_gmail_no_decay(benchmark::State & state)326 void BM_gmail_no_decay(benchmark::State& state) {
327   BenchmarkTrace(state, "gmail.zip", false);
328 }
329 BENCHMARK(BM_gmail_no_decay)->BENCH_OPTIONS;
330 #endif
331 
BM_maps_default(benchmark::State & state)332 void BM_maps_default(benchmark::State& state) {
333   BenchmarkTrace(state, "maps.zip", true);
334 }
335 BENCHMARK(BM_maps_default)->BENCH_OPTIONS;
336 
337 #if defined(__BIONIC__)
BM_maps_no_decay(benchmark::State & state)338 void BM_maps_no_decay(benchmark::State& state) {
339   BenchmarkTrace(state, "maps.zip", false);
340 }
341 BENCHMARK(BM_maps_no_decay)->BENCH_OPTIONS;
342 #endif
343 
BM_photos_default(benchmark::State & state)344 void BM_photos_default(benchmark::State& state) {
345   BenchmarkTrace(state, "photos.zip", true);
346 }
347 BENCHMARK(BM_photos_default)->BENCH_OPTIONS;
348 
349 #if defined(__BIONIC__)
BM_photos_no_decay(benchmark::State & state)350 void BM_photos_no_decay(benchmark::State& state) {
351   BenchmarkTrace(state, "photos.zip", false);
352 }
353 BENCHMARK(BM_photos_no_decay)->BENCH_OPTIONS;
354 #endif
355 
BM_pubg_default(benchmark::State & state)356 void BM_pubg_default(benchmark::State& state) {
357   BenchmarkTrace(state, "pubg.zip", true);
358 }
359 BENCHMARK(BM_pubg_default)->BENCH_OPTIONS;
360 
361 #if defined(__BIONIC__)
BM_pubg_no_decay(benchmark::State & state)362 void BM_pubg_no_decay(benchmark::State& state) {
363   BenchmarkTrace(state, "pubg.zip", false);
364 }
365 BENCHMARK(BM_pubg_no_decay)->BENCH_OPTIONS;
366 #endif
367 
BM_surfaceflinger_default(benchmark::State & state)368 void BM_surfaceflinger_default(benchmark::State& state) {
369   BenchmarkTrace(state, "surfaceflinger.zip", true);
370 }
371 BENCHMARK(BM_surfaceflinger_default)->BENCH_OPTIONS;
372 
373 #if defined(__BIONIC__)
BM_surfaceflinger_no_decay(benchmark::State & state)374 void BM_surfaceflinger_no_decay(benchmark::State& state) {
375   BenchmarkTrace(state, "surfaceflinger.zip", false);
376 }
377 BENCHMARK(BM_surfaceflinger_no_decay)->BENCH_OPTIONS;
378 #endif
379 
BM_system_server_default(benchmark::State & state)380 void BM_system_server_default(benchmark::State& state) {
381   BenchmarkTrace(state, "system_server.zip", true);
382 }
383 BENCHMARK(BM_system_server_default)->BENCH_OPTIONS;
384 
385 #if defined(__BIONIC__)
BM_system_server_no_decay(benchmark::State & state)386 void BM_system_server_no_decay(benchmark::State& state) {
387   BenchmarkTrace(state, "system_server.zip", false);
388 }
389 BENCHMARK(BM_system_server_no_decay)->BENCH_OPTIONS;
390 #endif
391 
BM_systemui_default(benchmark::State & state)392 void BM_systemui_default(benchmark::State& state) {
393   BenchmarkTrace(state, "systemui.zip", true);
394 }
395 BENCHMARK(BM_systemui_default)->BENCH_OPTIONS;
396 
397 #if defined(__BIONIC__)
BM_systemui_no_decay(benchmark::State & state)398 void BM_systemui_no_decay(benchmark::State& state) {
399   BenchmarkTrace(state, "systemui.zip", false);
400 }
401 BENCHMARK(BM_systemui_no_decay)->BENCH_OPTIONS;
402 #endif
403 
BM_youtube_default(benchmark::State & state)404 void BM_youtube_default(benchmark::State& state) {
405   BenchmarkTrace(state, "youtube.zip", true);
406 }
407 BENCHMARK(BM_youtube_default)->BENCH_OPTIONS;
408 
409 #if defined(__BIONIC__)
BM_youtube_no_decay(benchmark::State & state)410 void BM_youtube_no_decay(benchmark::State& state) {
411   BenchmarkTrace(state, "youtube.zip", false);
412 }
413 BENCHMARK(BM_youtube_no_decay)->BENCH_OPTIONS;
414 #endif
415 
main(int argc,char ** argv)416 int main(int argc, char** argv) {
417   std::vector<char*> args;
418   args.push_back(argv[0]);
419 
420   // Look for the --cpu=XX option.
421   for (int i = 1; i < argc; i++) {
422     if (strncmp(argv[i], "--cpu=", 6) == 0) {
423       char* endptr;
424       int cpu = strtol(&argv[i][6], &endptr, 10);
425       if (argv[i][0] == '\0' || endptr == nullptr || *endptr != '\0') {
426         printf("Invalid format of --cpu option, '%s' must be an integer value.\n", argv[i] + 6);
427         return 1;
428       }
429       cpu_set_t cpuset;
430       CPU_ZERO(&cpuset);
431       CPU_SET(cpu, &cpuset);
432       if (sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {
433         if (errno == EINVAL) {
434           printf("Invalid cpu %d\n", cpu);
435           return 1;
436         }
437         perror("sched_setaffinity failed");
438         return 1;
439       }
440       printf("Locking to cpu %d\n", cpu);
441     } else {
442       args.push_back(argv[i]);
443     }
444   }
445 
446   argc = args.size();
447   ::benchmark::Initialize(&argc, args.data());
448   if (::benchmark::ReportUnrecognizedArguments(argc, args.data())) return 1;
449   ::benchmark::RunSpecifiedBenchmarks();
450 }
451