1 /*
2  * Copyright (C) 2015 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 <errno.h>
19 #include <fcntl.h>
20 #include <inttypes.h>
21 #include <malloc.h>
22 #include <stdint.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 #include "Alloc.h"
31 #include "File.h"
32 #include "NativeInfo.h"
33 #include "Pointers.h"
34 #include "Thread.h"
35 #include "Threads.h"
36 
37 constexpr size_t kDefaultMaxThreads = 512;
38 
39 static size_t GetMaxAllocs(const AllocEntry* entries, size_t num_entries) {
40   size_t max_allocs = 0;
41   size_t num_allocs = 0;
42   for (size_t i = 0; i < num_entries; i++) {
43     switch (entries[i].type) {
44       case THREAD_DONE:
45         break;
46       case MALLOC:
47       case CALLOC:
48       case MEMALIGN:
49         if (entries[i].ptr != 0) {
50           num_allocs++;
51         }
52         break;
53       case REALLOC:
54         if (entries[i].ptr == 0 && entries[i].u.old_ptr != 0) {
55           num_allocs--;
56         } else if (entries[i].ptr != 0 && entries[i].u.old_ptr == 0) {
57           num_allocs++;
58         }
59         break;
60       case FREE:
61         if (entries[i].ptr != 0) {
62           num_allocs--;
63         }
64         break;
65     }
66     if (num_allocs > max_allocs) {
67       max_allocs = num_allocs;
68     }
69   }
70   return max_allocs;
71 }
72 
73 static void ProcessDump(const AllocEntry* entries, size_t num_entries, size_t max_threads) {
74   // Do a pass to get the maximum number of allocations used at one
75   // time to allow a single mmap that can hold the maximum number of
76   // pointers needed at once.
77   size_t max_allocs = GetMaxAllocs(entries, num_entries);
78   Pointers pointers(max_allocs);
79   Threads threads(&pointers, max_threads);
80 
81   NativePrintf("Maximum threads available:   %zu\n", threads.max_threads());
82   NativePrintf("Maximum allocations in dump: %zu\n", max_allocs);
83   NativePrintf("Total pointers available:    %zu\n\n", pointers.max_pointers());
84 
85   NativePrintInfo("Initial ");
86 
87   for (size_t i = 0; i < num_entries; i++) {
88     if (((i + 1) % 100000) == 0) {
89       NativePrintf("  At line %zu:\n", i + 1);
90       NativePrintInfo("    ");
91     }
92     const AllocEntry& entry = entries[i];
93     Thread* thread = threads.FindThread(entry.tid);
94     if (thread == nullptr) {
95       thread = threads.CreateThread(entry.tid);
96     }
97 
98     // Wait for the thread to complete any previous actions before handling
99     // the next action.
100     thread->WaitForReady();
101 
102     thread->SetAllocEntry(&entry);
103 
104     bool does_free = AllocDoesFree(entry);
105     if (does_free) {
106       // Make sure that any other threads doing allocations are complete
107       // before triggering the action. Otherwise, another thread could
108       // be creating the allocation we are going to free.
109       threads.WaitForAllToQuiesce();
110     }
111 
112     // Tell the thread to execute the action.
113     thread->SetPending();
114 
115     if (entries[i].type == THREAD_DONE) {
116       // Wait for the thread to finish and clear the thread entry.
117       threads.Finish(thread);
118     }
119 
120     // Wait for this action to complete. This avoids a race where
121     // another thread could be creating the same allocation where are
122     // trying to free.
123     if (does_free) {
124       thread->WaitForReady();
125     }
126   }
127   // Wait for all threads to stop processing actions.
128   threads.WaitForAllToQuiesce();
129 
130   NativePrintInfo("Final ");
131 
132   // Free any outstanding pointers.
133   // This allows us to run a tool like valgrind to verify that no memory
134   // is leaked and everything is accounted for during a run.
135   threads.FinishAll();
136   pointers.FreeAll();
137 
138   // Print out the total time making all allocation calls.
139   char buffer[256];
140   uint64_t total_nsecs = threads.total_time_nsecs();
141   NativeFormatFloat(buffer, sizeof(buffer), total_nsecs, 1000000000);
142   NativePrintf("Total Allocation/Free Time: %" PRIu64 "ns %ss\n", total_nsecs, buffer);
143 }
144 
145 int main(int argc, char** argv) {
146   if (argc != 2 && argc != 3) {
147     if (argc > 3) {
148       fprintf(stderr, "Only two arguments are expected.\n");
149     } else {
150       fprintf(stderr, "Requires at least one argument.\n");
151     }
152     fprintf(stderr, "Usage: %s MEMORY_LOG_FILE [MAX_THREADS]\n", basename(argv[0]));
153     fprintf(stderr, "  MEMORY_LOG_FILE\n");
154     fprintf(stderr, "    This can either be a text file or a zipped text file.\n");
155     fprintf(stderr, "  MAX_THREADs\n");
156     fprintf(stderr, "    The maximum number of threads in the trace. The default is %zu.\n",
157             kDefaultMaxThreads);
158     fprintf(stderr, "    This pre-allocates the memory for thread data to avoid allocating\n");
159     fprintf(stderr, "    while the trace is being replayed.\n");
160     return 1;
161   }
162 
163 #if defined(__LP64__)
164   NativePrintf("64 bit environment.\n");
165 #else
166   NativePrintf("32 bit environment.\n");
167 #endif
168 
169 #if defined(__BIONIC__)
170   NativePrintf("Setting decay time to 1\n");
171   mallopt(M_DECAY_TIME, 1);
172 #endif
173 
174   size_t max_threads = kDefaultMaxThreads;
175   if (argc == 3) {
176     max_threads = atoi(argv[2]);
177   }
178 
179   AllocEntry* entries;
180   size_t num_entries;
181   GetUnwindInfo(argv[1], &entries, &num_entries);
182 
183   NativePrintf("Processing: %s\n", argv[1]);
184 
185   ProcessDump(entries, num_entries, max_threads);
186 
187   FreeEntries(entries, num_entries);
188 
189   return 0;
190 }
191