• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 <fcntl.h>
18 #include <getopt.h>
19 #include <inttypes.h>
20 #include <stdio.h>
21 #include <unistd.h>
22 
23 #include <string>
24 #include <unordered_map>
25 #include <utility>
26 
27 #include <android-base/file.h>
28 
29 #include <memory_trace/MemoryTrace.h>
30 
31 #include "File.h"
32 
Usage()33 static void Usage() {
34   fprintf(stderr, "Usage: %s [--attempt_repair] TRACE_FILE1 TRACE_FILE2 ...\n",
35           android::base::Basename(android::base::GetExecutablePath()).c_str());
36   fprintf(stderr, "  --attempt_repair\n");
37   fprintf(stderr, "    If a trace file has some errors, try to fix them. The new\n");
38   fprintf(stderr, "    file will be named TRACE_FILE.repair\n");
39   fprintf(stderr, "  TRACE_FILE1 TRACE_FILE2 ...\n");
40   fprintf(stderr, "      The trace files to verify\n");
41   fprintf(stderr, "\n  Verify trace are valid.\n");
42   exit(1);
43 }
44 
WriteRepairEntries(const std::string & repair_file,memory_trace::Entry * entries,size_t num_entries)45 static bool WriteRepairEntries(const std::string& repair_file, memory_trace::Entry* entries,
46                                size_t num_entries) {
47   int fd = open(repair_file.c_str(), O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, 0644);
48   if (fd == -1) {
49     printf("  Failed to create repair file %s: %s\n", repair_file.c_str(), strerror(errno));
50     return false;
51   }
52   bool valid = true;
53   for (size_t i = 0; i < num_entries; i++) {
54     if (!memory_trace::WriteEntryToFd(fd, entries[i])) {
55       printf("  Failed to write entry to file:\n");
56       valid = false;
57       break;
58     }
59   }
60   close(fd);
61   if (!valid) {
62     unlink(repair_file.c_str());
63   }
64   return valid;
65 }
66 
VerifyTrace(const char * trace_file,bool attempt_repair)67 static void VerifyTrace(const char* trace_file, bool attempt_repair) {
68   printf("Checking %s\n", trace_file);
69 
70   memory_trace::Entry* entries;
71   size_t num_entries;
72   GetUnwindInfo(trace_file, &entries, &num_entries);
73 
74   size_t errors_found = 0;
75   size_t errors_repaired = 0;
76   std::unordered_map<uint64_t, std::pair<memory_trace::Entry*, size_t>> live_ptrs;
77   std::pair<memory_trace::Entry*, size_t> erased(nullptr, 0);
78   for (size_t i = 0; i < num_entries; i++) {
79     memory_trace::Entry* entry = &entries[i];
80 
81     size_t size = 0;
82     uint64_t ptr = 0;
83     switch (entry->type) {
84       case memory_trace::MALLOC:
85       case memory_trace::MEMALIGN:
86         size = entry->size;
87         ptr = entry->ptr;
88         break;
89       case memory_trace::CALLOC:
90         size = entry->size * entry->u.n_elements;
91         ptr = entry->ptr;
92         break;
93         break;
94       case memory_trace::REALLOC:
95         size = entry->size;
96         if (entry->ptr != 0) {
97           ptr = entry->ptr;
98         }
99         if (entry->u.old_ptr != 0) {
100           // Verify old pointer
101           auto entry_iter = live_ptrs.find(entry->u.old_ptr);
102           if (entry_iter == live_ptrs.end()) {
103             // Verify the pointer didn't get realloc'd to itself.
104             if (entry->u.old_ptr != entry->ptr) {
105               printf("  Line %zu: freeing of unknown ptr 0x%" PRIx64 "\n", i + 1, entry->u.old_ptr);
106               printf("    %s\n", memory_trace::CreateStringFromEntry(*entry).c_str());
107               errors_found++;
108               if (attempt_repair) {
109                 printf("  Unable to repair this failure.\n");
110               }
111             }
112           } else {
113             if (attempt_repair) {
114               erased = entry_iter->second;
115             }
116             live_ptrs.erase(entry_iter);
117           }
118         }
119         break;
120       case memory_trace::FREE:
121         if (entry->ptr != 0) {
122           // Verify pointer is present.
123           auto entry_iter = live_ptrs.find(entry->ptr);
124           if (entry_iter == live_ptrs.end()) {
125             printf("  Line %zu: freeing of unknown ptr 0x%" PRIx64 "\n", i + 1, entry->ptr);
126             printf("    %s\n", memory_trace::CreateStringFromEntry(*entry).c_str());
127             errors_found++;
128             if (attempt_repair) {
129               printf("  Unable to repair this failure.\n");
130             }
131           } else {
132             live_ptrs.erase(entry_iter);
133           }
134         }
135         break;
136       case memory_trace::THREAD_DONE:
137       case memory_trace::UNKNOWN:
138         break;
139     }
140 
141     if (ptr == 0) {
142       continue;
143     }
144 
145     auto old_entry = live_ptrs.find(ptr);
146     if (old_entry != live_ptrs.end()) {
147       printf("  Line %zu: duplicate ptr 0x%" PRIx64 "\n", i + 1, ptr);
148       printf("    Original entry at line %zu:\n", old_entry->second.second);
149       printf("      %s\n", memory_trace::CreateStringFromEntry(*old_entry->second.first).c_str());
150       printf("    Duplicate entry at line %zu:\n", i + 1);
151       printf("      %s\n", memory_trace::CreateStringFromEntry(*entry).c_str());
152       errors_found++;
153       if (attempt_repair) {
154         // There is a small chance of a race where the same pointer is returned
155         // in two different threads before the free is recorded. If this occurs,
156         // the way to repair is to search forward for the free of the pointer and
157         // swap the two entries.
158         bool fixed = false;
159         for (size_t j = i + 1; j < num_entries; j++) {
160           if ((entries[j].type == memory_trace::FREE && entries[j].ptr == ptr) ||
161               (entries[j].type == memory_trace::REALLOC && entries[j].u.old_ptr == ptr)) {
162             memory_trace::Entry tmp_entry = *entry;
163             *entry = entries[j];
164             entries[j] = tmp_entry;
165             errors_repaired++;
166 
167             live_ptrs.erase(old_entry);
168             if (entry->type == memory_trace::REALLOC) {
169               if (entry->ptr != 0) {
170                 // Need to add the newly allocated pointer.
171                 live_ptrs[entry->ptr] = std::make_pair(entry, i + 1);
172               }
173               if (erased.first != nullptr) {
174                 // Need to put the erased old ptr back.
175                 live_ptrs[tmp_entry.u.old_ptr] = erased;
176               }
177             }
178             fixed = true;
179             break;
180           }
181         }
182         if (!fixed) {
183           printf("  Unable to fix error.\n");
184         }
185       }
186     } else {
187       live_ptrs[ptr] = std::make_pair(entry, i + 1);
188     }
189 
190     if (entry->present_bytes != -1 && size != 0 &&
191         static_cast<size_t>(entry->present_bytes) > size) {
192       printf("Line %zu: present bytes %" PRId64 " greater than size %zu\n  %s\n", i + 1,
193              entry->present_bytes, size, memory_trace::CreateStringFromEntry(*entry).c_str());
194       errors_found++;
195     }
196   }
197 
198   if (errors_found != 0) {
199     printf("Trace %s is not valid.\n", trace_file);
200     if (attempt_repair) {
201       // Save the repaired data out to a file.
202       std::string repair_file(std::string(trace_file) + ".repair");
203       printf("Creating repaired trace file %s...\n", repair_file.c_str());
204       if (!WriteRepairEntries(repair_file, entries, num_entries)) {
205         printf("Failed trying to write repaired entries to file.\n");
206       } else if (errors_repaired == errors_found) {
207         printf("Repaired file is complete, no more errors.\n");
208       } else {
209         printf("Repaired file is still not valid.\n");
210       }
211     }
212   } else if (attempt_repair) {
213     printf("Trace %s is valid, no repair needed.\n", trace_file);
214   } else {
215     printf("Trace %s is valid.\n", trace_file);
216   }
217 
218   FreeEntries(entries, num_entries);
219 }
220 
main(int argc,char ** argv)221 int main(int argc, char** argv) {
222   option options[] = {
223       {"attempt_repair", no_argument, nullptr, 'a'},
224       {nullptr, 0, nullptr, 0},
225   };
226   int option_index = 0;
227   int opt = getopt_long(argc, argv, "", options, &option_index);
228   if (argc == 1 || (argc == 2 && opt != -1)) {
229     fprintf(stderr, "Requires at least one TRACE_FILE\n");
230     Usage();
231   }
232 
233   bool attempt_repair = false;
234   if (opt == 'a') {
235     attempt_repair = true;
236   } else if (opt != -1) {
237     Usage();
238   }
239 
240   for (int i = 1; i < argc; i++) {
241     if (i + 1 == optind) {
242       continue;
243     }
244     VerifyTrace(argv[i], attempt_repair);
245   }
246 
247   return 0;
248 }
249