mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2025-07-23 22:58:31 +00:00
[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:
parent
737e2cd338
commit
4d85225467
|
@ -239,6 +239,15 @@ typedef struct {
|
||||||
MDRVA rva;
|
MDRVA rva;
|
||||||
} MDLocationDescriptor; /* MINIDUMP_LOCATION_DESCRIPTOR */
|
} 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 {
|
typedef struct {
|
||||||
/* The base address of the memory range on the host that produced the
|
/* 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_JAVASCRIPT_DATA_STREAM = 20,
|
||||||
MD_SYSTEM_MEMORY_INFO_STREAM = 21,
|
MD_SYSTEM_MEMORY_INFO_STREAM = 21,
|
||||||
MD_PROCESS_VM_COUNTERS_STREAM = 22,
|
MD_PROCESS_VM_COUNTERS_STREAM = 22,
|
||||||
|
MD_THREAD_NAME_LIST_STREAM = 24, /* MDRawThreadNameList */
|
||||||
MD_LAST_RESERVED_STREAM = 0x0000ffff,
|
MD_LAST_RESERVED_STREAM = 0x0000ffff,
|
||||||
|
|
||||||
/* Breakpad extension types. 0x4767 = "Gg" */
|
/* Breakpad extension types. 0x4767 = "Gg" */
|
||||||
|
@ -382,6 +392,20 @@ typedef struct {
|
||||||
static const size_t MDRawThreadList_minsize = offsetof(MDRawThreadList,
|
static const size_t MDRawThreadList_minsize = offsetof(MDRawThreadList,
|
||||||
threads[0]);
|
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 {
|
typedef struct {
|
||||||
uint64_t base_of_image;
|
uint64_t base_of_image;
|
||||||
|
|
|
@ -370,6 +370,86 @@ class MinidumpThreadList : public MinidumpStream {
|
||||||
DISALLOW_COPY_AND_ASSIGN(MinidumpThreadList);
|
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<MinidumpThreadName> 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
|
// MinidumpModule wraps MDRawModule, which contains information about loaded
|
||||||
// code modules. Access is provided to various data referenced indirectly
|
// 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
|
// to avoid exposing an ugly API (GetStream needs to accept a garbage
|
||||||
// parameter).
|
// parameter).
|
||||||
virtual MinidumpThreadList* GetThreadList();
|
virtual MinidumpThreadList* GetThreadList();
|
||||||
|
virtual MinidumpThreadNameList* GetThreadNameList();
|
||||||
virtual MinidumpModuleList* GetModuleList();
|
virtual MinidumpModuleList* GetModuleList();
|
||||||
virtual MinidumpMemoryList* GetMemoryList();
|
virtual MinidumpMemoryList* GetMemoryList();
|
||||||
virtual MinidumpException* GetException();
|
virtual MinidumpException* GetException();
|
||||||
|
|
|
@ -56,9 +56,13 @@ enum ProcessResult {
|
||||||
PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than one
|
PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than one
|
||||||
// requesting thread.
|
// requesting thread.
|
||||||
|
|
||||||
PROCESS_SYMBOL_SUPPLIER_INTERRUPTED // The dump processing was
|
PROCESS_SYMBOL_SUPPLIER_INTERRUPTED, // The dump processing was
|
||||||
// interrupted by the
|
// interrupted by the
|
||||||
// SymbolSupplier(not fatal).
|
// SymbolSupplier(not fatal).
|
||||||
|
|
||||||
|
PROCESS_ERROR_GETTING_THREAD_NAME, // There was an error getting one
|
||||||
|
// thread's name from the dump.
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -111,6 +111,7 @@ class ProcessState {
|
||||||
const vector<MemoryRegion*>* thread_memory_regions() const {
|
const vector<MemoryRegion*>* thread_memory_regions() const {
|
||||||
return &thread_memory_regions_;
|
return &thread_memory_regions_;
|
||||||
}
|
}
|
||||||
|
const vector<string>* thread_names() const { return &thread_names_; }
|
||||||
const SystemInfo* system_info() const { return &system_info_; }
|
const SystemInfo* system_info() const { return &system_info_; }
|
||||||
const CodeModules* modules() const { return modules_; }
|
const CodeModules* modules() const { return modules_; }
|
||||||
const CodeModules* unloaded_modules() const { return unloaded_modules_; }
|
const CodeModules* unloaded_modules() const { return unloaded_modules_; }
|
||||||
|
@ -176,6 +177,12 @@ class ProcessState {
|
||||||
vector<CallStack*> threads_;
|
vector<CallStack*> threads_;
|
||||||
vector<MemoryRegion*> thread_memory_regions_;
|
vector<MemoryRegion*> 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<string> thread_names_;
|
||||||
|
|
||||||
// OS and CPU information.
|
// OS and CPU information.
|
||||||
SystemInfo system_info_;
|
SystemInfo system_info_;
|
||||||
|
|
||||||
|
|
|
@ -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
|
// MinidumpModule
|
||||||
|
@ -5280,6 +5503,7 @@ bool Minidump::Read() {
|
||||||
unsigned int stream_type = directory_entry->stream_type;
|
unsigned int stream_type = directory_entry->stream_type;
|
||||||
switch (stream_type) {
|
switch (stream_type) {
|
||||||
case MD_THREAD_LIST_STREAM:
|
case MD_THREAD_LIST_STREAM:
|
||||||
|
case MD_THREAD_NAME_LIST_STREAM:
|
||||||
case MD_MODULE_LIST_STREAM:
|
case MD_MODULE_LIST_STREAM:
|
||||||
case MD_MEMORY_LIST_STREAM:
|
case MD_MEMORY_LIST_STREAM:
|
||||||
case MD_EXCEPTION_STREAM:
|
case MD_EXCEPTION_STREAM:
|
||||||
|
@ -5318,6 +5542,10 @@ MinidumpThreadList* Minidump::GetThreadList() {
|
||||||
return GetStream(&thread_list);
|
return GetStream(&thread_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MinidumpThreadNameList* Minidump::GetThreadNameList() {
|
||||||
|
MinidumpThreadNameList* thread_name_list;
|
||||||
|
return GetStream(&thread_name_list);
|
||||||
|
}
|
||||||
|
|
||||||
MinidumpModuleList* Minidump::GetModuleList() {
|
MinidumpModuleList* Minidump::GetModuleList() {
|
||||||
MinidumpModuleList* module_list;
|
MinidumpModuleList* module_list;
|
||||||
|
@ -5417,6 +5645,8 @@ static const char* get_stream_name(uint32_t stream_type) {
|
||||||
return "MD_RESERVED_STREAM_1";
|
return "MD_RESERVED_STREAM_1";
|
||||||
case MD_THREAD_LIST_STREAM:
|
case MD_THREAD_LIST_STREAM:
|
||||||
return "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:
|
case MD_MODULE_LIST_STREAM:
|
||||||
return "MD_MODULE_LIST_STREAM";
|
return "MD_MODULE_LIST_STREAM";
|
||||||
case MD_MEMORY_LIST_STREAM:
|
case MD_MEMORY_LIST_STREAM:
|
||||||
|
|
|
@ -45,6 +45,7 @@ namespace {
|
||||||
|
|
||||||
using google_breakpad::Minidump;
|
using google_breakpad::Minidump;
|
||||||
using google_breakpad::MinidumpThreadList;
|
using google_breakpad::MinidumpThreadList;
|
||||||
|
using google_breakpad::MinidumpThreadNameList;
|
||||||
using google_breakpad::MinidumpModuleList;
|
using google_breakpad::MinidumpModuleList;
|
||||||
using google_breakpad::MinidumpMemoryInfoList;
|
using google_breakpad::MinidumpMemoryInfoList;
|
||||||
using google_breakpad::MinidumpMemoryList;
|
using google_breakpad::MinidumpMemoryList;
|
||||||
|
@ -122,6 +123,11 @@ static bool PrintMinidumpDump(const Options& options) {
|
||||||
thread_list->Print();
|
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
|
// It's useful to be able to see the full list of modules here even if it
|
||||||
// would cause minidump_stackwalk to fail.
|
// would cause minidump_stackwalk to fail.
|
||||||
MinidumpModuleList::set_max_modules(UINT32_MAX);
|
MinidumpModuleList::set_max_modules(UINT32_MAX);
|
||||||
|
|
|
@ -32,7 +32,9 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "common/scoped_ptr.h"
|
#include "common/scoped_ptr.h"
|
||||||
#include "common/stdio_wrapper.h"
|
#include "common/stdio_wrapper.h"
|
||||||
|
@ -199,6 +201,28 @@ ProcessResult MinidumpProcessor::Process(
|
||||||
// Reset frame_symbolizer_ at the beginning of stackwalk for each minidump.
|
// Reset frame_symbolizer_ at the beginning of stackwalk for each minidump.
|
||||||
frame_symbolizer_->Reset();
|
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;
|
for (unsigned int thread_index = 0;
|
||||||
thread_index < thread_count;
|
thread_index < thread_count;
|
||||||
++thread_index) {
|
++thread_index) {
|
||||||
|
@ -220,6 +244,14 @@ ProcessResult MinidumpProcessor::Process(
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_string += " id " + HexString(thread_id);
|
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;
|
BPLOG(INFO) << "Looking at thread " << thread_string;
|
||||||
|
|
||||||
// If this thread is the thread that produced the minidump, don't process
|
// 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);
|
stack->set_tid(thread_id);
|
||||||
process_state->threads_.push_back(stack.release());
|
process_state->threads_.push_back(stack.release());
|
||||||
process_state->thread_memory_regions_.push_back(thread_memory);
|
process_state->thread_memory_regions_.push_back(thread_memory);
|
||||||
|
process_state->thread_names_.push_back(thread_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interrupted) {
|
if (interrupted) {
|
||||||
|
|
|
@ -58,6 +58,7 @@ void ProcessState::Clear() {
|
||||||
}
|
}
|
||||||
threads_.clear();
|
threads_.clear();
|
||||||
system_info_.Clear();
|
system_info_.Clear();
|
||||||
|
thread_names_.clear();
|
||||||
// modules_without_symbols_ and modules_with_corrupt_symbols_ DO NOT own
|
// modules_without_symbols_ and modules_with_corrupt_symbols_ DO NOT own
|
||||||
// the underlying CodeModule pointers. Just clear the vectors.
|
// the underlying CodeModule pointers. Just clear the vectors.
|
||||||
modules_without_symbols_.clear();
|
modules_without_symbols_.clear();
|
||||||
|
|
BIN
src/processor/testdata/thread_name_list.dmp
vendored
Normal file
BIN
src/processor/testdata/thread_name_list.dmp
vendored
Normal file
Binary file not shown.
|
@ -271,10 +271,15 @@ static void ProcessSingleReport(Options *options, NSString *file_path) {
|
||||||
int requesting_thread = process_state.requesting_thread();
|
int requesting_thread = process_state.requesting_thread();
|
||||||
if (requesting_thread != -1) {
|
if (requesting_thread != -1) {
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Thread %d (%s)\n",
|
printf("Thread %d (%s)",
|
||||||
requesting_thread,
|
requesting_thread,
|
||||||
process_state.crashed() ? "crashed" :
|
process_state.crashed() ? "crashed" :
|
||||||
"requested dump, did not crash");
|
"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);
|
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) {
|
if (thread_index != requesting_thread) {
|
||||||
// Don't print the crash thread again, it was already printed.
|
// Don't print the crash thread again, it was already printed.
|
||||||
printf("\n");
|
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);
|
PrintStack(process_state.threads()->at(thread_index), cpu);
|
||||||
google_breakpad::MemoryRegion *thread_stack_bytes =
|
google_breakpad::MemoryRegion *thread_stack_bytes =
|
||||||
thread_memory_regions->at(thread_index);
|
thread_memory_regions->at(thread_index);
|
||||||
|
|
Loading…
Reference in a new issue