[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 <jperaza@chromium.org>
Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
This commit is contained in:
Ben Hamilton 2022-06-06 19:01:15 -06:00 committed by Joshua Peraza
parent 737e2cd338
commit 4d85225467
10 changed files with 399 additions and 3 deletions

View file

@ -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<off_t>::max()) {
BPLOG(ERROR) << "MinidumpThreadName RVA64 out of range";
return false;
}
// Read the thread name.
const off_t thread_name_rva_offset =
static_cast<off_t>(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<uint32_t>::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<MinidumpThreadNames> 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:

View file

@ -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);

View file

@ -32,7 +32,9 @@
#include <assert.h>
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#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<uint32_t, string> 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) {

View file

@ -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();

Binary file not shown.