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