xref: /aosp_15_r20/system/core/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp (revision 00c7fec1bb09f3284aad6a6f96d2f63dfc3650ad)
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_proto_to_text.h>
18 #include <libdebuggerd/utility_host.h>
19 
20 #include <inttypes.h>
21 
22 #include <algorithm>
23 #include <functional>
24 #include <optional>
25 #include <set>
26 #include <string>
27 #include <unordered_set>
28 #include <utility>
29 #include <vector>
30 
31 #include <android-base/stringprintf.h>
32 #include <android-base/strings.h>
33 #include <android-base/unique_fd.h>
34 
35 #include "libdebuggerd/utility_host.h"
36 #include "tombstone.pb.h"
37 
38 using android::base::StringAppendF;
39 using android::base::StringPrintf;
40 
41 #define CB(log, ...) callback(StringPrintf(__VA_ARGS__), log)
42 #define CBL(...) CB(true, __VA_ARGS__)
43 #define CBS(...) CB(false, __VA_ARGS__)
44 using CallbackType = std::function<void(const std::string& line, bool should_log)>;
45 using SymbolizeCallbackType = std::function<void(const BacktraceFrame& frame)>;
46 
47 #define DESCRIBE_FLAG(flag) \
48   if (value & flag) {       \
49     desc += ", ";           \
50     desc += #flag;          \
51     value &= ~flag;         \
52   }
53 
describe_end(long value,std::string & desc)54 static std::string describe_end(long value, std::string& desc) {
55   if (value) {
56     desc += StringPrintf(", unknown 0x%lx", value);
57   }
58   return desc.empty() ? "" : " (" + desc.substr(2) + ")";
59 }
60 
abi_string(const Architecture & arch)61 static const char* abi_string(const Architecture& arch) {
62   switch (arch) {
63     case Architecture::ARM32:
64       return "arm";
65     case Architecture::ARM64:
66       return "arm64";
67     case Architecture::RISCV64:
68       return "riscv64";
69     case Architecture::X86:
70       return "x86";
71     case Architecture::X86_64:
72       return "x86_64";
73     default:
74       return "<unknown>";
75   }
76 }
77 
pointer_width(const Tombstone & tombstone)78 static int pointer_width(const Tombstone& tombstone) {
79   switch (tombstone.arch()) {
80     case Architecture::ARM32:
81       return 4;
82     case Architecture::ARM64:
83       return 8;
84     case Architecture::RISCV64:
85       return 8;
86     case Architecture::X86:
87       return 4;
88     case Architecture::X86_64:
89       return 8;
90     default:
91       return 8;
92   }
93 }
94 
untag_address(Architecture arch,uint64_t addr)95 static uint64_t untag_address(Architecture arch, uint64_t addr) {
96   if (arch == Architecture::ARM64) {
97     return addr & ((1ULL << 56) - 1);
98   }
99   return addr;
100 }
101 
print_thread_header(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)102 static void print_thread_header(CallbackType callback, const Tombstone& tombstone,
103                                 const Thread& thread, bool should_log) {
104   const char* process_name = "<unknown>";
105   if (!tombstone.command_line().empty()) {
106     process_name = tombstone.command_line()[0].c_str();
107     CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str());
108   } else {
109     CB(should_log, "Cmdline: <unknown>");
110   }
111   CB(should_log, "pid: %d, tid: %d, name: %s  >>> %s <<<", tombstone.pid(), thread.id(),
112      thread.name().c_str(), process_name);
113   CB(should_log, "uid: %d", tombstone.uid());
114   if (thread.tagged_addr_ctrl() != -1) {
115     CB(should_log, "tagged_addr_ctrl: %016" PRIx64 "%s", thread.tagged_addr_ctrl(),
116        describe_tagged_addr_ctrl(thread.tagged_addr_ctrl()).c_str());
117   }
118   if (thread.pac_enabled_keys() != -1) {
119     CB(should_log, "pac_enabled_keys: %016" PRIx64 "%s", thread.pac_enabled_keys(),
120        describe_pac_enabled_keys(thread.pac_enabled_keys()).c_str());
121   }
122 }
123 
print_register_row(CallbackType callback,int word_size,std::vector<std::pair<std::string,uint64_t>> row,bool should_log)124 static void print_register_row(CallbackType callback, int word_size,
125                                std::vector<std::pair<std::string, uint64_t>> row, bool should_log) {
126   std::string output = "  ";
127   for (const auto& [name, value] : row) {
128     output += android::base::StringPrintf("  %-3s %0*" PRIx64, name.c_str(), 2 * word_size,
129                                           static_cast<uint64_t>(value));
130   }
131   callback(output, should_log);
132 }
133 
print_thread_registers(CallbackType callback,const Tombstone & tombstone,const Thread & thread,bool should_log)134 static void print_thread_registers(CallbackType callback, const Tombstone& tombstone,
135                                    const Thread& thread, bool should_log) {
136   static constexpr size_t column_count = 4;
137   std::vector<std::pair<std::string, uint64_t>> current_row;
138   std::vector<std::pair<std::string, uint64_t>> special_row;
139   std::unordered_set<std::string> special_registers;
140 
141   int word_size = pointer_width(tombstone);
142 
143   switch (tombstone.arch()) {
144     case Architecture::ARM32:
145       special_registers = {"ip", "lr", "sp", "pc", "pst"};
146       break;
147 
148     case Architecture::ARM64:
149       special_registers = {"ip", "lr", "sp", "pc", "pst"};
150       break;
151 
152     case Architecture::RISCV64:
153       special_registers = {"ra", "sp", "pc"};
154       break;
155 
156     case Architecture::X86:
157       special_registers = {"ebp", "esp", "eip"};
158       break;
159 
160     case Architecture::X86_64:
161       special_registers = {"rbp", "rsp", "rip"};
162       break;
163 
164     default:
165       CBL("Unknown architecture %d printing thread registers", tombstone.arch());
166       return;
167   }
168 
169   for (const auto& reg : thread.registers()) {
170     auto row = &current_row;
171     if (special_registers.count(reg.name()) == 1) {
172       row = &special_row;
173     }
174 
175     row->emplace_back(reg.name(), reg.u64());
176     if (current_row.size() == column_count) {
177       print_register_row(callback, word_size, current_row, should_log);
178       current_row.clear();
179     }
180   }
181 
182   if (!current_row.empty()) {
183     print_register_row(callback, word_size, current_row, should_log);
184   }
185 
186   print_register_row(callback, word_size, special_row, should_log);
187 }
188 
print_backtrace(CallbackType callback,SymbolizeCallbackType symbolize,const Tombstone & tombstone,const google::protobuf::RepeatedPtrField<BacktraceFrame> & backtrace,bool should_log)189 static void print_backtrace(CallbackType callback, SymbolizeCallbackType symbolize,
190                             const Tombstone& tombstone,
191                             const google::protobuf::RepeatedPtrField<BacktraceFrame>& backtrace,
192                             bool should_log) {
193   int index = 0;
194   for (const auto& frame : backtrace) {
195     std::string function;
196 
197     if (!frame.function_name().empty()) {
198       function =
199           StringPrintf(" (%s+%" PRId64 ")", frame.function_name().c_str(), frame.function_offset());
200     }
201 
202     std::string build_id;
203     if (!frame.build_id().empty()) {
204       build_id = StringPrintf(" (BuildId: %s)", frame.build_id().c_str());
205     }
206 
207     std::string line =
208         StringPrintf("      #%02d pc %0*" PRIx64 "  %s", index++, pointer_width(tombstone) * 2,
209                      frame.rel_pc(), frame.file_name().c_str());
210     if (frame.file_map_offset() != 0) {
211       line += StringPrintf(" (offset 0x%" PRIx64 ")", frame.file_map_offset());
212     }
213     line += function + build_id;
214     CB(should_log, "%s", line.c_str());
215 
216     symbolize(frame);
217   }
218 }
219 
print_thread_backtrace(CallbackType callback,SymbolizeCallbackType symbolize,const Tombstone & tombstone,const Thread & thread,bool should_log)220 static void print_thread_backtrace(CallbackType callback, SymbolizeCallbackType symbolize,
221                                    const Tombstone& tombstone, const Thread& thread,
222                                    bool should_log) {
223   CBS("");
224   CB(should_log, "%d total frames", thread.current_backtrace().size());
225   CB(should_log, "backtrace:");
226   if (!thread.backtrace_note().empty()) {
227     CB(should_log, "  NOTE: %s",
228        android::base::Join(thread.backtrace_note(), "\n  NOTE: ").c_str());
229   }
230   print_backtrace(callback, symbolize, tombstone, thread.current_backtrace(), should_log);
231 }
232 
print_thread_memory_dump(CallbackType callback,const Tombstone & tombstone,const Thread & thread)233 static void print_thread_memory_dump(CallbackType callback, const Tombstone& tombstone,
234                                      const Thread& thread) {
235   static constexpr size_t bytes_per_line = 16;
236   static_assert(bytes_per_line == kTagGranuleSize);
237   int word_size = pointer_width(tombstone);
238   for (const auto& mem : thread.memory_dump()) {
239     CBS("");
240     if (mem.mapping_name().empty()) {
241       CBS("memory near %s:", mem.register_name().c_str());
242     } else {
243       CBS("memory near %s (%s):", mem.register_name().c_str(), mem.mapping_name().c_str());
244     }
245     uint64_t addr = mem.begin_address();
246     for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {
247       uint64_t tagged_addr = addr;
248       if (mem.has_arm_mte_metadata() &&
249           mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {
250         tagged_addr |=
251             static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])
252             << 56;
253       }
254       std::string line = StringPrintf("    %0*" PRIx64, word_size * 2, tagged_addr + offset);
255 
256       size_t bytes = std::min(bytes_per_line, mem.memory().size() - offset);
257       for (size_t i = 0; i < bytes; i += word_size) {
258         uint64_t word = 0;
259 
260         // Assumes little-endian, but what doesn't?
261         memcpy(&word, mem.memory().data() + offset + i, word_size);
262 
263         StringAppendF(&line, " %0*" PRIx64, word_size * 2, word);
264       }
265 
266       char ascii[bytes_per_line + 1];
267 
268       memset(ascii, '.', sizeof(ascii));
269       ascii[bytes_per_line] = '\0';
270 
271       for (size_t i = 0; i < bytes; ++i) {
272         uint8_t byte = mem.memory()[offset + i];
273         if (byte >= 0x20 && byte < 0x7f) {
274           ascii[i] = byte;
275         }
276       }
277 
278       CBS("%s  %s", line.c_str(), ascii);
279     }
280   }
281 }
282 
print_thread(CallbackType callback,SymbolizeCallbackType symbolize,const Tombstone & tombstone,const Thread & thread)283 static void print_thread(CallbackType callback, SymbolizeCallbackType symbolize,
284                          const Tombstone& tombstone, const Thread& thread) {
285   print_thread_header(callback, tombstone, thread, false);
286   print_thread_registers(callback, tombstone, thread, false);
287   print_thread_backtrace(callback, symbolize, tombstone, thread, false);
288   print_thread_memory_dump(callback, tombstone, thread);
289 }
290 
print_tag_dump(CallbackType callback,const Tombstone & tombstone)291 static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {
292   if (!tombstone.has_signal_info()) return;
293 
294   const Signal& signal = tombstone.signal_info();
295 
296   if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {
297     return;
298   }
299 
300   const MemoryDump& memory_dump = signal.fault_adjacent_metadata();
301 
302   if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {
303     return;
304   }
305 
306   const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();
307 
308   CBS("");
309   CBS("Memory tags around the fault address (0x%" PRIx64 "), one tag per %zu bytes:",
310       signal.fault_address(), kTagGranuleSize);
311   constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);
312 
313   size_t tag_index = 0;
314   size_t num_tags = tags.length();
315   uintptr_t fault_granule =
316       untag_address(tombstone.arch(), signal.fault_address()) & ~(kTagGranuleSize - 1);
317   for (size_t row = 0; tag_index < num_tags; ++row) {
318     uintptr_t row_addr =
319         (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;
320     std::string row_contents;
321     bool row_has_fault = false;
322 
323     for (size_t column = 0; column < kNumTagColumns; ++column) {
324       uintptr_t granule_addr = row_addr + column * kTagGranuleSize;
325       if (granule_addr < memory_dump.begin_address() ||
326           granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {
327         row_contents += " . ";
328       } else if (granule_addr == fault_granule) {
329         row_contents += StringPrintf("[%1hhx]", tags[tag_index++]);
330         row_has_fault = true;
331       } else {
332         row_contents += StringPrintf(" %1hhx ", tags[tag_index++]);
333       }
334     }
335 
336     if (row_contents.back() == ' ') row_contents.pop_back();
337 
338     if (row_has_fault) {
339       CBS("    =>0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
340     } else {
341       CBS("      0x%" PRIxPTR ":%s", row_addr, row_contents.c_str());
342     }
343   }
344 }
345 
print_memory_maps(CallbackType callback,const Tombstone & tombstone)346 static void print_memory_maps(CallbackType callback, const Tombstone& tombstone) {
347   int word_size = pointer_width(tombstone);
348   const auto format_pointer = [word_size](uint64_t ptr) -> std::string {
349     if (word_size == 8) {
350       uint64_t top = ptr >> 32;
351       uint64_t bottom = ptr & 0xFFFFFFFF;
352       return StringPrintf("%08" PRIx64 "'%08" PRIx64, top, bottom);
353     }
354 
355     return StringPrintf("%0*" PRIx64, word_size * 2, ptr);
356   };
357 
358   std::string memory_map_header =
359       StringPrintf("memory map (%d %s):", tombstone.memory_mappings().size(),
360                    tombstone.memory_mappings().size() == 1 ? "entry" : "entries");
361 
362   const Signal& signal_info = tombstone.signal_info();
363   bool has_fault_address = signal_info.has_fault_address();
364   uint64_t fault_address = untag_address(tombstone.arch(), signal_info.fault_address());
365   bool preamble_printed = false;
366   bool printed_fault_address_marker = false;
367   for (const auto& map : tombstone.memory_mappings()) {
368     if (!preamble_printed) {
369       preamble_printed = true;
370       if (has_fault_address) {
371         if (fault_address < map.begin_address()) {
372           memory_map_header +=
373               StringPrintf("\n--->Fault address falls at %s before any mapped regions",
374                            format_pointer(fault_address).c_str());
375           printed_fault_address_marker = true;
376         } else {
377           memory_map_header += " (fault address prefixed with --->)";
378         }
379       }
380       CBS("%s", memory_map_header.c_str());
381     }
382 
383     std::string line = "    ";
384     if (has_fault_address && !printed_fault_address_marker) {
385       if (fault_address < map.begin_address()) {
386         printed_fault_address_marker = true;
387         CBS("--->Fault address falls at %s between mapped regions",
388             format_pointer(fault_address).c_str());
389       } else if (fault_address >= map.begin_address() && fault_address < map.end_address()) {
390         printed_fault_address_marker = true;
391         line = "--->";
392       }
393     }
394     StringAppendF(&line, "%s-%s", format_pointer(map.begin_address()).c_str(),
395                   format_pointer(map.end_address() - 1).c_str());
396     StringAppendF(&line, " %s%s%s", map.read() ? "r" : "-", map.write() ? "w" : "-",
397                   map.execute() ? "x" : "-");
398     StringAppendF(&line, "  %8" PRIx64 "  %8" PRIx64, map.offset(),
399                   map.end_address() - map.begin_address());
400 
401     if (!map.mapping_name().empty()) {
402       StringAppendF(&line, "  %s", map.mapping_name().c_str());
403 
404       if (!map.build_id().empty()) {
405         StringAppendF(&line, " (BuildId: %s)", map.build_id().c_str());
406       }
407 
408       if (map.load_bias() != 0) {
409         StringAppendF(&line, " (load bias 0x%" PRIx64 ")", map.load_bias());
410       }
411     }
412 
413     CBS("%s", line.c_str());
414   }
415 
416   if (has_fault_address && !printed_fault_address_marker) {
417     CBS("--->Fault address falls at %s after any mapped regions",
418         format_pointer(fault_address).c_str());
419   }
420 }
421 
print_main_thread(CallbackType callback,SymbolizeCallbackType symbolize,const Tombstone & tombstone,const Thread & thread)422 static void print_main_thread(CallbackType callback, SymbolizeCallbackType symbolize,
423                               const Tombstone& tombstone, const Thread& thread) {
424   print_thread_header(callback, tombstone, thread, true);
425 
426   const Signal& signal_info = tombstone.signal_info();
427   std::string sender_desc;
428 
429   if (signal_info.has_sender()) {
430     sender_desc =
431         StringPrintf(" from pid %d, uid %d", signal_info.sender_pid(), signal_info.sender_uid());
432   }
433 
434   bool is_async_mte_crash = false;
435   bool is_mte_crash = false;
436   if (!tombstone.has_signal_info()) {
437     CBL("signal information missing");
438   } else {
439     std::string fault_addr_desc;
440     if (signal_info.has_fault_address()) {
441       fault_addr_desc =
442           StringPrintf("0x%0*" PRIx64, 2 * pointer_width(tombstone), signal_info.fault_address());
443     } else {
444       fault_addr_desc = "--------";
445     }
446 
447     CBL("signal %d (%s), code %d (%s%s), fault addr %s", signal_info.number(),
448         signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),
449         sender_desc.c_str(), fault_addr_desc.c_str());
450 #ifdef SEGV_MTEAERR
451     is_async_mte_crash = signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTEAERR;
452     is_mte_crash = is_async_mte_crash ||
453                    (signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTESERR);
454 #endif
455   }
456 
457   if (tombstone.causes_size() == 1) {
458     CBL("Cause: %s", tombstone.causes(0).human_readable().c_str());
459   }
460 
461   if (!tombstone.abort_message().empty()) {
462     CBL("Abort message: '%s'", tombstone.abort_message().c_str());
463   }
464 
465   for (const auto& crash_detail : tombstone.crash_details()) {
466     std::string oct_encoded_name = oct_encode(crash_detail.name());
467     std::string oct_encoded_data = oct_encode(crash_detail.data());
468     CBL("Extra crash detail: %s: '%s'", oct_encoded_name.c_str(), oct_encoded_data.c_str());
469   }
470 
471   print_thread_registers(callback, tombstone, thread, true);
472   if (is_async_mte_crash) {
473     CBL("Note: This crash is a delayed async MTE crash. Memory corruption has occurred");
474     CBL("      in this process. The stack trace below is the first system call or context");
475     CBL("      switch that was executed after the memory corruption happened.");
476   }
477   print_thread_backtrace(callback, symbolize, tombstone, thread, true);
478 
479   if (tombstone.causes_size() > 1) {
480     CBS("");
481     CBL("Note: multiple potential causes for this crash were detected, listing them in decreasing "
482         "order of likelihood.");
483   }
484 
485   if (tombstone.has_stack_history_buffer()) {
486     for (const StackHistoryBufferEntry& shbe : tombstone.stack_history_buffer().entries()) {
487       std::string stack_record_str = StringPrintf(
488           "stack_record fp:0x%" PRIx64 " tag:0x%" PRIx64 " pc:%s+0x%" PRIx64, shbe.fp(), shbe.tag(),
489           shbe.addr().file_name().c_str(), shbe.addr().rel_pc());
490       if (!shbe.addr().build_id().empty()) {
491         StringAppendF(&stack_record_str, " (BuildId: %s)", shbe.addr().build_id().c_str());
492       }
493 
494       CBL("%s", stack_record_str.c_str());
495     }
496   }
497 
498   for (const Cause& cause : tombstone.causes()) {
499     if (tombstone.causes_size() > 1) {
500       CBS("");
501       CBL("Cause: %s", cause.human_readable().c_str());
502     }
503 
504     if (cause.has_memory_error() && cause.memory_error().has_heap()) {
505       const HeapObject& heap_object = cause.memory_error().heap();
506 
507       if (heap_object.deallocation_backtrace_size() != 0) {
508         CBS("");
509         CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid());
510         print_backtrace(callback, symbolize, tombstone, heap_object.deallocation_backtrace(), true);
511       }
512 
513       if (heap_object.allocation_backtrace_size() != 0) {
514         CBS("");
515         CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid());
516         print_backtrace(callback, symbolize, tombstone, heap_object.allocation_backtrace(), true);
517       }
518     }
519   }
520 
521   print_tag_dump(callback, tombstone);
522 
523   if (is_mte_crash) {
524     CBS("");
525     CBL("Learn more about MTE reports: "
526         "https://source.android.com/docs/security/test/memory-safety/mte-reports");
527   }
528 
529   print_thread_memory_dump(callback, tombstone, thread);
530 
531   CBS("");
532 
533   // No memory maps to print.
534   if (!tombstone.memory_mappings().empty()) {
535     print_memory_maps(callback, tombstone);
536   } else {
537     CBS("No memory maps found");
538   }
539 }
540 
print_logs(CallbackType callback,const Tombstone & tombstone,int tail)541 void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {
542   for (const auto& buffer : tombstone.log_buffers()) {
543     if (tail) {
544       CBS("--------- tail end of log %s", buffer.name().c_str());
545     } else {
546       CBS("--------- log %s", buffer.name().c_str());
547     }
548 
549     int begin = 0;
550     if (tail != 0) {
551       begin = std::max(0, buffer.logs().size() - tail);
552     }
553 
554     for (int i = begin; i < buffer.logs().size(); ++i) {
555       const LogMessage& msg = buffer.logs(i);
556 
557       static const char* kPrioChars = "!.VDIWEFS";
558       char priority = (msg.priority() < strlen(kPrioChars) ? kPrioChars[msg.priority()] : '?');
559       CBS("%s %5u %5u %c %-8s: %s", msg.timestamp().c_str(), msg.pid(), msg.tid(), priority,
560           msg.tag().c_str(), msg.message().c_str());
561     }
562   }
563 }
564 
print_guest_thread(CallbackType callback,SymbolizeCallbackType symbolize,const Tombstone & tombstone,const Thread & guest_thread,pid_t tid,bool should_log)565 static void print_guest_thread(CallbackType callback, SymbolizeCallbackType symbolize,
566                                const Tombstone& tombstone, const Thread& guest_thread, pid_t tid,
567                                bool should_log) {
568   CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
569   CBS("Guest thread information for tid: %d", tid);
570   print_thread_registers(callback, tombstone, guest_thread, should_log);
571 
572   CBS("");
573   CB(true, "%d total frames", guest_thread.current_backtrace().size());
574   CB(true, "backtrace:");
575   print_backtrace(callback, symbolize, tombstone, guest_thread.current_backtrace(), should_log);
576 
577   print_thread_memory_dump(callback, tombstone, guest_thread);
578 }
579 
tombstone_proto_to_text(const Tombstone & tombstone,CallbackType callback,SymbolizeCallbackType symbolize)580 bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback,
581                              SymbolizeCallbackType symbolize) {
582   CBL("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***");
583   CBL("Build fingerprint: '%s'", tombstone.build_fingerprint().c_str());
584   CBL("Revision: '%s'", tombstone.revision().c_str());
585   CBL("ABI: '%s'", abi_string(tombstone.arch()));
586   if (tombstone.guest_arch() != Architecture::NONE) {
587     CBL("Guest architecture: '%s'", abi_string(tombstone.guest_arch()));
588   }
589   CBL("Timestamp: %s", tombstone.timestamp().c_str());
590   CBL("Process uptime: %ds", tombstone.process_uptime());
591 
592   // only print this info if the page size is not 4k or has been in 16k mode
593   if (tombstone.page_size() != 4096) {
594     CBL("Page size: %d bytes", tombstone.page_size());
595   } else if (tombstone.has_been_16kb_mode()) {
596     CBL("Has been in 16kb mode: yes");
597   }
598 
599   // Process header
600   const auto& threads = tombstone.threads();
601   auto main_thread_it = threads.find(tombstone.tid());
602   if (main_thread_it == threads.end()) {
603     CBL("failed to find entry for main thread in tombstone");
604     return false;
605   }
606 
607   const auto& main_thread = main_thread_it->second;
608 
609   print_main_thread(callback, symbolize, tombstone, main_thread);
610 
611   print_logs(callback, tombstone, 50);
612 
613   const auto& guest_threads = tombstone.guest_threads();
614   auto main_guest_thread_it = guest_threads.find(tombstone.tid());
615   if (main_guest_thread_it != threads.end()) {
616     print_guest_thread(callback, symbolize, tombstone, main_guest_thread_it->second,
617                        tombstone.tid(), true);
618   }
619 
620   // protobuf's map is unordered, so sort the keys first.
621   std::set<int> thread_ids;
622   for (const auto& [tid, _] : threads) {
623     if (tid != tombstone.tid()) {
624       thread_ids.insert(tid);
625     }
626   }
627 
628   for (const auto& tid : thread_ids) {
629     CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
630     print_thread(callback, symbolize, tombstone, threads.find(tid)->second);
631     auto guest_thread_it = guest_threads.find(tid);
632     if (guest_thread_it != guest_threads.end()) {
633       print_guest_thread(callback, symbolize, tombstone, guest_thread_it->second, tid, false);
634     }
635   }
636 
637   if (tombstone.open_fds().size() > 0) {
638     CBS("");
639     CBS("open files:");
640     for (const auto& fd : tombstone.open_fds()) {
641       std::optional<std::string> owner;
642       if (!fd.owner().empty()) {
643         owner = StringPrintf("owned by %s 0x%" PRIx64, fd.owner().c_str(), fd.tag());
644       }
645 
646       CBS("    fd %d: %s (%s)", fd.fd(), fd.path().c_str(), owner ? owner->c_str() : "unowned");
647     }
648   }
649 
650   print_logs(callback, tombstone, 0);
651 
652   return true;
653 }
654