1 /*
2 * Copyright (C) 2020 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 <libdebuggerd/tombstone.h>
18
19 #include <inttypes.h>
20
21 #include <functional>
22 #include <set>
23 #include <string>
24 #include <unordered_set>
25 #include <utility>
26 #include <vector>
27
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <android-base/unique_fd.h>
31 #include <async_safe/log.h>
32 #include <bionic/macros.h>
33
34 #include "tombstone.pb.h"
35
36 using android::base::StringAppendF;
37 using android::base::StringPrintf;
38
39 #define CB(log, ...) callback(StringPrintf(__VA_ARGS__), log)
40 #define CBL(...) CB(true, __VA_ARGS__)
41 #define CBS(...) CB(false, __VA_ARGS__)
42 using CallbackType = std::function<void(const std::string& line, bool should_log)>;
43
abi_string(const Tombstone & tombstone)44 static const char* abi_string(const Tombstone& tombstone) {
45 switch (tombstone.arch()) {
46 case Architecture::ARM32:
47 return "arm";
48 case Architecture::ARM64:
49 return "arm64";
50 case Architecture::X86:
51 return "x86";
52 case Architecture::X86_64:
53 return "x86_64";
54 default:
55 return "<unknown>";
56 }
57 }
58
pointer_width(const Tombstone & tombstone)59 static int pointer_width(const Tombstone& tombstone) {
60 switch (tombstone.arch()) {
61 case Architecture::ARM32:
62 return 4;
63 case Architecture::ARM64:
64 return 8;
65 case Architecture::X86:
66 return 4;
67 case Architecture::X86_64:
68 return 8;
69 default:
70 return 8;
71 }
72 }
73
print_thread_header(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)74 static void print_thread_header(CallbackType callback, const Tombstone& tombstone,
75 const Thread& thread, bool should_log) {
76 const char* process_name = "<unknown>";
77 if (!tombstone.command_line().empty()) {
78 process_name = tombstone.command_line()[0].c_str();
79 CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
80 }
81 CB(should_log, "pid: %d, tid: %d, name: %s >>> %s <<<", tombstone.pid(), thread.id(),
82 thread.name().c_str(), process_name);
83 CB(should_log, "uid: %d", tombstone.uid());
84 if (thread.tagged_addr_ctrl() != -1) {
85 CB(should_log, "tagged_addr_ctrl: %016" PRIx64, thread.tagged_addr_ctrl());
86 }
87 }
88
print_register_row(CallbackType callback,int word_size,std::vector<std::pair<std::string,uint64_t>> row,bool should_log)89 static void print_register_row(CallbackType callback, int word_size,
90 std::vector<std::pair<std::string, uint64_t>> row, bool should_log) {
91 std::string output = " ";
92 for (const auto& [name, value] : row) {
93 output += android::base::StringPrintf(" %-3s %0*" PRIx64, name.c_str(), 2 * word_size,
94 static_cast<uint64_t>(value));
95 }
96 callback(output, should_log);
97 }
98
print_thread_registers(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)99 static void print_thread_registers(CallbackType callback, const Tombstone& tombstone,
100 const Thread& thread, bool should_log) {
101 static constexpr size_t column_count = 4;
102 std::vector<std::pair<std::string, uint64_t>> current_row;
103 std::vector<std::pair<std::string, uint64_t>> special_row;
104 std::unordered_set<std::string> special_registers;
105
106 int word_size = pointer_width(tombstone);
107
108 switch (tombstone.arch()) {
109 case Architecture::ARM32:
110 special_registers = {"ip", "lr", "sp", "pc", "pst"};
111 break;
112
113 case Architecture::ARM64:
114 special_registers = {"ip", "lr", "sp", "pc", "pst"};
115 break;
116
117 case Architecture::X86:
118 special_registers = {"ebp", "esp", "eip"};
119 break;
120
121 case Architecture::X86_64:
122 special_registers = {"rbp", "rsp", "rip"};
123 break;
124
125 default:
126 async_safe_fatal("unknown architecture");
127 }
128
129 for (const auto& reg : thread.registers()) {
130 auto row = ¤t_row;
131 if (special_registers.count(reg.name()) == 1) {
132 row = &special_row;
133 }
134
135 row->emplace_back(reg.name(), reg.u64());
136 if (current_row.size() == column_count) {
137 print_register_row(callback, word_size, current_row, should_log);
138 current_row.clear();
139 }
140 }
141
142 if (!current_row.empty()) {
143 print_register_row(callback, word_size, current_row, should_log);
144 }
145
146 print_register_row(callback, word_size, special_row, should_log);
147 }
148
print_backtrace(CallbackType callback,const Tombstone & tombstone,const google::protobuf::RepeatedPtrField<BacktraceFrame> & backtrace,bool should_log)149 static void print_backtrace(CallbackType callback, const Tombstone& tombstone,
150 const google::protobuf::RepeatedPtrField<BacktraceFrame>& backtrace,
151 bool should_log) {
152 int index = 0;
153 for (const auto& frame : backtrace) {
154 std::string function;
155
156 if (!frame.function_name().empty()) {
157 function =
158 StringPrintf(" (%s+%" PRId64 ")", frame.function_name().c_str(), frame.function_offset());
159 }
160
161 std::string build_id;
162 if (!frame.build_id().empty()) {
163 build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
164 }
165
166 CB(should_log, " #%02d pc %0*" PRIx64 " %s%s%s", index++, pointer_width(tombstone) * 2,
167 frame.rel_pc(), frame.file_name().c_str(), function.c_str(), build_id.c_str());
168 }
169 }
170
print_thread_backtrace(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)171 static void print_thread_backtrace(CallbackType callback, const Tombstone& tombstone,
172 const Thread& thread, bool should_log) {
173 CBS("");
174 CB(should_log, "backtrace:");
175 if (!thread.backtrace_note().empty()) {
176 CB(should_log, " NOTE: %s",
177 android::base::Join(thread.backtrace_note(), "\n NOTE: ").c_str());
178 }
179 print_backtrace(callback, tombstone, thread.current_backtrace(), should_log);
180 }
181
print_thread_memory_dump(CallbackType callback,const Tombstone & tombstone,const Thread & thread)182 static void print_thread_memory_dump(CallbackType callback, const Tombstone& tombstone,
183 const Thread& thread) {
184 static constexpr size_t bytes_per_line = 16;
185 static_assert(bytes_per_line == kTagGranuleSize);
186 int word_size = pointer_width(tombstone);
187 for (const auto& mem : thread.memory_dump()) {
188 CBS("");
189 if (mem.mapping_name().empty()) {
190 CBS("memory near %s:", mem.register_name().c_str());
191 } else {
192 CBS("memory near %s (%s):", mem.register_name().c_str(), mem.mapping_name().c_str());
193 }
194 uint64_t addr = mem.begin_address();
195 for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {
196 uint64_t tagged_addr = addr;
197 if (mem.has_arm_mte_metadata() &&
198 mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {
199 tagged_addr |=
200 static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])
201 << 56;
202 }
203 std::string line = StringPrintf(" %0*" PRIx64, word_size * 2, tagged_addr + offset);
204
205 size_t bytes = std::min(bytes_per_line, mem.memory().size() - offset);
206 for (size_t i = 0; i < bytes; i += word_size) {
207 uint64_t word = 0;
208
209 // Assumes little-endian, but what doesn't?
210 memcpy(&word, mem.memory().data() + offset + i, word_size);
211
212 StringAppendF(&line, " %0*" PRIx64, word_size * 2, word);
213 }
214
215 char ascii[bytes_per_line + 1];
216
217 memset(ascii, '.', sizeof(ascii));
218 ascii[bytes_per_line] = '\0';
219
220 for (size_t i = 0; i < bytes; ++i) {
221 uint8_t byte = mem.memory()[offset + i];
222 if (byte >= 0x20 && byte < 0x7f) {
223 ascii[i] = byte;
224 }
225 }
226
227 CBS("%s %s", line.c_str(), ascii);
228 }
229 }
230 }
231
print_thread(CallbackType callback,const Tombstone & tombstone,const Thread & thread)232 static void print_thread(CallbackType callback, const Tombstone& tombstone, const Thread& thread) {
233 print_thread_header(callback, tombstone, thread, false);
234 print_thread_registers(callback, tombstone, thread, false);
235 print_thread_backtrace(callback, tombstone, thread, false);
236 print_thread_memory_dump(callback, tombstone, thread);
237 }
238
print_tag_dump(CallbackType callback,const Tombstone & tombstone)239 static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {
240 if (!tombstone.has_signal_info()) return;
241
242 const Signal& signal = tombstone.signal_info();
243
244 if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {
245 return;
246 }
247
248 const MemoryDump& memory_dump = signal.fault_adjacent_metadata();
249
250 if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {
251 return;
252 }
253
254 const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();
255
256 CBS("");
257 CBS("Memory tags around the fault address (0x%" PRIx64 "), one tag per %zu bytes:",
258 signal.fault_address(), kTagGranuleSize);
259 constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
260
261 size_t tag_index = 0;
262 size_t num_tags = tags.length();
263 uintptr_t fault_granule = untag_address(signal.fault_address()) & ~(kTagGranuleSize - 1);
264 for (size_t row = 0; tag_index < num_tags; ++row) {
265 uintptr_t row_addr =
266 (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;
267 std::string row_contents;
268 bool row_has_fault = false;
269
270 for (size_t column = 0; column < kNumTagColumns; ++column) {
271 uintptr_t granule_addr = row_addr + column * kTagGranuleSize;
272 if (granule_addr < memory_dump.begin_address() ||
273 granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {
274 row_contents += " . ";
275 } else if (granule_addr == fault_granule) {
276 row_contents += StringPrintf("[%1hhx]", tags[tag_index++]);
277 row_has_fault = true;
278 } else {
279 row_contents += StringPrintf(" %1hhx ", tags[tag_index++]);
280 }
281 }
282
283 if (row_contents.back() == ' ') row_contents.pop_back();
284
285 if (row_has_fault) {
286 CBS(" =>0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
287 } else {
288 CBS(" 0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
289 }
290 }
291 }
292
print_main_thread(CallbackType callback,const Tombstone & tombstone,const Thread & thread)293 static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
294 const Thread& thread) {
295 print_thread_header(callback, tombstone, thread, true);
296
297 const Signal& signal_info = tombstone.signal_info();
298 std::string sender_desc;
299
300 if (signal_info.has_sender()) {
301 sender_desc =
302 StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
303 }
304
305 if (!tombstone.has_signal_info()) {
306 CBL("signal information missing");
307 } else {
308 std::string fault_addr_desc;
309 if (signal_info.has_fault_address()) {
310 fault_addr_desc = StringPrintf("0x%" PRIx64, signal_info.fault_address());
311 } else {
312 fault_addr_desc = "--------";
313 }
314
315 CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
316 signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
317 sender_desc.c_str(), fault_addr_desc.c_str());
318 }
319
320 if (tombstone.causes_size() == 1) {
321 CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
322 }
323
324 if (!tombstone.abort_message().empty()) {
325 CBL("Abort message: '%s'", tombstone.abort_message().c_str());
326 }
327
328 print_thread_registers(callback, tombstone, thread, true);
329 print_thread_backtrace(callback, tombstone, thread, true);
330
331 if (tombstone.causes_size() > 1) {
332 CBS("");
333 CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
334 "order of probability.");
335 }
336
337 for (const Cause& cause : tombstone.causes()) {
338 if (tombstone.causes_size() > 1) {
339 CBS("");
340 CBL("Cause: %s", cause.human_readable().c_str());
341 }
342
343 if (cause.has_memory_error() && cause.memory_error().has_heap()) {
344 const HeapObject& heap_object = cause.memory_error().heap();
345
346 if (heap_object.deallocation_backtrace_size() != 0) {
347 CBS("");
348 CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
349 print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true);
350 }
351
352 if (heap_object.allocation_backtrace_size() != 0) {
353 CBS("");
354 CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
355 print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true);
356 }
357 }
358 }
359
360 print_tag_dump(callback, tombstone);
361
362 print_thread_memory_dump(callback, tombstone, thread);
363
364 CBS("");
365 CBS("memory map (%d %s):", tombstone.memory_mappings().size(),
366 tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
367 int word_size = pointer_width(tombstone);
368 const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
369 if (word_size == 8) {
370 uint64_t top = ptr >> 32;
371 uint64_t bottom = ptr & 0xFFFFFFFF;
372 return StringPrintf("%08" PRIx64 "'%08" PRIx64, top, bottom);
373 }
374
375 return StringPrintf("%0*" PRIx64, word_size * 2, ptr);
376 };
377
378 for (const auto& map : tombstone.memory_mappings()) {
379 std::string line = " ";
380 StringAppendF(&line, "%s-%s", format_pointer(map.begin_address()).c_str(),
381 format_pointer(map.end_address() - 1).c_str());
382 StringAppendF(&line, " %s%s%s", map.read() ? "r" : "-", map.write() ? "w" : "-",
383 map.execute() ? "x" : "-");
384 StringAppendF(&line, " %8" PRIx64 " %8" PRIx64, map.offset(),
385 map.end_address() - map.begin_address());
386
387 if (!map.mapping_name().empty()) {
388 StringAppendF(&line, " %s", map.mapping_name().c_str());
389
390 if (!map.build_id().empty()) {
391 StringAppendF(&line, " (BuildId: %s)", map.build_id().c_str());
392 }
393
394 if (map.load_bias() != 0) {
395 StringAppendF(&line, " (load bias 0x%" PRIx64 ")", map.load_bias());
396 }
397 }
398
399 CBS("%s", line.c_str());
400 }
401 }
402
print_logs(CallbackType callback,const Tombstone & tombstone,int tail)403 void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
404 for (const auto& buffer : tombstone.log_buffers()) {
405 if (tail) {
406 CBS("--------- tail end of log %s", buffer.name().c_str());
407 } else {
408 CBS("--------- log %s", buffer.name().c_str());
409 }
410
411 int begin = 0;
412 if (tail != 0) {
413 begin = std::max(0, buffer.logs().size() - tail);
414 }
415
416 for (int i = begin; i < buffer.logs().size(); ++i) {
417 const LogMessage& msg = buffer.logs(i);
418
419 static const char* kPrioChars = "!.VDIWEFS";
420 char priority = (msg.priority() < strlen(kPrioChars) ? kPrioChars[msg.priority()] : '?');
421 CBS("%s %5u %5u %c %-8s: %s", msg.timestamp().c_str(), msg.pid(), msg.tid(), priority,
422 msg.tag().c_str(), msg.message().c_str());
423 }
424 }
425 }
426
tombstone_proto_to_text(const Tombstone & tombstone,CallbackType callback)427 bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) {
428 CBL("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***");
429 CBL("Build fingerprint: '%s'", tombstone.build_fingerprint().c_str());
430 CBL("Revision: '%s'", tombstone.revision().c_str());
431 CBL("ABI: '%s'", abi_string(tombstone));
432 CBL("Timestamp: %s", tombstone.timestamp().c_str());
433 CBL("Process uptime: %ds", tombstone.process_uptime());
434
435 // Process header
436 const auto& threads = tombstone.threads();
437 auto main_thread_it = threads.find(tombstone.tid());
438 if (main_thread_it == threads.end()) {
439 CBL("failed to find entry for main thread in tombstone");
440 return false;
441 }
442
443 const auto& main_thread = main_thread_it->second;
444
445 print_main_thread(callback, tombstone, main_thread);
446
447 print_logs(callback, tombstone, 50);
448
449 // protobuf's map is unordered, so sort the keys first.
450 std::set<int> thread_ids;
451 for (const auto& [tid, _] : threads) {
452 if (tid != tombstone.tid()) {
453 thread_ids.insert(tid);
454 }
455 }
456
457 for (const auto& tid : thread_ids) {
458 CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
459 print_thread(callback, tombstone, threads.find(tid)->second);
460 }
461
462 if (tombstone.open_fds().size() > 0) {
463 CBS("");
464 CBS("open files:");
465 for (const auto& fd : tombstone.open_fds()) {
466 std::optional<std::string> owner;
467 if (!fd.owner().empty()) {
468 owner = StringPrintf("owned by %s 0x%" PRIx64, fd.owner().c_str(), fd.tag());
469 }
470
471 CBS(" fd %d: %s (%s)", fd.fd(), fd.path().c_str(), owner ? owner->c_str() : "unowned");
472 }
473 }
474
475 print_logs(callback, tombstone, 0);
476
477 return true;
478 }
479