From 4d85225467d47f3e9245543543b9a9d4e265067a Mon Sep 17 00:00:00 2001 From: Ben Hamilton Date: Mon, 6 Jun 2022 19:01:15 -0600 Subject: [PATCH] [breakpad] Add MINIDUMP_THREAD_NAME_LIST support Change-Id: I84205358ae48e757fa3b836747eadc32c2671756 Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3690389 Reviewed-by: Joshua Peraza Reviewed-by: Ivan Penkov --- src/google_breakpad/common/minidump_format.h | 24 ++ src/google_breakpad/processor/minidump.h | 81 ++++++ .../processor/process_result.h | 6 +- src/google_breakpad/processor/process_state.h | 7 + src/processor/minidump.cc | 230 ++++++++++++++++++ src/processor/minidump_dump.cc | 6 + src/processor/minidump_processor.cc | 33 +++ src/processor/process_state.cc | 1 + src/processor/testdata/thread_name_list.dmp | Bin 0 -> 18011 bytes src/tools/mac/crash_report/crash_report.mm | 14 +- 10 files changed, 399 insertions(+), 3 deletions(-) create mode 100644 src/processor/testdata/thread_name_list.dmp diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h index 7b36d112..e2366635 100644 --- a/src/google_breakpad/common/minidump_format.h +++ b/src/google_breakpad/common/minidump_format.h @@ -239,6 +239,15 @@ typedef struct { MDRVA rva; } MDLocationDescriptor; /* MINIDUMP_LOCATION_DESCRIPTOR */ +/* An MDRVA64 is an 64-bit offset into the minidump file. The beginning of the + * MDRawHeader is at offset 0. */ +typedef uint64_t MDRVA64; /* RVA64 */ + +typedef struct { + uint64_t data_size; + MDRVA64 rva; +} MDLocationDescriptor64; /* MINIDUMP_LOCATION_DESCRIPTOR64 */ + typedef struct { /* The base address of the memory range on the host that produced the @@ -332,6 +341,7 @@ typedef enum { MD_JAVASCRIPT_DATA_STREAM = 20, MD_SYSTEM_MEMORY_INFO_STREAM = 21, MD_PROCESS_VM_COUNTERS_STREAM = 22, + MD_THREAD_NAME_LIST_STREAM = 24, /* MDRawThreadNameList */ MD_LAST_RESERVED_STREAM = 0x0000ffff, /* Breakpad extension types. 0x4767 = "Gg" */ @@ -382,6 +392,20 @@ typedef struct { static const size_t MDRawThreadList_minsize = offsetof(MDRawThreadList, threads[0]); +#pragma pack(push, 4) +typedef struct { + uint32_t thread_id; + MDRVA64 thread_name_rva; /* MDString */ +} MDRawThreadName; /* MINIDUMP_THREAD_NAME */ + +typedef struct { + uint32_t number_of_thread_names; + MDRawThreadName thread_names[1]; +} MDRawThreadNameList; /* MINIDUMP_THREAD_NAME_LIST */ +#pragma pack(pop) + +static const size_t MDRawThreadNameList_minsize = + offsetof(MDRawThreadNameList, thread_names[0]); typedef struct { uint64_t base_of_image; diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h index 1c40a821..200a7e82 100644 --- a/src/google_breakpad/processor/minidump.h +++ b/src/google_breakpad/processor/minidump.h @@ -370,6 +370,86 @@ class MinidumpThreadList : public MinidumpStream { DISALLOW_COPY_AND_ASSIGN(MinidumpThreadList); }; +// MinidumpThreadName contains the name of a thread. +class MinidumpThreadName : public MinidumpObject { + public: + virtual ~MinidumpThreadName(); + + const MDRawThreadName* thread_name() const { + return valid_ ? &thread_name_ : NULL; + } + + // Gets the thread ID. + virtual bool GetThreadID(uint32_t* thread_id) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + // Returns the name of the thread. + virtual std::string GetThreadName() const; + + protected: + explicit MinidumpThreadName(Minidump* minidump); + + private: + // These objects are managed by MinidumpThreadNameList. + friend class MinidumpThreadNameList; + + // This works like MinidumpStream::Read, but is driven by + // MinidumpThreadNameList. No size checking is done, because + // MinidumpThreadNameList handles that directly. + bool Read(); + + // Reads indirectly-referenced data, including the thread name. + bool ReadAuxiliaryData(); + + // True after a successful Read. This is different from valid_, which is not + // set true until ReadAuxiliaryData also completes successfully. + // thread_name_valid_ is only used by ReadAuxiliaryData and the functions it + // calls to determine whether the object is ready for auxiliary data to be + // read. + bool thread_name_valid_; + + MDRawThreadName thread_name_; + + // Cached thread name. + const string* name_; +}; + +// MinidumpThreadNameList contains all of the names of the threads (as +// MinidumpThreadNames) in a process. +class MinidumpThreadNameList : public MinidumpStream { + public: + virtual ~MinidumpThreadNameList(); + + virtual unsigned int thread_name_count() const { + return valid_ ? thread_name_count_ : 0; + } + + // Sequential access to thread names. + virtual MinidumpThreadName* GetThreadNameAtIndex(unsigned int index) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + protected: + explicit MinidumpThreadNameList(Minidump* aMinidump); + + private: + friend class Minidump; + + typedef vector MinidumpThreadNames; + + static const uint32_t kStreamType = MD_THREAD_NAME_LIST_STREAM; + + bool Read(uint32_t aExpectedSize) override; + + // The list of thread names. + MinidumpThreadNames* thread_names_; + uint32_t thread_name_count_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpThreadNameList); +}; // MinidumpModule wraps MDRawModule, which contains information about loaded // code modules. Access is provided to various data referenced indirectly @@ -1188,6 +1268,7 @@ class Minidump { // to avoid exposing an ugly API (GetStream needs to accept a garbage // parameter). virtual MinidumpThreadList* GetThreadList(); + virtual MinidumpThreadNameList* GetThreadNameList(); virtual MinidumpModuleList* GetModuleList(); virtual MinidumpMemoryList* GetMemoryList(); virtual MinidumpException* GetException(); diff --git a/src/google_breakpad/processor/process_result.h b/src/google_breakpad/processor/process_result.h index 15c7213e..9317e98e 100644 --- a/src/google_breakpad/processor/process_result.h +++ b/src/google_breakpad/processor/process_result.h @@ -56,9 +56,13 @@ enum ProcessResult { PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than one // requesting thread. - PROCESS_SYMBOL_SUPPLIER_INTERRUPTED // The dump processing was + PROCESS_SYMBOL_SUPPLIER_INTERRUPTED, // The dump processing was // interrupted by the // SymbolSupplier(not fatal). + + PROCESS_ERROR_GETTING_THREAD_NAME, // There was an error getting one + // thread's name from the dump. + }; } // namespace google_breakpad diff --git a/src/google_breakpad/processor/process_state.h b/src/google_breakpad/processor/process_state.h index 9bc44c45..c13246fd 100644 --- a/src/google_breakpad/processor/process_state.h +++ b/src/google_breakpad/processor/process_state.h @@ -111,6 +111,7 @@ class ProcessState { const vector* thread_memory_regions() const { return &thread_memory_regions_; } + const vector* thread_names() const { return &thread_names_; } const SystemInfo* system_info() const { return &system_info_; } const CodeModules* modules() const { return modules_; } const CodeModules* unloaded_modules() const { return unloaded_modules_; } @@ -176,6 +177,12 @@ class ProcessState { vector threads_; vector thread_memory_regions_; + // Names of each thread at the time of the crash, one for each entry in + // threads_. Note that a thread's name might be empty if there was no + // corresponding ThreadNamesStream in the minidump, or if a particular thread + // ID was not present in the THREAD_NAME_LIST. + vector thread_names_; + // OS and CPU information. SystemInfo system_info_; diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 572c717c..db7a4a16 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -1843,6 +1843,229 @@ void MinidumpThreadList::Print() { } } +// +// MinidumpThreadName +// + +MinidumpThreadName::MinidumpThreadName(Minidump* minidump) + : MinidumpObject(minidump), + thread_name_valid_(false), + thread_name_(), + name_(NULL) {} + +MinidumpThreadName::~MinidumpThreadName() { + delete name_; +} + +bool MinidumpThreadName::Read() { + // Invalidate cached data. + delete name_; + name_ = NULL; + + valid_ = false; + + if (!minidump_->ReadBytes(&thread_name_, sizeof(thread_name_))) { + BPLOG(ERROR) << "MinidumpThreadName cannot read thread name"; + return false; + } + + if (minidump_->swap()) { + Swap(&thread_name_.thread_id); + Swap(&thread_name_.thread_name_rva); + } + + thread_name_valid_ = true; + return true; +} + +bool MinidumpThreadName::ReadAuxiliaryData() { + if (!thread_name_valid_) { + BPLOG(ERROR) << "Invalid MinidumpThreadName for ReadAuxiliaryData"; + return false; + } + + // On 32-bit systems, check that the RVA64 is within range (off_t is 32 bits). + if (thread_name_.thread_name_rva > numeric_limits::max()) { + BPLOG(ERROR) << "MinidumpThreadName RVA64 out of range"; + return false; + } + + // Read the thread name. + const off_t thread_name_rva_offset = + static_cast(thread_name_.thread_name_rva); + name_ = minidump_->ReadString(thread_name_rva_offset); + if (!name_) { + BPLOG(ERROR) << "MinidumpThreadName could not read name"; + return false; + } + + // At this point, we have enough info for the thread name to be valid. + valid_ = true; + return true; +} + +bool MinidumpThreadName::GetThreadID(uint32_t* thread_id) const { + BPLOG_IF(ERROR, !thread_id) << "MinidumpThreadName::GetThreadID requires " + "|thread_id|"; + assert(thread_id); + *thread_id = 0; + + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpThreadName for GetThreadID"; + return false; + } + + *thread_id = thread_name_.thread_id; + return true; +} + +string MinidumpThreadName::GetThreadName() const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpThreadName for GetThreadName"; + return ""; + } + + return *name_; +} + +void MinidumpThreadName::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpThreadName cannot print invalid data"; + return; + } + + printf("MDRawThreadName\n"); + printf(" thread_id = 0x%x\n", thread_name_.thread_id); + printf(" thread_name_rva = 0x%" PRIx64 "\n", + thread_name_.thread_name_rva); + printf(" thread_name = \"%s\"\n", GetThreadName().c_str()); + printf("\n"); +} + +// +// MinidumpThreadNameList +// + +MinidumpThreadNameList::MinidumpThreadNameList(Minidump* minidump) + : MinidumpStream(minidump), thread_names_(NULL), thread_name_count_(0) {} + +MinidumpThreadNameList::~MinidumpThreadNameList() { + delete thread_names_; +} + +bool MinidumpThreadNameList::Read(uint32_t expected_size) { + // Invalidate cached data. + delete thread_names_; + thread_names_ = NULL; + thread_name_count_ = 0; + + valid_ = false; + + uint32_t thread_name_count; + if (expected_size < sizeof(thread_name_count)) { + BPLOG(ERROR) << "MinidumpThreadNameList count size mismatch, " + << expected_size << " < " << sizeof(thread_name_count); + return false; + } + if (!minidump_->ReadBytes(&thread_name_count, sizeof(thread_name_count))) { + BPLOG(ERROR) << "MinidumpThreadNameList cannot read thread name count"; + return false; + } + + if (minidump_->swap()) + Swap(&thread_name_count); + + if (thread_name_count > + numeric_limits::max() / sizeof(MDRawThreadName)) { + BPLOG(ERROR) << "MinidumpThreadNameList thread name count " + << thread_name_count << " would cause multiplication overflow"; + return false; + } + + if (expected_size != + sizeof(thread_name_count) + thread_name_count * sizeof(MDRawThreadName)) { + BPLOG(ERROR) << "MinidumpThreadNameList size mismatch, " << expected_size + << " != " + << sizeof(thread_name_count) + + thread_name_count * sizeof(MDRawThreadName); + return false; + } + + if (thread_name_count > MinidumpThreadList::max_threads()) { + BPLOG(ERROR) << "MinidumpThreadNameList count " << thread_name_count + << " exceeds maximum " << MinidumpThreadList::max_threads(); + return false; + } + + if (thread_name_count != 0) { + scoped_ptr thread_names(new MinidumpThreadNames( + thread_name_count, MinidumpThreadName(minidump_))); + + for (unsigned int thread_name_index = 0; + thread_name_index < thread_name_count; ++thread_name_index) { + MinidumpThreadName* thread_name = &(*thread_names)[thread_name_index]; + + // Assume that the file offset is correct after the last read. + if (!thread_name->Read()) { + BPLOG(ERROR) << "MinidumpThreadNameList cannot read thread name " + << thread_name_index << "/" << thread_name_count; + return false; + } + } + + for (unsigned int thread_name_index = 0; + thread_name_index < thread_name_count; ++thread_name_index) { + MinidumpThreadName* thread_name = &(*thread_names)[thread_name_index]; + + if (!thread_name->ReadAuxiliaryData() && !thread_name->valid()) { + BPLOG(ERROR) << "MinidumpThreadNameList cannot read thread name " + << thread_name_index << "/" << thread_name_count; + return false; + } + } + + thread_names_ = thread_names.release(); + } + + thread_name_count_ = thread_name_count; + + valid_ = true; + return true; +} + +MinidumpThreadName* MinidumpThreadNameList::GetThreadNameAtIndex( + unsigned int index) const { + if (!valid_) { + BPLOG(ERROR) << "Invalid MinidumpThreadNameList for GetThreadNameAtIndex"; + return NULL; + } + + if (index >= thread_name_count_) { + BPLOG(ERROR) << "MinidumpThreadNameList index out of range: " << index + << "/" << thread_name_count_; + return NULL; + } + + return &(*thread_names_)[index]; +} + +void MinidumpThreadNameList::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpThreadNameList cannot print invalid data"; + return; + } + + printf("MinidumpThreadNameList\n"); + printf(" thread_name_count = %d\n", thread_name_count_); + printf("\n"); + + for (unsigned int thread_name_index = 0; + thread_name_index < thread_name_count_; ++thread_name_index) { + printf("thread_name[%d]\n", thread_name_index); + + (*thread_names_)[thread_name_index].Print(); + } +} // // MinidumpModule @@ -5280,6 +5503,7 @@ bool Minidump::Read() { unsigned int stream_type = directory_entry->stream_type; switch (stream_type) { case MD_THREAD_LIST_STREAM: + case MD_THREAD_NAME_LIST_STREAM: case MD_MODULE_LIST_STREAM: case MD_MEMORY_LIST_STREAM: case MD_EXCEPTION_STREAM: @@ -5318,6 +5542,10 @@ MinidumpThreadList* Minidump::GetThreadList() { return GetStream(&thread_list); } +MinidumpThreadNameList* Minidump::GetThreadNameList() { + MinidumpThreadNameList* thread_name_list; + return GetStream(&thread_name_list); +} MinidumpModuleList* Minidump::GetModuleList() { MinidumpModuleList* module_list; @@ -5417,6 +5645,8 @@ static const char* get_stream_name(uint32_t stream_type) { return "MD_RESERVED_STREAM_1"; case MD_THREAD_LIST_STREAM: return "MD_THREAD_LIST_STREAM"; + case MD_THREAD_NAME_LIST_STREAM: + return "MD_THREAD_NAME_LIST_STREAM"; case MD_MODULE_LIST_STREAM: return "MD_MODULE_LIST_STREAM"; case MD_MEMORY_LIST_STREAM: diff --git a/src/processor/minidump_dump.cc b/src/processor/minidump_dump.cc index ed2edaf0..ce7432e5 100644 --- a/src/processor/minidump_dump.cc +++ b/src/processor/minidump_dump.cc @@ -45,6 +45,7 @@ namespace { using google_breakpad::Minidump; using google_breakpad::MinidumpThreadList; +using google_breakpad::MinidumpThreadNameList; using google_breakpad::MinidumpModuleList; using google_breakpad::MinidumpMemoryInfoList; using google_breakpad::MinidumpMemoryList; @@ -122,6 +123,11 @@ static bool PrintMinidumpDump(const Options& options) { thread_list->Print(); } + MinidumpThreadNameList *thread_name_list = minidump.GetThreadNameList(); + if (thread_name_list) { + thread_name_list->Print(); + } + // It's useful to be able to see the full list of modules here even if it // would cause minidump_stackwalk to fail. MinidumpModuleList::set_max_modules(UINT32_MAX); diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc index ac86fbd3..fd4b28e2 100644 --- a/src/processor/minidump_processor.cc +++ b/src/processor/minidump_processor.cc @@ -32,7 +32,9 @@ #include #include +#include #include +#include #include "common/scoped_ptr.h" #include "common/stdio_wrapper.h" @@ -199,6 +201,28 @@ ProcessResult MinidumpProcessor::Process( // Reset frame_symbolizer_ at the beginning of stackwalk for each minidump. frame_symbolizer_->Reset(); + + MinidumpThreadNameList* thread_names = dump->GetThreadNameList(); + std::map thread_id_to_name; + if (thread_names) { + const unsigned int thread_name_count = thread_names->thread_name_count(); + for (unsigned int thread_name_index = 0; + thread_name_index < thread_name_count; + ++thread_name_index) { + MinidumpThreadName* thread_name = thread_names->GetThreadNameAtIndex(thread_name_index); + if (!thread_name) { + BPLOG(ERROR) << "Could not get thread name for thread at index " << thread_name_index; + return PROCESS_ERROR_GETTING_THREAD_NAME; + } + uint32_t thread_id; + if (!thread_name->GetThreadID(&thread_id)) { + BPLOG(ERROR) << "Could not get thread ID for thread at index " << thread_name_index; + return PROCESS_ERROR_GETTING_THREAD_NAME; + } + thread_id_to_name.insert(std::make_pair(thread_id, thread_name->GetThreadName())); + } + } + for (unsigned int thread_index = 0; thread_index < thread_count; ++thread_index) { @@ -220,6 +244,14 @@ ProcessResult MinidumpProcessor::Process( } thread_string += " id " + HexString(thread_id); + auto thread_name_iter = thread_id_to_name.find(thread_id); + string thread_name; + if (thread_name_iter != thread_id_to_name.end()) { + thread_name = thread_name_iter->second; + } + if (!thread_name.empty()) { + thread_string += " name [" + thread_name + "]"; + } BPLOG(INFO) << "Looking at thread " << thread_string; // If this thread is the thread that produced the minidump, don't process @@ -311,6 +343,7 @@ ProcessResult MinidumpProcessor::Process( stack->set_tid(thread_id); process_state->threads_.push_back(stack.release()); process_state->thread_memory_regions_.push_back(thread_memory); + process_state->thread_names_.push_back(thread_name); } if (interrupted) { diff --git a/src/processor/process_state.cc b/src/processor/process_state.cc index c9269e41..52e484d9 100644 --- a/src/processor/process_state.cc +++ b/src/processor/process_state.cc @@ -58,6 +58,7 @@ void ProcessState::Clear() { } threads_.clear(); system_info_.Clear(); + thread_names_.clear(); // modules_without_symbols_ and modules_with_corrupt_symbols_ DO NOT own // the underlying CodeModule pointers. Just clear the vectors. modules_without_symbols_.clear(); diff --git a/src/processor/testdata/thread_name_list.dmp b/src/processor/testdata/thread_name_list.dmp new file mode 100644 index 0000000000000000000000000000000000000000..fbe84b6378f09b9140e935276fa147758e19666d GIT binary patch literal 18011 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 O&AP0>xNAD=xcfh>9~->@ literal 0 HcmV?d00001 diff --git a/src/tools/mac/crash_report/crash_report.mm b/src/tools/mac/crash_report/crash_report.mm index f68200c7..40b36252 100644 --- a/src/tools/mac/crash_report/crash_report.mm +++ b/src/tools/mac/crash_report/crash_report.mm @@ -271,10 +271,15 @@ static void ProcessSingleReport(Options *options, NSString *file_path) { int requesting_thread = process_state.requesting_thread(); if (requesting_thread != -1) { printf("\n"); - printf("Thread %d (%s)\n", + printf("Thread %d (%s)", requesting_thread, process_state.crashed() ? "crashed" : "requested dump, did not crash"); + string requesting_thread_name = process_state.thread_names()->at(requesting_thread); + if (!requesting_thread_name.empty()) { + printf(" (name: %s)", requesting_thread_name.c_str()); + } + printf("\n"); PrintStack(process_state.threads()->at(requesting_thread), cpu); } @@ -287,7 +292,12 @@ static void ProcessSingleReport(Options *options, NSString *file_path) { if (thread_index != requesting_thread) { // Don't print the crash thread again, it was already printed. printf("\n"); - printf("Thread %d\n", thread_index); + printf("Thread %d", thread_index); + string thread_name = process_state.thread_names()->at(thread_index); + if (!thread_name.empty()) { + printf(" (name: %s)", thread_name.c_str()); + } + printf("\n"); PrintStack(process_state.threads()->at(thread_index), cpu); google_breakpad::MemoryRegion *thread_stack_bytes = thread_memory_regions->at(thread_index);