• 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 <errno.h>
19 #include <stdint.h>
20 #include <sys/mman.h>
21 #include <sys/param.h>
22 #include <sys/types.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 
26 #include <string>
27 #include <unordered_map>
28 
29 #include <android-base/file.h>
30 #include <android-base/strings.h>
31 #include <ziparchive/zip_archive.h>
32 
33 #include <memory_trace/MemoryTrace.h>
34 
35 #include "File.h"
36 
ZipGetContents(const char * filename)37 std::string ZipGetContents(const char* filename) {
38   ZipArchiveHandle archive;
39   if (OpenArchive(filename, &archive) != 0) {
40     return "";
41   }
42 
43   // It is assumed that the archive contains only a single entry.
44   void* cookie;
45   std::string contents;
46   if (StartIteration(archive, &cookie) == 0) {
47     ZipEntry entry;
48     std::string name;
49     if (Next(cookie, &entry, &name) == 0) {
50       contents.resize(entry.uncompressed_length);
51       if (ExtractToMemory(archive, &entry, reinterpret_cast<uint8_t*>(contents.data()),
52                           entry.uncompressed_length) != 0) {
53         contents = "";
54       }
55     }
56   }
57 
58   CloseArchive(archive);
59   return contents;
60 }
61 
WaitPid(pid_t pid)62 static void WaitPid(pid_t pid) {
63   int wstatus;
64   pid_t wait_pid = TEMP_FAILURE_RETRY(waitpid(pid, &wstatus, 0));
65   if (wait_pid != pid) {
66     if (wait_pid == -1) {
67       err(1, "waitpid() failed");
68     } else {
69       errx(1, "Unexpected pid from waitpid(): expected %d, returned %d", pid, wait_pid);
70     }
71   }
72   if (!WIFEXITED(wstatus)) {
73     errx(1, "Forked process did not terminate with exit() call");
74   }
75   if (WEXITSTATUS(wstatus) != 0) {
76     errx(1, "Bad exit value from forked process: returned %d", WEXITSTATUS(wstatus));
77   }
78 }
79 
UpdatePresentBytes(std::unordered_map<uint64_t,memory_trace::Entry * > & entries_by_ptr,memory_trace::Entry * entry)80 static void UpdatePresentBytes(std::unordered_map<uint64_t, memory_trace::Entry*>& entries_by_ptr,
81                                memory_trace::Entry* entry) {
82   switch (entry->type) {
83     case memory_trace::FREE:
84       if (entry->present_bytes != -1) {
85         // Need to find the pointer for this free and update the present bytes
86         // on the original pointer.
87         auto iter = entries_by_ptr.find(entry->ptr);
88         if (iter != entries_by_ptr.end()) {
89           // Present bytes can be larger than the recorded size when the
90           // real size returned by malloc_usable_size is greater than that.
91           // Therefore, always choose the smaller of the two.
92           iter->second->present_bytes =
93               MIN(entry->present_bytes, static_cast<int64_t>(iter->second->size));
94           entries_by_ptr.erase(entry->ptr);
95         }
96       }
97       break;
98     case memory_trace::CALLOC:
99     case memory_trace::MALLOC:
100     case memory_trace::MEMALIGN:
101       entries_by_ptr[entry->ptr] = entry;
102       break;
103 
104     case memory_trace::REALLOC:
105       if (entry->ptr != 0) {
106         entries_by_ptr[entry->ptr] = entry;
107       }
108       if (entry->u.old_ptr != 0 && entry->present_bytes != -1) {
109         // The old pointer got freed, so add it that way.
110         auto iter = entries_by_ptr.find(entry->u.old_ptr);
111         if (iter != entries_by_ptr.end()) {
112           // Present bytes can be larger than the recorded size when the
113           // real size returned by malloc_usable_size is greater than that.
114           // Therefore, always choose the smaller of the two.
115           iter->second->present_bytes =
116               MIN(entry->present_bytes, static_cast<int64_t>(iter->second->size));
117           entries_by_ptr.erase(entry->u.old_ptr);
118           entry->present_bytes = -1;
119         }
120       }
121       break;
122     default:
123       break;
124   }
125 }
126 
127 // This function should not do any memory allocations in the main function.
128 // Any true allocation should happen in fork'd code.
GetUnwindInfo(const char * filename,memory_trace::Entry ** entries,size_t * num_entries)129 void GetUnwindInfo(const char* filename, memory_trace::Entry** entries, size_t* num_entries) {
130   void* mem =
131       mmap(nullptr, sizeof(size_t), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
132   if (mem == MAP_FAILED) {
133     err(1, "Unable to allocate a shared map of size %zu", sizeof(size_t));
134   }
135   *reinterpret_cast<size_t*>(mem) = 0;
136 
137   pid_t pid;
138   if ((pid = fork()) == 0) {
139     // First get the number of lines in the trace file. It is assumed
140     // that there are no blank lines, and every line contains a valid
141     // allocation operation.
142     std::string contents;
143     if (android::base::EndsWith(filename, ".zip")) {
144       contents = ZipGetContents(filename);
145     } else if (!android::base::ReadFileToString(filename, &contents)) {
146       errx(1, "Unable to get contents of %s", filename);
147     }
148     if (contents.empty()) {
149       errx(1, "Unable to get contents of %s", filename);
150     }
151 
152     size_t lines = 0;
153     size_t index = 0;
154     while (true) {
155       index = contents.find('\n', index);
156       if (index == std::string::npos) {
157         break;
158       }
159       index++;
160       lines++;
161     }
162     if (contents[contents.size() - 1] != '\n') {
163       // Add one since the last line doesn't end in '\n'.
164       lines++;
165     }
166     *reinterpret_cast<size_t*>(mem) = lines;
167     _exit(0);
168   } else if (pid == -1) {
169     err(1, "fork() call failed");
170   }
171   WaitPid(pid);
172   *num_entries = *reinterpret_cast<size_t*>(mem);
173   munmap(mem, sizeof(size_t));
174 
175   mem = mmap(nullptr, *num_entries * sizeof(memory_trace::Entry), PROT_READ | PROT_WRITE,
176              MAP_ANONYMOUS | MAP_SHARED, -1, 0);
177   if (mem == MAP_FAILED) {
178     err(1, "Unable to allocate a shared map of size %zu",
179         *num_entries * sizeof(memory_trace::Entry));
180   }
181   *entries = reinterpret_cast<memory_trace::Entry*>(mem);
182 
183   if ((pid = fork()) == 0) {
184     std::string contents;
185     if (android::base::EndsWith(filename, ".zip")) {
186       contents = ZipGetContents(filename);
187     } else if (!android::base::ReadFileToString(filename, &contents)) {
188       errx(1, "Unable to get contents of %s", filename);
189     }
190     if (contents.empty()) {
191       errx(1, "Contents of zip file %s is empty.", filename);
192     }
193 
194     std::unordered_map<uint64_t, memory_trace::Entry*> entries_by_ptr;
195     size_t entry_idx = 0;
196     size_t start_str = 0;
197     size_t end_str = 0;
198     while (true) {
199       end_str = contents.find('\n', start_str);
200       if (end_str == std::string::npos) {
201         break;
202       }
203       if (entry_idx == *num_entries) {
204         errx(1, "Too many entries, stopped at entry %zu", entry_idx);
205       }
206       contents[end_str] = '\0';
207 
208       std::string error;
209       memory_trace::Entry* entry = &(*entries)[entry_idx++];
210       if (!memory_trace::FillInEntryFromString(&contents[start_str], *entry, error)) {
211         errx(1, "%s", error.c_str());
212       }
213       start_str = end_str + 1;
214 
215       // If this operation does a free, set the present_bytes on the original
216       // allocation.
217       UpdatePresentBytes(entries_by_ptr, entry);
218     }
219     if (entry_idx != *num_entries) {
220       errx(1, "Mismatched number of entries found: expected %zu, found %zu", *num_entries,
221            entry_idx);
222     }
223     _exit(0);
224   } else if (pid == -1) {
225     err(1, "fork() call failed");
226   }
227   WaitPid(pid);
228 }
229 
FreeEntries(memory_trace::Entry * entries,size_t num_entries)230 void FreeEntries(memory_trace::Entry* entries, size_t num_entries) {
231   munmap(entries, num_entries * sizeof(memory_trace::Entry));
232 }
233