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