xref: /aosp_15_r20/system/extras/memory_replay/main.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker  * Copyright (C) 2015 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker  *
4*288bf522SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker  *
8*288bf522SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker  *
10*288bf522SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker  * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker  */
16*288bf522SAndroid Build Coastguard Worker 
17*288bf522SAndroid Build Coastguard Worker #include <err.h>
18*288bf522SAndroid Build Coastguard Worker #include <errno.h>
19*288bf522SAndroid Build Coastguard Worker #include <fcntl.h>
20*288bf522SAndroid Build Coastguard Worker #include <inttypes.h>
21*288bf522SAndroid Build Coastguard Worker #include <malloc.h>
22*288bf522SAndroid Build Coastguard Worker #include <stdint.h>
23*288bf522SAndroid Build Coastguard Worker #include <stdio.h>
24*288bf522SAndroid Build Coastguard Worker #include <stdlib.h>
25*288bf522SAndroid Build Coastguard Worker #include <string.h>
26*288bf522SAndroid Build Coastguard Worker #include <sys/stat.h>
27*288bf522SAndroid Build Coastguard Worker #include <sys/types.h>
28*288bf522SAndroid Build Coastguard Worker #include <unistd.h>
29*288bf522SAndroid Build Coastguard Worker 
30*288bf522SAndroid Build Coastguard Worker #include <memory_trace/MemoryTrace.h>
31*288bf522SAndroid Build Coastguard Worker 
32*288bf522SAndroid Build Coastguard Worker #include "Alloc.h"
33*288bf522SAndroid Build Coastguard Worker #include "File.h"
34*288bf522SAndroid Build Coastguard Worker #include "NativeInfo.h"
35*288bf522SAndroid Build Coastguard Worker #include "Pointers.h"
36*288bf522SAndroid Build Coastguard Worker #include "Thread.h"
37*288bf522SAndroid Build Coastguard Worker #include "Threads.h"
38*288bf522SAndroid Build Coastguard Worker 
39*288bf522SAndroid Build Coastguard Worker #include <log/log.h>
40*288bf522SAndroid Build Coastguard Worker #include <log/log_read.h>
41*288bf522SAndroid Build Coastguard Worker 
42*288bf522SAndroid Build Coastguard Worker constexpr size_t kDefaultMaxThreads = 512;
43*288bf522SAndroid Build Coastguard Worker 
GetMaxAllocs(const memory_trace::Entry * entries,size_t num_entries)44*288bf522SAndroid Build Coastguard Worker static size_t GetMaxAllocs(const memory_trace::Entry* entries, size_t num_entries) {
45*288bf522SAndroid Build Coastguard Worker   size_t max_allocs = 0;
46*288bf522SAndroid Build Coastguard Worker   size_t num_allocs = 0;
47*288bf522SAndroid Build Coastguard Worker   for (size_t i = 0; i < num_entries; i++) {
48*288bf522SAndroid Build Coastguard Worker     switch (entries[i].type) {
49*288bf522SAndroid Build Coastguard Worker       case memory_trace::THREAD_DONE:
50*288bf522SAndroid Build Coastguard Worker         break;
51*288bf522SAndroid Build Coastguard Worker       case memory_trace::MALLOC:
52*288bf522SAndroid Build Coastguard Worker       case memory_trace::CALLOC:
53*288bf522SAndroid Build Coastguard Worker       case memory_trace::MEMALIGN:
54*288bf522SAndroid Build Coastguard Worker         if (entries[i].ptr != 0) {
55*288bf522SAndroid Build Coastguard Worker           num_allocs++;
56*288bf522SAndroid Build Coastguard Worker         }
57*288bf522SAndroid Build Coastguard Worker         break;
58*288bf522SAndroid Build Coastguard Worker       case memory_trace::REALLOC:
59*288bf522SAndroid Build Coastguard Worker         if (entries[i].ptr == 0 && entries[i].u.old_ptr != 0) {
60*288bf522SAndroid Build Coastguard Worker           num_allocs--;
61*288bf522SAndroid Build Coastguard Worker         } else if (entries[i].ptr != 0 && entries[i].u.old_ptr == 0) {
62*288bf522SAndroid Build Coastguard Worker           num_allocs++;
63*288bf522SAndroid Build Coastguard Worker         }
64*288bf522SAndroid Build Coastguard Worker         break;
65*288bf522SAndroid Build Coastguard Worker       case memory_trace::FREE:
66*288bf522SAndroid Build Coastguard Worker         if (entries[i].ptr != 0) {
67*288bf522SAndroid Build Coastguard Worker           num_allocs--;
68*288bf522SAndroid Build Coastguard Worker         }
69*288bf522SAndroid Build Coastguard Worker         break;
70*288bf522SAndroid Build Coastguard Worker     }
71*288bf522SAndroid Build Coastguard Worker     if (num_allocs > max_allocs) {
72*288bf522SAndroid Build Coastguard Worker       max_allocs = num_allocs;
73*288bf522SAndroid Build Coastguard Worker     }
74*288bf522SAndroid Build Coastguard Worker   }
75*288bf522SAndroid Build Coastguard Worker   return max_allocs;
76*288bf522SAndroid Build Coastguard Worker }
77*288bf522SAndroid Build Coastguard Worker 
PrintLogStats(const char * log_name)78*288bf522SAndroid Build Coastguard Worker static void PrintLogStats(const char* log_name) {
79*288bf522SAndroid Build Coastguard Worker   logger_list* list =
80*288bf522SAndroid Build Coastguard Worker       android_logger_list_open(android_name_to_log_id(log_name), ANDROID_LOG_NONBLOCK, 0, getpid());
81*288bf522SAndroid Build Coastguard Worker   if (list == nullptr) {
82*288bf522SAndroid Build Coastguard Worker     printf("Failed to open log for %s\n", log_name);
83*288bf522SAndroid Build Coastguard Worker     return;
84*288bf522SAndroid Build Coastguard Worker   }
85*288bf522SAndroid Build Coastguard Worker   while (true) {
86*288bf522SAndroid Build Coastguard Worker     log_msg entry;
87*288bf522SAndroid Build Coastguard Worker     ssize_t retval = android_logger_list_read(list, &entry);
88*288bf522SAndroid Build Coastguard Worker     if (retval == 0) {
89*288bf522SAndroid Build Coastguard Worker       break;
90*288bf522SAndroid Build Coastguard Worker     }
91*288bf522SAndroid Build Coastguard Worker     if (retval < 0) {
92*288bf522SAndroid Build Coastguard Worker       if (retval == -EINTR) {
93*288bf522SAndroid Build Coastguard Worker         continue;
94*288bf522SAndroid Build Coastguard Worker       }
95*288bf522SAndroid Build Coastguard Worker       // EAGAIN means there is nothing left to read when ANDROID_LOG_NONBLOCK is set.
96*288bf522SAndroid Build Coastguard Worker       if (retval != -EAGAIN) {
97*288bf522SAndroid Build Coastguard Worker         printf("Failed to read log entry: %s\n", strerrordesc_np(retval));
98*288bf522SAndroid Build Coastguard Worker       }
99*288bf522SAndroid Build Coastguard Worker       break;
100*288bf522SAndroid Build Coastguard Worker     }
101*288bf522SAndroid Build Coastguard Worker     if (entry.msg() == nullptr) {
102*288bf522SAndroid Build Coastguard Worker       continue;
103*288bf522SAndroid Build Coastguard Worker     }
104*288bf522SAndroid Build Coastguard Worker     // Only print allocator tagged log entries.
105*288bf522SAndroid Build Coastguard Worker     std::string_view tag(entry.msg() + 1);
106*288bf522SAndroid Build Coastguard Worker     if (tag != "scudo" && tag != "jemalloc") {
107*288bf522SAndroid Build Coastguard Worker       continue;
108*288bf522SAndroid Build Coastguard Worker     }
109*288bf522SAndroid Build Coastguard Worker     printf("%s\n", &tag.back() + 2);
110*288bf522SAndroid Build Coastguard Worker   }
111*288bf522SAndroid Build Coastguard Worker   android_logger_list_close(list);
112*288bf522SAndroid Build Coastguard Worker }
113*288bf522SAndroid Build Coastguard Worker 
ProcessDump(const memory_trace::Entry * entries,size_t num_entries,size_t max_threads)114*288bf522SAndroid Build Coastguard Worker static void ProcessDump(const memory_trace::Entry* entries, size_t num_entries,
115*288bf522SAndroid Build Coastguard Worker                         size_t max_threads) {
116*288bf522SAndroid Build Coastguard Worker   // Do a pass to get the maximum number of allocations used at one
117*288bf522SAndroid Build Coastguard Worker   // time to allow a single mmap that can hold the maximum number of
118*288bf522SAndroid Build Coastguard Worker   // pointers needed at once.
119*288bf522SAndroid Build Coastguard Worker   size_t max_allocs = GetMaxAllocs(entries, num_entries);
120*288bf522SAndroid Build Coastguard Worker   Pointers pointers(max_allocs);
121*288bf522SAndroid Build Coastguard Worker   Threads threads(&pointers, max_threads);
122*288bf522SAndroid Build Coastguard Worker 
123*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "Maximum threads available:   %zu\n", threads.max_threads());
124*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "Maximum allocations in dump: %zu\n", max_allocs);
125*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "Total pointers available:    %zu\n\n", pointers.max_pointers());
126*288bf522SAndroid Build Coastguard Worker 
127*288bf522SAndroid Build Coastguard Worker   NativePrintInfo("Initial ");
128*288bf522SAndroid Build Coastguard Worker 
129*288bf522SAndroid Build Coastguard Worker   for (size_t i = 0; i < num_entries; i++) {
130*288bf522SAndroid Build Coastguard Worker     if (((i + 1) % 100000) == 0) {
131*288bf522SAndroid Build Coastguard Worker       dprintf(STDOUT_FILENO, "  At line %zu:\n", i + 1);
132*288bf522SAndroid Build Coastguard Worker       NativePrintInfo("    ");
133*288bf522SAndroid Build Coastguard Worker     }
134*288bf522SAndroid Build Coastguard Worker     const memory_trace::Entry& entry = entries[i];
135*288bf522SAndroid Build Coastguard Worker     Thread* thread = threads.FindThread(entry.tid);
136*288bf522SAndroid Build Coastguard Worker     if (thread == nullptr) {
137*288bf522SAndroid Build Coastguard Worker       thread = threads.CreateThread(entry.tid);
138*288bf522SAndroid Build Coastguard Worker     }
139*288bf522SAndroid Build Coastguard Worker 
140*288bf522SAndroid Build Coastguard Worker     // Wait for the thread to complete any previous actions before handling
141*288bf522SAndroid Build Coastguard Worker     // the next action.
142*288bf522SAndroid Build Coastguard Worker     thread->WaitForReady();
143*288bf522SAndroid Build Coastguard Worker 
144*288bf522SAndroid Build Coastguard Worker     thread->SetEntry(&entry);
145*288bf522SAndroid Build Coastguard Worker 
146*288bf522SAndroid Build Coastguard Worker     bool does_free = AllocDoesFree(entry);
147*288bf522SAndroid Build Coastguard Worker     if (does_free) {
148*288bf522SAndroid Build Coastguard Worker       // Make sure that any other threads doing allocations are complete
149*288bf522SAndroid Build Coastguard Worker       // before triggering the action. Otherwise, another thread could
150*288bf522SAndroid Build Coastguard Worker       // be creating the allocation we are going to free.
151*288bf522SAndroid Build Coastguard Worker       threads.WaitForAllToQuiesce();
152*288bf522SAndroid Build Coastguard Worker     }
153*288bf522SAndroid Build Coastguard Worker 
154*288bf522SAndroid Build Coastguard Worker     // Tell the thread to execute the action.
155*288bf522SAndroid Build Coastguard Worker     thread->SetPending();
156*288bf522SAndroid Build Coastguard Worker 
157*288bf522SAndroid Build Coastguard Worker     if (entries[i].type == memory_trace::THREAD_DONE) {
158*288bf522SAndroid Build Coastguard Worker       // Wait for the thread to finish and clear the thread entry.
159*288bf522SAndroid Build Coastguard Worker       threads.Finish(thread);
160*288bf522SAndroid Build Coastguard Worker     }
161*288bf522SAndroid Build Coastguard Worker 
162*288bf522SAndroid Build Coastguard Worker     // Wait for this action to complete. This avoids a race where
163*288bf522SAndroid Build Coastguard Worker     // another thread could be creating the same allocation where are
164*288bf522SAndroid Build Coastguard Worker     // trying to free.
165*288bf522SAndroid Build Coastguard Worker     if (does_free) {
166*288bf522SAndroid Build Coastguard Worker       thread->WaitForReady();
167*288bf522SAndroid Build Coastguard Worker     }
168*288bf522SAndroid Build Coastguard Worker   }
169*288bf522SAndroid Build Coastguard Worker   // Wait for all threads to stop processing actions.
170*288bf522SAndroid Build Coastguard Worker   threads.WaitForAllToQuiesce();
171*288bf522SAndroid Build Coastguard Worker 
172*288bf522SAndroid Build Coastguard Worker   NativePrintInfo("Final ");
173*288bf522SAndroid Build Coastguard Worker 
174*288bf522SAndroid Build Coastguard Worker   // Free any outstanding pointers.
175*288bf522SAndroid Build Coastguard Worker   // This allows us to run a tool like valgrind to verify that no memory
176*288bf522SAndroid Build Coastguard Worker   // is leaked and everything is accounted for during a run.
177*288bf522SAndroid Build Coastguard Worker   threads.FinishAll();
178*288bf522SAndroid Build Coastguard Worker   pointers.FreeAll();
179*288bf522SAndroid Build Coastguard Worker 
180*288bf522SAndroid Build Coastguard Worker   // Print out the total time making all allocation calls.
181*288bf522SAndroid Build Coastguard Worker   char buffer[256];
182*288bf522SAndroid Build Coastguard Worker   uint64_t total_nsecs = threads.total_time_nsecs();
183*288bf522SAndroid Build Coastguard Worker   NativeFormatFloat(buffer, sizeof(buffer), total_nsecs, 1000000000);
184*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "Total Allocation/Free Time: %" PRIu64 "ns %ss\n", total_nsecs, buffer);
185*288bf522SAndroid Build Coastguard Worker 
186*288bf522SAndroid Build Coastguard Worker   // Send native allocator stats to the log
187*288bf522SAndroid Build Coastguard Worker   mallopt(M_LOG_STATS, 0);
188*288bf522SAndroid Build Coastguard Worker 
189*288bf522SAndroid Build Coastguard Worker   // No need to avoid allocations at this point since all stats have been sent to the log.
190*288bf522SAndroid Build Coastguard Worker   printf("Native Allocator Stats:\n");
191*288bf522SAndroid Build Coastguard Worker   PrintLogStats("system");
192*288bf522SAndroid Build Coastguard Worker   PrintLogStats("main");
193*288bf522SAndroid Build Coastguard Worker }
194*288bf522SAndroid Build Coastguard Worker 
main(int argc,char ** argv)195*288bf522SAndroid Build Coastguard Worker int main(int argc, char** argv) {
196*288bf522SAndroid Build Coastguard Worker   if (argc != 2 && argc != 3) {
197*288bf522SAndroid Build Coastguard Worker     if (argc > 3) {
198*288bf522SAndroid Build Coastguard Worker       fprintf(stderr, "Only two arguments are expected.\n");
199*288bf522SAndroid Build Coastguard Worker     } else {
200*288bf522SAndroid Build Coastguard Worker       fprintf(stderr, "Requires at least one argument.\n");
201*288bf522SAndroid Build Coastguard Worker     }
202*288bf522SAndroid Build Coastguard Worker     fprintf(stderr, "Usage: %s MEMORY_LOG_FILE [MAX_THREADS]\n", basename(argv[0]));
203*288bf522SAndroid Build Coastguard Worker     fprintf(stderr, "  MEMORY_LOG_FILE\n");
204*288bf522SAndroid Build Coastguard Worker     fprintf(stderr, "    This can either be a text file or a zipped text file.\n");
205*288bf522SAndroid Build Coastguard Worker     fprintf(stderr, "  MAX_THREADs\n");
206*288bf522SAndroid Build Coastguard Worker     fprintf(stderr, "    The maximum number of threads in the trace. The default is %zu.\n",
207*288bf522SAndroid Build Coastguard Worker             kDefaultMaxThreads);
208*288bf522SAndroid Build Coastguard Worker     fprintf(stderr, "    This pre-allocates the memory for thread data to avoid allocating\n");
209*288bf522SAndroid Build Coastguard Worker     fprintf(stderr, "    while the trace is being replayed.\n");
210*288bf522SAndroid Build Coastguard Worker     return 1;
211*288bf522SAndroid Build Coastguard Worker   }
212*288bf522SAndroid Build Coastguard Worker 
213*288bf522SAndroid Build Coastguard Worker #if defined(__LP64__)
214*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "64 bit environment.\n");
215*288bf522SAndroid Build Coastguard Worker #else
216*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "32 bit environment.\n");
217*288bf522SAndroid Build Coastguard Worker #endif
218*288bf522SAndroid Build Coastguard Worker 
219*288bf522SAndroid Build Coastguard Worker #if defined(__BIONIC__)
220*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "Setting decay time to 1\n");
221*288bf522SAndroid Build Coastguard Worker   mallopt(M_DECAY_TIME, 1);
222*288bf522SAndroid Build Coastguard Worker #endif
223*288bf522SAndroid Build Coastguard Worker 
224*288bf522SAndroid Build Coastguard Worker   size_t max_threads = kDefaultMaxThreads;
225*288bf522SAndroid Build Coastguard Worker   if (argc == 3) {
226*288bf522SAndroid Build Coastguard Worker     max_threads = atoi(argv[2]);
227*288bf522SAndroid Build Coastguard Worker   }
228*288bf522SAndroid Build Coastguard Worker 
229*288bf522SAndroid Build Coastguard Worker   memory_trace::Entry* entries;
230*288bf522SAndroid Build Coastguard Worker   size_t num_entries;
231*288bf522SAndroid Build Coastguard Worker   GetUnwindInfo(argv[1], &entries, &num_entries);
232*288bf522SAndroid Build Coastguard Worker 
233*288bf522SAndroid Build Coastguard Worker   dprintf(STDOUT_FILENO, "Processing: %s\n", argv[1]);
234*288bf522SAndroid Build Coastguard Worker 
235*288bf522SAndroid Build Coastguard Worker   ProcessDump(entries, num_entries, max_threads);
236*288bf522SAndroid Build Coastguard Worker 
237*288bf522SAndroid Build Coastguard Worker   FreeEntries(entries, num_entries);
238*288bf522SAndroid Build Coastguard Worker 
239*288bf522SAndroid Build Coastguard Worker   return 0;
240*288bf522SAndroid Build Coastguard Worker }
241