661 lines
21 KiB
Diff
661 lines
21 KiB
Diff
diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h
|
|
--- a/src/google_breakpad/common/minidump_format.h
|
|
+++ b/src/google_breakpad/common/minidump_format.h
|
|
@@ -227,17 +227,18 @@ typedef struct {
|
|
|
|
/*
|
|
* DbgHelp.h
|
|
*/
|
|
|
|
|
|
/* An MDRVA is an offset into the minidump file. The beginning of the
|
|
* MDRawHeader is at offset 0. */
|
|
-typedef uint32_t MDRVA; /* RVA */
|
|
+typedef uint32_t MDRVA; /* RVA */
|
|
+typedef uint64_t MDRVA64; /* RVA64 */
|
|
|
|
typedef struct {
|
|
uint32_t data_size;
|
|
MDRVA rva;
|
|
} MDLocationDescriptor; /* MINIDUMP_LOCATION_DESCRIPTOR */
|
|
|
|
|
|
typedef struct {
|
|
@@ -327,16 +328,18 @@ typedef enum {
|
|
MD_MISC_INFO_STREAM = 15, /* MDRawMiscInfo */
|
|
MD_MEMORY_INFO_LIST_STREAM = 16, /* MDRawMemoryInfoList */
|
|
MD_THREAD_INFO_LIST_STREAM = 17,
|
|
MD_HANDLE_OPERATION_LIST_STREAM = 18,
|
|
MD_TOKEN_STREAM = 19,
|
|
MD_JAVASCRIPT_DATA_STREAM = 20,
|
|
MD_SYSTEM_MEMORY_INFO_STREAM = 21,
|
|
MD_PROCESS_VM_COUNTERS_STREAM = 22,
|
|
+ MD_IPT_TRACE_STREAM = 23,
|
|
+ MD_THREAD_NAMES_STREAM = 24,
|
|
MD_LAST_RESERVED_STREAM = 0x0000ffff,
|
|
|
|
/* Breakpad extension types. 0x4767 = "Gg" */
|
|
MD_BREAKPAD_INFO_STREAM = 0x47670001, /* MDRawBreakpadInfo */
|
|
MD_ASSERTION_INFO_STREAM = 0x47670002, /* MDRawAssertionInfo */
|
|
/* These are additional minidump stream values which are specific to
|
|
* the linux breakpad implementation. */
|
|
MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */
|
|
@@ -1117,16 +1120,26 @@ typedef struct {
|
|
* module_path;
|
|
* message;
|
|
* signature_string;
|
|
* backtrace;
|
|
* message2; */
|
|
uint8_t data[0];
|
|
} MDRawMacCrashInfoRecord;
|
|
|
|
+typedef struct __attribute__((packed,aligned(4))) {
|
|
+ uint32_t thread_id;
|
|
+ MDRVA64 rva_of_thread_name;
|
|
+} MDRawThreadName;
|
|
+
|
|
+typedef struct {
|
|
+ uint32_t number_of_thread_names;
|
|
+ MDRawThreadName thread_names[0];
|
|
+} MDRawThreadNamesList;
|
|
+
|
|
/* This is the maximum supported size for each string in
|
|
* (MDRawMacCrashInfoRecord).data. If we encounter a string in the
|
|
* __crash_info section which seems larger than this, that's a sign of data
|
|
* corruption. */
|
|
#define MACCRASHINFO_STRING_MAXSIZE 8192
|
|
|
|
/* In principle there should only be one or two non-empty __DATA,__crash_info
|
|
* sections per process. But the __crash_info section is almost entirely
|
|
diff --git a/src/google_breakpad/processor/call_stack.h b/src/google_breakpad/processor/call_stack.h
|
|
--- a/src/google_breakpad/processor/call_stack.h
|
|
+++ b/src/google_breakpad/processor/call_stack.h
|
|
@@ -41,21 +41,23 @@
|
|
// beginning with the innermost callee frame.
|
|
//
|
|
// Author: Mark Mentovai
|
|
|
|
#ifndef GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__
|
|
#define GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__
|
|
|
|
#include <cstdint>
|
|
+#include <string>
|
|
#include <vector>
|
|
|
|
namespace google_breakpad {
|
|
|
|
using std::vector;
|
|
+using std::string;
|
|
|
|
struct StackFrame;
|
|
template<typename T> class linked_ptr;
|
|
|
|
class CallStack {
|
|
public:
|
|
CallStack() { Clear(); }
|
|
~CallStack();
|
|
@@ -63,29 +65,33 @@ class CallStack {
|
|
// Resets the CallStack to its initial empty state
|
|
void Clear();
|
|
|
|
const vector<StackFrame*>* frames() const { return &frames_; }
|
|
|
|
// Set the TID associated with this call stack.
|
|
void set_tid(uint32_t tid) { tid_ = tid; }
|
|
void set_last_error(uint32_t last_error) { last_error_ = last_error; }
|
|
+ void set_name(const string& name) { name_ = name; }
|
|
|
|
uint32_t tid() const { return tid_; }
|
|
uint32_t last_error() const { return last_error_; }
|
|
+ const string name() const { return name_; }
|
|
|
|
private:
|
|
// Stackwalker is responsible for building the frames_ vector.
|
|
friend class Stackwalker;
|
|
|
|
// Storage for pushed frames.
|
|
vector<StackFrame*> frames_;
|
|
|
|
// The TID associated with this call stack. Default to 0 if it's not
|
|
// available.
|
|
uint32_t tid_;
|
|
// The last error the OS set for this thread (win32's GetLastError())
|
|
uint32_t last_error_;
|
|
+ // The name of this thread, empty if it's not available
|
|
+ string name_;
|
|
};
|
|
|
|
} // namespace google_breakpad
|
|
|
|
#endif // GOOGLE_BREAKPAD_PROCSSOR_CALL_STACK_H__
|
|
diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h
|
|
--- a/src/google_breakpad/processor/minidump.h
|
|
+++ b/src/google_breakpad/processor/minidump.h
|
|
@@ -1197,16 +1197,96 @@ class MinidumpMacCrashInfo : public Mini
|
|
bool ReadCrashInfoRecord(MDLocationDescriptor location,
|
|
uint32_t record_start_size);
|
|
bool Read(uint32_t expected_size);
|
|
|
|
string description_;
|
|
vector<crash_info_record_t> records_;
|
|
};
|
|
|
|
+// MinidumpThreadName wraps MDRawThreadName
|
|
+class MinidumpThreadName : public MinidumpObject {
|
|
+ public:
|
|
+ ~MinidumpThreadName() override;
|
|
+
|
|
+ const MDRawThreadName* thread_name() const {
|
|
+ if (valid_) {
|
|
+ return &thread_name_;
|
|
+ }
|
|
+
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ uint32_t thread_id() const {
|
|
+ if (valid_) {
|
|
+ return thread_name_.thread_id;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ string name() const;
|
|
+
|
|
+ // Print a human-readable representation of the object to stdout.
|
|
+ void Print();
|
|
+
|
|
+ protected:
|
|
+ explicit MinidumpThreadName(Minidump* minidump);
|
|
+
|
|
+ private:
|
|
+ // These objects are managed by MinidumpThreadNameList
|
|
+ friend class MinidumpThreadNamesList;
|
|
+
|
|
+ // This works like MinidumpStream::Read, but is driven by
|
|
+ // MinidumpThreadNameList.
|
|
+ bool Read(uint32_t expected_size);
|
|
+
|
|
+ // Reads the thread name. This is done separately from Read to
|
|
+ // allow contiguous reading of thread names by MinidumpThreadNameList.
|
|
+ bool ReadAuxiliaryData();
|
|
+
|
|
+ bool valid_;
|
|
+ MDRawThreadName thread_name_;
|
|
+ const string* name_;
|
|
+};
|
|
+
|
|
+
|
|
+// MinidumpThreadNamesList contains all the names for threads in a process
|
|
+// in the form of MinidumpThreadNames.
|
|
+class MinidumpThreadNamesList : public MinidumpStream {
|
|
+ public:
|
|
+ ~MinidumpThreadNamesList() override;
|
|
+
|
|
+ unsigned int name_count() const {
|
|
+ return valid_ ? name_count_ : 0;
|
|
+ }
|
|
+
|
|
+ const string GetNameForThreadId(uint32_t thread_id) const;
|
|
+
|
|
+ // Print a human-readable representation of the object to stdout.
|
|
+ void Print();
|
|
+
|
|
+ protected:
|
|
+ explicit MinidumpThreadNamesList(Minidump* minidump_);
|
|
+
|
|
+ private:
|
|
+ friend class Minidump;
|
|
+
|
|
+ typedef vector<MinidumpThreadName> MinidumpThreadNames;
|
|
+
|
|
+ static const uint32_t kStreamType = MD_THREAD_NAMES_STREAM;
|
|
+
|
|
+ bool Read(uint32_t expected_size_) override;
|
|
+
|
|
+ MinidumpThreadNames* thread_names_;
|
|
+ uint32_t name_count_;
|
|
+ bool valid_;
|
|
+
|
|
+ DISALLOW_COPY_AND_ASSIGN(MinidumpThreadNamesList);
|
|
+};
|
|
|
|
// Minidump is the user's interface to a minidump file. It wraps MDRawHeader
|
|
// and provides access to the minidump's top-level stream directory.
|
|
class Minidump {
|
|
public:
|
|
// path is the pathname of a file containing the minidump.
|
|
explicit Minidump(const string& path,
|
|
bool hexdump=false,
|
|
@@ -1261,16 +1343,17 @@ class Minidump {
|
|
virtual MinidumpAssertion* GetAssertion();
|
|
virtual MinidumpSystemInfo* GetSystemInfo();
|
|
virtual MinidumpUnloadedModuleList* GetUnloadedModuleList();
|
|
virtual MinidumpMiscInfo* GetMiscInfo();
|
|
virtual MinidumpBreakpadInfo* GetBreakpadInfo();
|
|
virtual MinidumpMemoryInfoList* GetMemoryInfoList();
|
|
MinidumpCrashpadInfo* GetCrashpadInfo();
|
|
MinidumpMacCrashInfo* GetMacCrashInfo();
|
|
+ MinidumpThreadNamesList* GetThreadNamesList();
|
|
|
|
// The next method also calls GetStream, but is exclusive for Linux dumps.
|
|
virtual MinidumpLinuxMapsList *GetLinuxMapsList();
|
|
|
|
// The next set of methods are provided for users who wish to access
|
|
// data in minidump files directly, while leveraging the rest of
|
|
// this class and related classes to handle the basic minidump
|
|
// structure and known stream types.
|
|
diff --git a/src/processor/call_stack.cc b/src/processor/call_stack.cc
|
|
--- a/src/processor/call_stack.cc
|
|
+++ b/src/processor/call_stack.cc
|
|
@@ -45,11 +45,12 @@ CallStack::~CallStack() {
|
|
void CallStack::Clear() {
|
|
for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
|
|
iterator != frames_.end();
|
|
++iterator) {
|
|
delete *iterator;
|
|
}
|
|
tid_ = 0;
|
|
last_error_ = 0;
|
|
+ name_ = "";
|
|
}
|
|
|
|
} // namespace google_breakpad
|
|
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
|
|
--- a/src/processor/minidump.cc
|
|
+++ b/src/processor/minidump.cc
|
|
@@ -5724,16 +5724,226 @@ MinidumpCrashpadInfo* Minidump::GetCrash
|
|
return GetStream(&crashpad_info);
|
|
}
|
|
|
|
MinidumpMacCrashInfo* Minidump::GetMacCrashInfo() {
|
|
MinidumpMacCrashInfo* mac_crash_info;
|
|
return GetStream(&mac_crash_info);
|
|
}
|
|
|
|
+MinidumpThreadNamesList* Minidump::GetThreadNamesList() {
|
|
+ MinidumpThreadNamesList* thread_names_list;
|
|
+ return GetStream(&thread_names_list);
|
|
+}
|
|
+
|
|
+//
|
|
+// MinidumpThreadName
|
|
+//
|
|
+
|
|
+
|
|
+MinidumpThreadName::MinidumpThreadName(Minidump* minidump)
|
|
+ : MinidumpObject(minidump),
|
|
+ valid_(false),
|
|
+ thread_name_(),
|
|
+ name_(NULL) {
|
|
+
|
|
+}
|
|
+
|
|
+MinidumpThreadName::~MinidumpThreadName() {
|
|
+ ;
|
|
+}
|
|
+
|
|
+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(" rva_of_thread_name = 0x%" PRIx64 "\n",
|
|
+ thread_name_.rva_of_thread_name);
|
|
+
|
|
+ printf(" (name) = \"%s\"\n", name().c_str());
|
|
+ printf("\n");
|
|
+}
|
|
+
|
|
+string MinidumpThreadName::name() const {
|
|
+ if (!valid_) {
|
|
+ BPLOG(ERROR) << "Invalid MinidumpThreadName for name";
|
|
+ return "";
|
|
+ }
|
|
+
|
|
+ return *name_;
|
|
+}
|
|
+
|
|
+bool MinidumpThreadName::Read(uint32_t expected_size) {
|
|
+
|
|
+ delete name_;
|
|
+
|
|
+ if (expected_size < sizeof(thread_name_)) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadName expected size is less than size "
|
|
+ << "of struct " << expected_size << " < "
|
|
+ << sizeof(thread_name_);
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (!minidump_->ReadBytes(&thread_name_, sizeof(thread_name_))) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadName cannot read name";
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (expected_size > sizeof(thread_name_)) {
|
|
+ uint32_t thread_name_bytes_remaining = expected_size - sizeof(thread_name_);
|
|
+ off_t pos = minidump_->Tell();
|
|
+ if (!minidump_->SeekSet(pos + thread_name_bytes_remaining)) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadName unable to seek to end of name";
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (minidump_->swap()) {
|
|
+ Swap(&thread_name_.thread_id);
|
|
+ uint64_t rva_of_thread_name;
|
|
+ memcpy(&rva_of_thread_name, &thread_name_.rva_of_thread_name, sizeof(uint64_t));
|
|
+ Swap(&rva_of_thread_name);
|
|
+ memcpy(&thread_name_.rva_of_thread_name, &rva_of_thread_name, sizeof(uint64_t));
|
|
+ }
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+bool MinidumpThreadName::ReadAuxiliaryData() {
|
|
+ // Each thread must have a name string.
|
|
+ name_ = minidump_->ReadString(thread_name_.rva_of_thread_name);
|
|
+ if (!name_) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadName could not read name";
|
|
+ valid_ = false;
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ // At this point, we have enough info for the name to be valid.
|
|
+ valid_ = true;
|
|
+ return true;
|
|
+}
|
|
+
|
|
+//
|
|
+// MinidumpThreadNamesList
|
|
+//
|
|
+
|
|
+
|
|
+MinidumpThreadNamesList::MinidumpThreadNamesList(Minidump* minidump)
|
|
+ : MinidumpStream(minidump),
|
|
+ thread_names_(NULL),
|
|
+ name_count_(0),
|
|
+ valid_(false) {
|
|
+ ;
|
|
+}
|
|
+
|
|
+MinidumpThreadNamesList::~MinidumpThreadNamesList() {
|
|
+ delete thread_names_;
|
|
+}
|
|
+
|
|
+const string MinidumpThreadNamesList::GetNameForThreadId(uint32_t thread_id) const {
|
|
+ if (valid_) {
|
|
+ for (unsigned int name_index = 0;
|
|
+ name_index < name_count_;
|
|
+ ++name_index) {
|
|
+ const MinidumpThreadName& thread_name = (*thread_names_)[name_index];
|
|
+ if (thread_name.thread_id() == thread_id) {
|
|
+ return thread_name.name();
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return "";
|
|
+}
|
|
+
|
|
+void MinidumpThreadNamesList::Print() {
|
|
+ if (!valid_) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadNamesList cannot print invalid data";
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ printf("MinidumpThreadNamesList\n");
|
|
+ printf(" name_count = %d\n", name_count_);
|
|
+ printf("\n");
|
|
+
|
|
+ for (unsigned int name_index = 0;
|
|
+ name_index < name_count_;
|
|
+ ++name_index) {
|
|
+ printf("thread_name[%d]\n", name_index);
|
|
+
|
|
+ (*thread_names_)[name_index].Print();
|
|
+ }
|
|
+}
|
|
+
|
|
+bool MinidumpThreadNamesList::Read(uint32_t expected_size) {
|
|
+ delete thread_names_;
|
|
+ thread_names_ = NULL;
|
|
+ name_count_ = 0;
|
|
+
|
|
+ valid_ = false;
|
|
+
|
|
+ uint32_t number_of_thread_names;
|
|
+ if (!minidump_->ReadBytes(&number_of_thread_names, sizeof(number_of_thread_names))) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadNamesList could not read the number of thread names";
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (minidump_->swap()) {
|
|
+ Swap(&number_of_thread_names);
|
|
+ }
|
|
+
|
|
+ if (expected_size !=
|
|
+ sizeof(number_of_thread_names) + (sizeof(MDRawThreadName) * number_of_thread_names)) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadNamesList expected_size mismatch " <<
|
|
+ expected_size << " != " << sizeof(number_of_thread_names) << " + (" <<
|
|
+ sizeof(MDRawThreadName) << " * " << number_of_thread_names << ")";
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (number_of_thread_names != 0) {
|
|
+ scoped_ptr<MinidumpThreadNames> thread_names(
|
|
+ new MinidumpThreadNames(number_of_thread_names,
|
|
+ MinidumpThreadName(minidump_)));
|
|
+
|
|
+ for (unsigned int name_index = 0;
|
|
+ name_index < number_of_thread_names;
|
|
+ ++name_index) {
|
|
+ MinidumpThreadName* thread_name = &(*thread_names)[name_index];
|
|
+
|
|
+ if (!thread_name->Read(sizeof(MDRawThreadName))) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadNamesList could not read name " <<
|
|
+ name_index << "/" << number_of_thread_names;
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ for (unsigned int name_index = 0;
|
|
+ name_index < number_of_thread_names;
|
|
+ ++name_index) {
|
|
+ MinidumpThreadName* thread_name = &(*thread_names)[name_index];
|
|
+
|
|
+ if (!thread_name->ReadAuxiliaryData()) {
|
|
+ BPLOG(ERROR) << "MinidumpThreadNamesList could not read required "
|
|
+ "auxiliary data for thread name " <<
|
|
+ name_index << "/" << number_of_thread_names;
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+ thread_names_ = thread_names.release();
|
|
+ }
|
|
+
|
|
+ name_count_ = number_of_thread_names;
|
|
+ valid_ = true;
|
|
+ return true;
|
|
+}
|
|
+
|
|
static const char* get_stream_name(uint32_t stream_type) {
|
|
switch (stream_type) {
|
|
case MD_UNUSED_STREAM:
|
|
return "MD_UNUSED_STREAM";
|
|
case MD_RESERVED_STREAM_0:
|
|
return "MD_RESERVED_STREAM_0";
|
|
case MD_RESERVED_STREAM_1:
|
|
return "MD_RESERVED_STREAM_1";
|
|
@@ -5772,16 +5982,20 @@ static const char* get_stream_name(uint3
|
|
case MD_TOKEN_STREAM:
|
|
return "MD_TOKEN_STREAM";
|
|
case MD_JAVASCRIPT_DATA_STREAM:
|
|
return "MD_JAVASCRIPT_DATA_STREAM";
|
|
case MD_SYSTEM_MEMORY_INFO_STREAM:
|
|
return "MD_SYSTEM_MEMORY_INFO_STREAM";
|
|
case MD_PROCESS_VM_COUNTERS_STREAM:
|
|
return "MD_PROCESS_VM_COUNTERS_STREAM";
|
|
+ case MD_IPT_TRACE_STREAM:
|
|
+ return "MD_IPT_TRACE_STREAM";
|
|
+ case MD_THREAD_NAMES_STREAM:
|
|
+ return "MD_THREAD_NAMES_STREAM";
|
|
case MD_LAST_RESERVED_STREAM:
|
|
return "MD_LAST_RESERVED_STREAM";
|
|
case MD_BREAKPAD_INFO_STREAM:
|
|
return "MD_BREAKPAD_INFO_STREAM";
|
|
case MD_ASSERTION_INFO_STREAM:
|
|
return "MD_ASSERTION_INFO_STREAM";
|
|
case MD_LINUX_CPU_INFO:
|
|
return "MD_LINUX_CPU_INFO";
|
|
diff --git a/src/processor/minidump_dump.cc b/src/processor/minidump_dump.cc
|
|
--- a/src/processor/minidump_dump.cc
|
|
+++ b/src/processor/minidump_dump.cc
|
|
@@ -49,16 +49,17 @@ using google_breakpad::MinidumpUnloadedM
|
|
using google_breakpad::MinidumpMemoryInfoList;
|
|
using google_breakpad::MinidumpMemoryList;
|
|
using google_breakpad::MinidumpException;
|
|
using google_breakpad::MinidumpAssertion;
|
|
using google_breakpad::MinidumpSystemInfo;
|
|
using google_breakpad::MinidumpMiscInfo;
|
|
using google_breakpad::MinidumpBreakpadInfo;
|
|
using google_breakpad::MinidumpCrashpadInfo;
|
|
+using google_breakpad::MinidumpThreadNamesList;
|
|
|
|
struct Options {
|
|
Options()
|
|
: minidumpPath(), hexdump(false), hexdump_width(16) {}
|
|
|
|
string minidumpPath;
|
|
bool hexdump;
|
|
unsigned int hexdump_width;
|
|
@@ -197,16 +198,21 @@ static bool PrintMinidumpDump(const Opti
|
|
}
|
|
|
|
MinidumpCrashpadInfo *crashpad_info = minidump.GetCrashpadInfo();
|
|
if (crashpad_info) {
|
|
// Crashpad info is optional, so don't treat absence as an error.
|
|
crashpad_info->Print();
|
|
}
|
|
|
|
+ MinidumpThreadNamesList *thread_names_list = minidump.GetThreadNamesList();
|
|
+ if (thread_names_list) {
|
|
+ thread_names_list->Print();
|
|
+ }
|
|
+
|
|
DumpRawStream(&minidump,
|
|
MD_LINUX_CMD_LINE,
|
|
"MD_LINUX_CMD_LINE",
|
|
&errors);
|
|
DumpRawStream(&minidump,
|
|
MD_LINUX_ENVIRON,
|
|
"MD_LINUX_ENVIRON",
|
|
&errors);
|
|
diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
|
|
--- a/src/processor/minidump_processor.cc
|
|
+++ b/src/processor/minidump_processor.cc
|
|
@@ -173,16 +173,22 @@ ProcessResult MinidumpProcessor::Process
|
|
}
|
|
|
|
MinidumpMemoryList *memory_list = dump->GetMemoryList();
|
|
if (memory_list) {
|
|
BPLOG(INFO) << "Found " << memory_list->region_count()
|
|
<< " memory regions.";
|
|
}
|
|
|
|
+ MinidumpThreadNamesList* thread_names_list = dump->GetThreadNamesList();
|
|
+ if (thread_names_list) {
|
|
+ BPLOG(INFO) << "Found " << thread_names_list->name_count()
|
|
+ << " thread names.";
|
|
+ }
|
|
+
|
|
MinidumpThreadList *threads = dump->GetThreadList();
|
|
if (!threads) {
|
|
BPLOG(ERROR) << "Minidump " << dump->path() << " has no thread list";
|
|
return PROCESS_ERROR_NO_THREAD_LIST;
|
|
}
|
|
|
|
BPLOG(INFO) << "Minidump " << dump->path() << " has " <<
|
|
(has_cpu_info ? "" : "no ") << "CPU info, " <<
|
|
@@ -308,16 +314,19 @@ ProcessResult MinidumpProcessor::Process
|
|
} else {
|
|
// Threads with missing CPU contexts will hit this, but
|
|
// don't abort processing the rest of the dump just for
|
|
// one bad thread.
|
|
BPLOG(ERROR) << "No stackwalker for " << thread_string;
|
|
}
|
|
stack->set_tid(thread_id);
|
|
stack->set_last_error(thread->GetLastError());
|
|
+ if (thread_names_list) {
|
|
+ stack->set_name(thread_names_list->GetNameForThreadId(thread_id));
|
|
+ }
|
|
process_state->threads_.push_back(stack.release());
|
|
process_state->thread_memory_regions_.push_back(thread_memory);
|
|
}
|
|
|
|
if (interrupted) {
|
|
BPLOG(INFO) << "Processing interrupted for " << dump->path();
|
|
return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED;
|
|
}
|
|
diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc
|
|
--- a/src/processor/stackwalk_common.cc
|
|
+++ b/src/processor/stackwalk_common.cc
|
|
@@ -876,35 +876,45 @@ void PrintProcessState(const ProcessStat
|
|
printf("\n");
|
|
printf("Application-specific information:\n");
|
|
printf("%s", process_state.mac_crash_info().c_str());
|
|
}
|
|
|
|
// If the thread that requested the dump is known, print it first.
|
|
int requesting_thread = process_state.requesting_thread();
|
|
if (requesting_thread != -1) {
|
|
- printf("\n");
|
|
- printf("Thread %d (%s)\n",
|
|
- requesting_thread,
|
|
- process_state.crashed() ? "crashed" :
|
|
- "requested dump, did not crash");
|
|
- PrintStack(process_state.threads()->at(requesting_thread), cpu,
|
|
+ const CallStack* requesting_thread_callstack =
|
|
+ process_state.threads()->at(requesting_thread);
|
|
+ printf("\n"
|
|
+ "Thread %d (%s)",
|
|
+ requesting_thread,
|
|
+ process_state.crashed() ? "crashed" :
|
|
+ "requested dump, did not crash");
|
|
+ if (!requesting_thread_callstack->name().empty()) {
|
|
+ printf(" - %s", requesting_thread_callstack->name().c_str());
|
|
+ }
|
|
+ PrintStack(requesting_thread_callstack, cpu,
|
|
output_stack_contents,
|
|
process_state.thread_memory_regions()->at(requesting_thread),
|
|
process_state.modules(), resolver);
|
|
}
|
|
|
|
// Print all of the threads in the dump.
|
|
int thread_count = process_state.threads()->size();
|
|
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
|
|
if (thread_index != requesting_thread) {
|
|
// Don't print the crash thread again, it was already printed.
|
|
+ const CallStack* callstack = process_state.threads()->at(thread_index);
|
|
+ printf("\n"
|
|
+ "Thread %d", thread_index);
|
|
+ if (!callstack->name().empty()) {
|
|
+ printf(" - %s", callstack->name().c_str());
|
|
+ }
|
|
printf("\n");
|
|
- printf("Thread %d\n", thread_index);
|
|
- PrintStack(process_state.threads()->at(thread_index), cpu,
|
|
+ PrintStack(callstack, cpu,
|
|
output_stack_contents,
|
|
process_state.thread_memory_regions()->at(thread_index),
|
|
process_state.modules(), resolver);
|
|
}
|
|
}
|
|
|
|
PrintModules(process_state.modules(),
|
|
process_state.modules_without_symbols(),
|