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