Suppress handler thread from appearing in MinidumpProcessor's ProcessState
(#65). r=bryner - Interface change: (ProcessState).crash_thread is now requesting_thread and will be populated for non-crash dumps. If the requesting thread cannot be determined, requesting_thread is set to -1. http://groups.google.com/group/airbag-dev/browse_thread/thread/c422ec481a2db440 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@62 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
5ac32b6534
commit
76f052f8fb
16 changed files with 22370 additions and 15417 deletions
|
@ -280,8 +280,9 @@ class MinidumpThread : public MinidumpObject {
|
||||||
|
|
||||||
// The thread ID is used to determine if a thread is the exception thread,
|
// The thread ID is used to determine if a thread is the exception thread,
|
||||||
// so a special getter is provided to retrieve this data from the
|
// so a special getter is provided to retrieve this data from the
|
||||||
// MDRawThread structure.
|
// MDRawThread structure. Returns false if the thread ID cannot be
|
||||||
u_int32_t GetThreadID();
|
// determined.
|
||||||
|
bool GetThreadID(u_int32_t *thread_id) const;
|
||||||
|
|
||||||
// Print a human-readable representation of the object to stdout.
|
// Print a human-readable representation of the object to stdout.
|
||||||
void Print();
|
void Print();
|
||||||
|
@ -349,9 +350,10 @@ class MinidumpModule : public MinidumpObject {
|
||||||
public:
|
public:
|
||||||
~MinidumpModule();
|
~MinidumpModule();
|
||||||
|
|
||||||
const MDRawModule* module() const { return valid_ ? &module_ : 0; }
|
const MDRawModule* module() const { return valid_ ? &module_ : NULL; }
|
||||||
u_int64_t base_address() const {
|
u_int64_t base_address() const {
|
||||||
return valid_ ? module_.base_of_image : static_cast<u_int64_t>(-1); }
|
return valid_ ? module_.base_of_image : static_cast<u_int64_t>(-1);
|
||||||
|
}
|
||||||
u_int32_t size() const { return valid_ ? module_.size_of_image : 0; }
|
u_int32_t size() const { return valid_ ? module_.size_of_image : 0; }
|
||||||
|
|
||||||
// The name of the file containing this module's code (exe, dll, so,
|
// The name of the file containing this module's code (exe, dll, so,
|
||||||
|
@ -513,12 +515,14 @@ class MinidumpException : public MinidumpStream {
|
||||||
~MinidumpException();
|
~MinidumpException();
|
||||||
|
|
||||||
const MDRawExceptionStream* exception() const {
|
const MDRawExceptionStream* exception() const {
|
||||||
return valid_ ? &exception_ : 0; }
|
return valid_ ? &exception_ : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// The thread ID is used to determine if a thread is the exception thread,
|
// The thread ID is used to determine if a thread is the exception thread,
|
||||||
// so a special getter is provided to retrieve this data from the
|
// so a special getter is provided to retrieve this data from the
|
||||||
// MDRawExceptionStream structure.
|
// MDRawExceptionStream structure. Returns false if the thread ID cannot
|
||||||
u_int32_t GetThreadID();
|
// be determined.
|
||||||
|
bool GetThreadID(u_int32_t *thread_id) const;
|
||||||
|
|
||||||
MinidumpContext* GetContext();
|
MinidumpContext* GetContext();
|
||||||
|
|
||||||
|
@ -546,7 +550,8 @@ class MinidumpSystemInfo : public MinidumpStream {
|
||||||
~MinidumpSystemInfo();
|
~MinidumpSystemInfo();
|
||||||
|
|
||||||
const MDRawSystemInfo* system_info() const {
|
const MDRawSystemInfo* system_info() const {
|
||||||
return valid_ ? &system_info_ : 0; }
|
return valid_ ? &system_info_ : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// I don't know what CSD stands for, but this field is documented as
|
// I don't know what CSD stands for, but this field is documented as
|
||||||
// returning a textual representation of the OS service pack. On other
|
// returning a textual representation of the OS service pack. On other
|
||||||
|
@ -587,7 +592,9 @@ class MinidumpSystemInfo : public MinidumpStream {
|
||||||
// information. See also MinidumpSystemInfo.
|
// information. See also MinidumpSystemInfo.
|
||||||
class MinidumpMiscInfo : public MinidumpStream {
|
class MinidumpMiscInfo : public MinidumpStream {
|
||||||
public:
|
public:
|
||||||
const MDRawMiscInfo* misc_info() const { return valid_ ? &misc_info_ : 0; }
|
const MDRawMiscInfo* misc_info() const {
|
||||||
|
return valid_ ? &misc_info_ : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Print a human-readable representation of the object to stdout.
|
// Print a human-readable representation of the object to stdout.
|
||||||
void Print();
|
void Print();
|
||||||
|
@ -605,6 +612,38 @@ class MinidumpMiscInfo : public MinidumpStream {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// MinidumpAirbagInfo wraps MDRawAirbagInfo, which is an optional stream in
|
||||||
|
// a minidump that provides additional information about the process state
|
||||||
|
// at the time the minidump was generated.
|
||||||
|
class MinidumpAirbagInfo : public MinidumpStream {
|
||||||
|
public:
|
||||||
|
const MDRawAirbagInfo* airbag_info() const {
|
||||||
|
return valid_ ? &airbag_info_ : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These thread IDs are used to determine if threads deserve special
|
||||||
|
// treatment, so special getters are provided to retrieve this data from
|
||||||
|
// the MDRawAirbagInfo structure. The getters return false if the thread
|
||||||
|
// IDs cannot be determined.
|
||||||
|
bool GetDumpThreadID(u_int32_t *thread_id) const;
|
||||||
|
bool GetRequestingThreadID(u_int32_t *thread_id) const;
|
||||||
|
|
||||||
|
// Print a human-readable representation of the object to stdout.
|
||||||
|
void Print();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class Minidump;
|
||||||
|
|
||||||
|
static const u_int32_t kStreamType = MD_AIRBAG_INFO_STREAM;
|
||||||
|
|
||||||
|
explicit MinidumpAirbagInfo(Minidump* minidump_);
|
||||||
|
|
||||||
|
bool Read(u_int32_t expected_size_);
|
||||||
|
|
||||||
|
MDRawAirbagInfo airbag_info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Minidump is the user's interface to a minidump file. It wraps MDRawHeader
|
// Minidump is the user's interface to a minidump file. It wraps MDRawHeader
|
||||||
// and provides access to the minidump's top-level stream directory.
|
// and provides access to the minidump's top-level stream directory.
|
||||||
class Minidump {
|
class Minidump {
|
||||||
|
@ -614,7 +653,7 @@ class Minidump {
|
||||||
|
|
||||||
~Minidump();
|
~Minidump();
|
||||||
|
|
||||||
const MDRawHeader* header() const { return valid_ ? &header_ : 0; }
|
const MDRawHeader* header() const { return valid_ ? &header_ : NULL; }
|
||||||
|
|
||||||
// Reads the minidump file's header and top-level stream directory.
|
// Reads the minidump file's header and top-level stream directory.
|
||||||
// The minidump is expected to be positioned at the beginning of the
|
// The minidump is expected to be positioned at the beginning of the
|
||||||
|
@ -622,7 +661,7 @@ class Minidump {
|
||||||
// Minidump object.
|
// Minidump object.
|
||||||
bool Read();
|
bool Read();
|
||||||
|
|
||||||
// The next 6 methods are stubs that call GetStream. They exist to
|
// The next set of methods are stubs that call GetStream. They exist to
|
||||||
// force code generation of the templatized API within the module, and
|
// force code generation of the templatized API within the module, and
|
||||||
// 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).
|
||||||
|
@ -632,6 +671,7 @@ class Minidump {
|
||||||
MinidumpException* GetException();
|
MinidumpException* GetException();
|
||||||
MinidumpSystemInfo* GetSystemInfo();
|
MinidumpSystemInfo* GetSystemInfo();
|
||||||
MinidumpMiscInfo* GetMiscInfo();
|
MinidumpMiscInfo* GetMiscInfo();
|
||||||
|
MinidumpAirbagInfo* GetAirbagInfo();
|
||||||
|
|
||||||
// The next set of methods are provided for users who wish to access
|
// The next set of methods are provided for users who wish to access
|
||||||
// data in minidump files directly, while leveraging the rest of
|
// data in minidump files directly, while leveraging the rest of
|
||||||
|
@ -639,7 +679,8 @@ class Minidump {
|
||||||
// structure and known stream types.
|
// structure and known stream types.
|
||||||
|
|
||||||
unsigned int GetDirectoryEntryCount() const {
|
unsigned int GetDirectoryEntryCount() const {
|
||||||
return valid_ ? header_.stream_count : 0; }
|
return valid_ ? header_.stream_count : 0;
|
||||||
|
}
|
||||||
const MDRawDirectory* GetDirectoryEntryAtIndex(unsigned int index) const;
|
const MDRawDirectory* GetDirectoryEntryAtIndex(unsigned int index) const;
|
||||||
|
|
||||||
// The next 2 methods are lower-level I/O routines. They use fd_.
|
// The next 2 methods are lower-level I/O routines. They use fd_.
|
||||||
|
|
|
@ -52,7 +52,7 @@ class ProcessState {
|
||||||
bool crashed() const { return crashed_; }
|
bool crashed() const { return crashed_; }
|
||||||
string crash_reason() const { return crash_reason_; }
|
string crash_reason() const { return crash_reason_; }
|
||||||
u_int64_t crash_address() const { return crash_address_; }
|
u_int64_t crash_address() const { return crash_address_; }
|
||||||
unsigned int crash_thread() const { return crash_thread_; }
|
int requesting_thread() const { return requesting_thread_; }
|
||||||
const vector<CallStack*>* threads() const { return &threads_; }
|
const vector<CallStack*>* threads() const { return &threads_; }
|
||||||
string os() const { return os_; }
|
string os() const { return os_; }
|
||||||
string os_version() const { return os_version_; }
|
string os_version() const { return os_version_; }
|
||||||
|
@ -65,7 +65,7 @@ class ProcessState {
|
||||||
|
|
||||||
// Disallow instantiation other than by friends.
|
// Disallow instantiation other than by friends.
|
||||||
ProcessState() : crashed_(false), crash_reason_(), crash_address_(0),
|
ProcessState() : crashed_(false), crash_reason_(), crash_address_(0),
|
||||||
crash_thread_(0), threads_(), os_(), os_version_(),
|
requesting_thread_(-1), threads_(), os_(), os_version_(),
|
||||||
cpu_(), cpu_info_() {}
|
cpu_(), cpu_info_() {}
|
||||||
|
|
||||||
// True if the process crashed, false if the dump was produced outside
|
// True if the process crashed, false if the dump was produced outside
|
||||||
|
@ -84,9 +84,15 @@ class ProcessState {
|
||||||
// this will be the address of the instruction that caused the fault.
|
// this will be the address of the instruction that caused the fault.
|
||||||
u_int64_t crash_address_;
|
u_int64_t crash_address_;
|
||||||
|
|
||||||
// If the process crashed, the index of the crashed thread's stack
|
// The index of the thread that requested a dump be written in the
|
||||||
// in the threads vector.
|
// threads vector. If a dump was produced as a result of a crash, this
|
||||||
unsigned int crash_thread_;
|
// will point to the thread that crashed. If the dump was produced as
|
||||||
|
// by user code without crashing, and the dump contains extended Airbag
|
||||||
|
// information, this will point to the thread that requested the dump.
|
||||||
|
// If the dump was not produced as a result of an exception and no
|
||||||
|
// extended Airbag information is present, this field will be set to -1,
|
||||||
|
// indicating that the dump thread is not available.
|
||||||
|
int requesting_thread_;
|
||||||
|
|
||||||
// Stacks for each thread (except possibly the exception handler
|
// Stacks for each thread (except possibly the exception handler
|
||||||
// thread) at the time of the crash.
|
// thread) at the time of the crash.
|
||||||
|
|
|
@ -661,7 +661,8 @@ const u_int8_t* MinidumpMemoryRegion::GetMemory() {
|
||||||
|
|
||||||
|
|
||||||
u_int64_t MinidumpMemoryRegion::GetBase() {
|
u_int64_t MinidumpMemoryRegion::GetBase() {
|
||||||
return valid_ ? descriptor_->start_of_memory_range : (u_int64_t)-1;
|
return valid_ ?
|
||||||
|
descriptor_->start_of_memory_range : static_cast<u_int64_t>(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -829,8 +830,12 @@ MinidumpContext* MinidumpThread::GetContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
u_int32_t MinidumpThread::GetThreadID() {
|
bool MinidumpThread::GetThreadID(u_int32_t *thread_id) const {
|
||||||
return valid_ ? thread_.thread_id : (u_int32_t)-1;
|
if (!thread_id || !valid_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*thread_id = thread_.thread_id;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -928,7 +933,10 @@ bool MinidumpThreadList::Read(u_int32_t expected_size) {
|
||||||
if (!thread->Read())
|
if (!thread->Read())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
u_int32_t thread_id = thread->GetThreadID();
|
u_int32_t thread_id;
|
||||||
|
if (!thread->GetThreadID(&thread_id))
|
||||||
|
return false;
|
||||||
|
|
||||||
if (GetThreadByID(thread_id)) {
|
if (GetThreadByID(thread_id)) {
|
||||||
// Another thread with this ID is already in the list. Data error.
|
// Another thread with this ID is already in the list. Data error.
|
||||||
return false;
|
return false;
|
||||||
|
@ -1710,8 +1718,12 @@ bool MinidumpException::Read(u_int32_t expected_size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
u_int32_t MinidumpException::GetThreadID() {
|
bool MinidumpException::GetThreadID(u_int32_t *thread_id) const {
|
||||||
return valid_ ? exception_.thread_id : 0;
|
if (!thread_id || !valid_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*thread_id = exception_.thread_id;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2008,6 +2020,85 @@ void MinidumpMiscInfo::Print() {
|
||||||
printf(" processor_current_idle_state = 0x%x\n",
|
printf(" processor_current_idle_state = 0x%x\n",
|
||||||
misc_info_.processor_current_idle_state);
|
misc_info_.processor_current_idle_state);
|
||||||
}
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// MinidumpAirbagInfo
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
MinidumpAirbagInfo::MinidumpAirbagInfo(Minidump* minidump)
|
||||||
|
: MinidumpStream(minidump),
|
||||||
|
airbag_info_() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MinidumpAirbagInfo::Read(u_int32_t expected_size) {
|
||||||
|
valid_ = false;
|
||||||
|
|
||||||
|
if (expected_size != sizeof(airbag_info_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!minidump_->ReadBytes(&airbag_info_, sizeof(airbag_info_)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (minidump_->swap()) {
|
||||||
|
Swap(&airbag_info_.validity);
|
||||||
|
Swap(&airbag_info_.dump_thread_id);
|
||||||
|
Swap(&airbag_info_.requesting_thread_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MinidumpAirbagInfo::GetDumpThreadID(u_int32_t *thread_id) const {
|
||||||
|
if (!thread_id || !valid_ ||
|
||||||
|
!(airbag_info_.validity & MD_AIRBAG_INFO_VALID_DUMP_THREAD_ID)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*thread_id = airbag_info_.dump_thread_id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool MinidumpAirbagInfo::GetRequestingThreadID(u_int32_t *thread_id)
|
||||||
|
const {
|
||||||
|
if (!thread_id || !valid_ ||
|
||||||
|
!(airbag_info_.validity & MD_AIRBAG_INFO_VALID_REQUESTING_THREAD_ID)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*thread_id = airbag_info_.requesting_thread_id;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MinidumpAirbagInfo::Print() {
|
||||||
|
if (!valid_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("MDRawAirbagInfo\n");
|
||||||
|
printf(" validity = 0x%x\n", airbag_info_.validity);
|
||||||
|
|
||||||
|
if (airbag_info_.validity & MD_AIRBAG_INFO_VALID_DUMP_THREAD_ID) {
|
||||||
|
printf(" dump_thread_id = 0x%x\n", airbag_info_.dump_thread_id);
|
||||||
|
} else {
|
||||||
|
printf(" dump_thread_id = (invalid)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (airbag_info_.validity & MD_AIRBAG_INFO_VALID_DUMP_THREAD_ID) {
|
||||||
|
printf(" requesting_thread_id = 0x%x\n",
|
||||||
|
airbag_info_.requesting_thread_id);
|
||||||
|
} else {
|
||||||
|
printf(" requesting_thread_id = (invalid)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2135,7 +2226,8 @@ bool Minidump::Read() {
|
||||||
case MD_MEMORY_LIST_STREAM:
|
case MD_MEMORY_LIST_STREAM:
|
||||||
case MD_EXCEPTION_STREAM:
|
case MD_EXCEPTION_STREAM:
|
||||||
case MD_SYSTEM_INFO_STREAM:
|
case MD_SYSTEM_INFO_STREAM:
|
||||||
case MD_MISC_INFO_STREAM: {
|
case MD_MISC_INFO_STREAM:
|
||||||
|
case MD_AIRBAG_INFO_STREAM: {
|
||||||
if (stream_map->find(stream_type) != stream_map->end()) {
|
if (stream_map->find(stream_type) != stream_map->end()) {
|
||||||
// Another stream with this type was already found. A minidump
|
// Another stream with this type was already found. A minidump
|
||||||
// file should contain at most one of each of these stream types.
|
// file should contain at most one of each of these stream types.
|
||||||
|
@ -2196,6 +2288,12 @@ MinidumpMiscInfo* Minidump::GetMiscInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
MinidumpAirbagInfo* Minidump::GetAirbagInfo() {
|
||||||
|
MinidumpAirbagInfo* airbag_info;
|
||||||
|
return GetStream(&airbag_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Minidump::Print() {
|
void Minidump::Print() {
|
||||||
if (!valid_)
|
if (!valid_)
|
||||||
return;
|
return;
|
||||||
|
@ -2235,7 +2333,7 @@ void Minidump::Print() {
|
||||||
++iterator) {
|
++iterator) {
|
||||||
u_int32_t stream_type = iterator->first;
|
u_int32_t stream_type = iterator->first;
|
||||||
MinidumpStreamInfo info = iterator->second;
|
MinidumpStreamInfo info = iterator->second;
|
||||||
printf(" stream type %2d at index %d\n", stream_type, info.stream_index);
|
printf(" stream type 0x%x at index %d\n", stream_type, info.stream_index);
|
||||||
}
|
}
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,81 +32,83 @@
|
||||||
//
|
//
|
||||||
// Author: Mark Mentovai
|
// Author: Mark Mentovai
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <cstdio>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "google_airbag/processor/minidump.h"
|
#include "google_airbag/processor/minidump.h"
|
||||||
|
|
||||||
|
|
||||||
using std::string;
|
|
||||||
using namespace google_airbag;
|
using namespace google_airbag;
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
int main(int argc, char** argv) {
|
|
||||||
if (argc != 2) {
|
if (argc != 2) {
|
||||||
fprintf(stderr, "usage: %s <file>\n", argv[0]);
|
fprintf(stderr, "usage: %s <file>\n", argv[0]);
|
||||||
exit(1);
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Minidump minidump(argv[1]);
|
Minidump minidump(argv[1]);
|
||||||
if (!minidump.Read()) {
|
if (!minidump.Read()) {
|
||||||
printf("minidump.Read() failed\n");
|
printf("minidump.Read() failed\n");
|
||||||
exit(1);
|
return 1;
|
||||||
}
|
}
|
||||||
minidump.Print();
|
minidump.Print();
|
||||||
|
|
||||||
int error = 0;
|
int errors = 0;
|
||||||
|
|
||||||
MinidumpThreadList* threadList = minidump.GetThreadList();
|
MinidumpThreadList *thread_list = minidump.GetThreadList();
|
||||||
if (!threadList) {
|
if (!thread_list) {
|
||||||
error |= 1 << 2;
|
++errors;
|
||||||
printf("minidump.GetThreadList() failed\n");
|
printf("minidump.GetThreadList() failed\n");
|
||||||
} else {
|
} else {
|
||||||
threadList->Print();
|
thread_list->Print();
|
||||||
}
|
}
|
||||||
|
|
||||||
MinidumpModuleList* moduleList = minidump.GetModuleList();
|
MinidumpModuleList *module_list = minidump.GetModuleList();
|
||||||
if (!moduleList) {
|
if (!module_list) {
|
||||||
error |= 1 << 3;
|
++errors;
|
||||||
printf("minidump.GetModuleList() failed\n");
|
printf("minidump.GetModuleList() failed\n");
|
||||||
} else {
|
} else {
|
||||||
moduleList->Print();
|
module_list->Print();
|
||||||
}
|
}
|
||||||
|
|
||||||
MinidumpMemoryList* memoryList = minidump.GetMemoryList();
|
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
|
||||||
if (!memoryList) {
|
if (!memory_list) {
|
||||||
error |= 1 << 4;
|
++errors;
|
||||||
printf("minidump.GetMemoryList() failed\n");
|
printf("minidump.GetMemoryList() failed\n");
|
||||||
} else {
|
} else {
|
||||||
memoryList->Print();
|
memory_list->Print();
|
||||||
}
|
}
|
||||||
|
|
||||||
MinidumpException* exception = minidump.GetException();
|
MinidumpException *exception = minidump.GetException();
|
||||||
if (!exception) {
|
if (!exception) {
|
||||||
error |= 1 << 5;
|
// Exception info is optional, so don't treat this as an error.
|
||||||
printf("minidump.GetException() failed\n");
|
printf("minidump.GetException() failed\n");
|
||||||
} else {
|
} else {
|
||||||
exception->Print();
|
exception->Print();
|
||||||
}
|
}
|
||||||
|
|
||||||
MinidumpSystemInfo* systemInfo = minidump.GetSystemInfo();
|
MinidumpSystemInfo *system_info = minidump.GetSystemInfo();
|
||||||
if (!systemInfo) {
|
if (!system_info) {
|
||||||
error |= 1 << 6;
|
++errors;
|
||||||
printf("minidump.GetSystemInfo() failed\n");
|
printf("minidump.GetSystemInfo() failed\n");
|
||||||
} else {
|
} else {
|
||||||
systemInfo->Print();
|
system_info->Print();
|
||||||
}
|
}
|
||||||
|
|
||||||
MinidumpMiscInfo* miscInfo = minidump.GetMiscInfo();
|
MinidumpMiscInfo *misc_info = minidump.GetMiscInfo();
|
||||||
if (!miscInfo) {
|
if (!misc_info) {
|
||||||
error |= 1 << 7;
|
++errors;
|
||||||
printf("minidump.GetMiscInfo() failed\n");
|
printf("minidump.GetMiscInfo() failed\n");
|
||||||
} else {
|
} else {
|
||||||
miscInfo->Print();
|
misc_info->Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpAirbagInfo *airbag_info = minidump.GetAirbagInfo();
|
||||||
|
if (!airbag_info) {
|
||||||
|
// Airbag info is optional, so don't treat this as an error.
|
||||||
|
printf("minidump.GetAirbagInfo() failed\n");
|
||||||
|
} else {
|
||||||
|
airbag_info->Print();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use return instead of exit to allow destructors to run.
|
// Use return instead of exit to allow destructors to run.
|
||||||
return(error);
|
return errors == 0 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
testdata_dir=$srcdir/src/processor/testdata
|
testdata_dir=$srcdir/src/processor/testdata
|
||||||
./src/processor/minidump_dump $testdata_dir/minidump1.dmp | \
|
./src/processor/minidump_dump $testdata_dir/minidump2.dmp | \
|
||||||
tr -s '\015' '\012' | \
|
tr -d '\015' | \
|
||||||
diff -u $testdata_dir/minidump1.out -
|
diff -u $testdata_dir/minidump2.dump.out -
|
||||||
exit $?
|
exit $?
|
||||||
|
|
|
@ -54,11 +54,22 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
||||||
process_state->cpu_ = GetCPUInfo(&dump, &process_state->cpu_info_);
|
process_state->cpu_ = GetCPUInfo(&dump, &process_state->cpu_info_);
|
||||||
process_state->os_ = GetOSInfo(&dump, &process_state->os_version_);
|
process_state->os_ = GetOSInfo(&dump, &process_state->os_version_);
|
||||||
|
|
||||||
u_int32_t exception_thread_id = 0;
|
u_int32_t dump_thread_id = 0;
|
||||||
|
bool has_dump_thread = false;
|
||||||
|
u_int32_t requesting_thread_id = 0;
|
||||||
|
bool has_requesting_thread = false;
|
||||||
|
|
||||||
|
MinidumpAirbagInfo *airbag_info = dump.GetAirbagInfo();
|
||||||
|
if (airbag_info) {
|
||||||
|
has_dump_thread = airbag_info->GetDumpThreadID(&dump_thread_id);
|
||||||
|
has_requesting_thread =
|
||||||
|
airbag_info->GetRequestingThreadID(&requesting_thread_id);
|
||||||
|
}
|
||||||
|
|
||||||
MinidumpException *exception = dump.GetException();
|
MinidumpException *exception = dump.GetException();
|
||||||
if (exception) {
|
if (exception) {
|
||||||
process_state->crashed_ = true;
|
process_state->crashed_ = true;
|
||||||
exception_thread_id = exception->GetThreadID();
|
has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
|
||||||
|
|
||||||
process_state->crash_reason_ = GetCrashReason(
|
process_state->crash_reason_ = GetCrashReason(
|
||||||
&dump, &process_state->crash_address_);
|
&dump, &process_state->crash_address_);
|
||||||
|
@ -69,7 +80,7 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool found_crash_thread = false;
|
bool found_requesting_thread = false;
|
||||||
unsigned int thread_count = threads->thread_count();
|
unsigned int thread_count = threads->thread_count();
|
||||||
for (unsigned int thread_index = 0;
|
for (unsigned int thread_index = 0;
|
||||||
thread_index < thread_count;
|
thread_index < thread_count;
|
||||||
|
@ -79,24 +90,46 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u_int32_t thread_id;
|
||||||
|
if (!thread->GetThreadID(&thread_id)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this thread is the thread that produced the minidump, don't process
|
||||||
|
// it. Because of the problems associated with a thread producing a
|
||||||
|
// dump of itself (when both its context and its stack are in flux),
|
||||||
|
// processing that stack wouldn't provide much useful data.
|
||||||
|
if (has_dump_thread && thread_id == dump_thread_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
MinidumpContext *context = thread->GetContext();
|
MinidumpContext *context = thread->GetContext();
|
||||||
|
|
||||||
if (process_state->crashed_ &&
|
if (has_requesting_thread && thread_id == requesting_thread_id) {
|
||||||
thread->GetThreadID() == exception_thread_id) {
|
if (found_requesting_thread) {
|
||||||
if (found_crash_thread) {
|
// There can't be more than one requesting thread.
|
||||||
// There can't be more than one crash thread.
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the exception record's context for the crashed thread, instead
|
// Use processed_state->threads_.size() instead of thread_index.
|
||||||
// of the thread's own context. For the crashed thread, the thread's
|
// thread_index points to the thread index in the minidump, which
|
||||||
// own context is the state inside the exception handler. Using it
|
// might be greater than the thread index in the threads vector if
|
||||||
// would not result in the expected stack trace from the time of the
|
// any of the minidump's threads are skipped and not placed into the
|
||||||
// crash.
|
// processed threads vector. The thread vector's current size will
|
||||||
context = exception->GetContext();
|
// be the index of the current thread when it's pushed into the
|
||||||
|
// vector.
|
||||||
|
process_state->requesting_thread_ = process_state->threads_.size();
|
||||||
|
|
||||||
process_state->crash_thread_ = thread_index;
|
found_requesting_thread = true;
|
||||||
found_crash_thread = true;
|
|
||||||
|
if (process_state->crashed_) {
|
||||||
|
// Use the exception record's context for the crashed thread, instead
|
||||||
|
// of the thread's own context. For the crashed thread, the thread's
|
||||||
|
// own context is the state inside the exception handler. Using it
|
||||||
|
// would not result in the expected stack trace from the time of the
|
||||||
|
// crash.
|
||||||
|
context = exception->GetContext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
|
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
|
||||||
|
@ -121,8 +154,8 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
|
||||||
process_state->threads_.push_back(stack.release());
|
process_state->threads_.push_back(stack.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the process crashed, there must be a crash thread.
|
// If a requesting thread was indicated, it must be present.
|
||||||
if (process_state->crashed_ && !found_crash_thread) {
|
if (has_requesting_thread && !found_requesting_thread) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,7 +66,7 @@ string TestSymbolSupplier::GetSymbolFile(MinidumpModule *module) {
|
||||||
// reached by a SimpleSymbolSupplier.
|
// reached by a SimpleSymbolSupplier.
|
||||||
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||||
"/src/processor/testdata/symbols/"
|
"/src/processor/testdata/symbols/"
|
||||||
"test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1/test_app.sym";
|
"test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1/test_app.sym";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -91,9 +91,9 @@ static bool RunTests() {
|
||||||
ASSERT_EQ(state->os_version(), "5.1.2600 Service Pack 2");
|
ASSERT_EQ(state->os_version(), "5.1.2600 Service Pack 2");
|
||||||
ASSERT_TRUE(state->crashed());
|
ASSERT_TRUE(state->crashed());
|
||||||
ASSERT_EQ(state->crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
|
ASSERT_EQ(state->crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
|
||||||
ASSERT_EQ(state->crash_address(), 0);
|
ASSERT_EQ(state->crash_address(), 0x45);
|
||||||
ASSERT_EQ(state->threads()->size(), 1);
|
ASSERT_EQ(state->threads()->size(), 1);
|
||||||
ASSERT_EQ(state->crash_thread(), 0);
|
ASSERT_EQ(state->requesting_thread(), 0);
|
||||||
CallStack *stack = state->threads()->at(0);
|
CallStack *stack = state->threads()->at(0);
|
||||||
ASSERT_TRUE(stack);
|
ASSERT_TRUE(stack);
|
||||||
ASSERT_EQ(stack->frames()->size(), 4);
|
ASSERT_EQ(stack->frames()->size(), 4);
|
||||||
|
@ -102,13 +102,13 @@ static bool RunTests() {
|
||||||
ASSERT_EQ(stack->frames()->at(0)->module_name, "c:\\test_app.exe");
|
ASSERT_EQ(stack->frames()->at(0)->module_name, "c:\\test_app.exe");
|
||||||
ASSERT_EQ(stack->frames()->at(0)->function_name, "CrashFunction()");
|
ASSERT_EQ(stack->frames()->at(0)->function_name, "CrashFunction()");
|
||||||
ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc");
|
ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc");
|
||||||
ASSERT_EQ(stack->frames()->at(0)->source_line, 65);
|
ASSERT_EQ(stack->frames()->at(0)->source_line, 51);
|
||||||
|
|
||||||
ASSERT_EQ(stack->frames()->at(1)->module_base, 0x400000);
|
ASSERT_EQ(stack->frames()->at(1)->module_base, 0x400000);
|
||||||
ASSERT_EQ(stack->frames()->at(1)->module_name, "c:\\test_app.exe");
|
ASSERT_EQ(stack->frames()->at(1)->module_name, "c:\\test_app.exe");
|
||||||
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
|
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
|
||||||
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
|
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
|
||||||
ASSERT_EQ(stack->frames()->at(1)->source_line, 70);
|
ASSERT_EQ(stack->frames()->at(1)->source_line, 56);
|
||||||
|
|
||||||
// This comes from the CRT
|
// This comes from the CRT
|
||||||
ASSERT_EQ(stack->frames()->at(2)->module_base, 0x400000);
|
ASSERT_EQ(stack->frames()->at(2)->module_base, 0x400000);
|
||||||
|
|
|
@ -193,19 +193,21 @@ static bool PrintMinidumpProcess(const string &minidump_file,
|
||||||
printf("No crash\n");
|
printf("No crash\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there's a crash thread, print it first.
|
// If the thread that requested the dump is known, print it first.
|
||||||
int crash_thread = -1;
|
int requesting_thread = process_state->requesting_thread();
|
||||||
if (process_state->crashed()) {
|
if (requesting_thread != -1) {
|
||||||
crash_thread = process_state->crash_thread();
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Thread %d (crashed)\n", crash_thread);
|
printf("Thread %d (%s)\n",
|
||||||
PrintStack(process_state->threads()->at(crash_thread), cpu);
|
requesting_thread,
|
||||||
|
process_state->crashed() ? "crashed" :
|
||||||
|
"requested dump, did not crash");
|
||||||
|
PrintStack(process_state->threads()->at(requesting_thread), cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print all of the threads in the dump.
|
// Print all of the threads in the dump.
|
||||||
int thread_count = process_state->threads()->size();
|
int thread_count = process_state->threads()->size();
|
||||||
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
|
for (int thread_index = 0; thread_index < thread_count; ++thread_index) {
|
||||||
if (thread_index != crash_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\n", thread_index);
|
||||||
|
|
BIN
src/processor/testdata/minidump1.dmp
vendored
BIN
src/processor/testdata/minidump1.dmp
vendored
Binary file not shown.
3735
src/processor/testdata/minidump1.out
vendored
3735
src/processor/testdata/minidump1.out
vendored
File diff suppressed because one or more lines are too long
BIN
src/processor/testdata/minidump2.dmp
vendored
BIN
src/processor/testdata/minidump2.dmp
vendored
Binary file not shown.
666
src/processor/testdata/minidump2.dump.out
vendored
Normal file
666
src/processor/testdata/minidump2.dump.out
vendored
Normal file
File diff suppressed because one or more lines are too long
16
src/processor/testdata/minidump2.stackwalk.out
vendored
16
src/processor/testdata/minidump2.stackwalk.out
vendored
|
@ -4,16 +4,16 @@ CPU: x86
|
||||||
GenuineIntel family 6 model 13 stepping 8
|
GenuineIntel family 6 model 13 stepping 8
|
||||||
|
|
||||||
Crash reason: EXCEPTION_ACCESS_VIOLATION
|
Crash reason: EXCEPTION_ACCESS_VIOLATION
|
||||||
Crash address: 0x0
|
Crash address: 0x45
|
||||||
|
|
||||||
Thread 0 (crashed)
|
Thread 0 (crashed)
|
||||||
0 test_app.exe!CrashFunction() [test_app.cc : 65 + 0x3]
|
0 test_app.exe!CrashFunction() [test_app.cc : 51 + 0x3]
|
||||||
eip = 0x0040102e esp = 0x0012ff3c ebp = 0x0012ff40 ebx = 0x7c80abc1
|
eip = 0x0040208e esp = 0x0012feec ebp = 0x0012fef0 ebx = 0x7c80abc1
|
||||||
esi = 0x00000002 edi = 0x00000a28 eax = 0x00000000 ecx = 0x00000001
|
esi = 0x00000002 edi = 0x00000a28 eax = 0x00000045 ecx = 0x0012fefc
|
||||||
edx = 0x0041c888 efl = 0x00010286
|
edx = 0x7c90eb94 efl = 0x00010246
|
||||||
1 test_app.exe!main [test_app.cc : 70 + 0x4]
|
1 test_app.exe!main [test_app.cc : 56 + 0x4]
|
||||||
eip = 0x0040107f esp = 0x0012ff48 ebp = 0x0012ff70
|
eip = 0x004020df esp = 0x0012fef8 ebp = 0x0012ff70
|
||||||
2 test_app.exe!__tmainCRTStartup [crt0.c : 318 + 0x11]
|
2 test_app.exe!__tmainCRTStartup [crt0.c : 318 + 0x11]
|
||||||
eip = 0x0040153c esp = 0x0012ff78 ebp = 0x0012ffc0 ebx = 0x7c80abc1
|
eip = 0x0040395c esp = 0x0012ff78 ebp = 0x0012ffc0
|
||||||
3 kernel32.dll!BaseProcessStart + 0x22
|
3 kernel32.dll!BaseProcessStart + 0x22
|
||||||
eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0
|
eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0
|
||||||
|
|
File diff suppressed because it is too large
Load diff
21406
src/processor/testdata/symbols/test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1/test_app.sym
vendored
Normal file
21406
src/processor/testdata/symbols/test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1/test_app.sym
vendored
Normal file
File diff suppressed because it is too large
Load diff
49
src/processor/testdata/test_app.cc
vendored
49
src/processor/testdata/test_app.cc
vendored
|
@ -28,47 +28,32 @@
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// This file is used to generate minidump2.dmp and minidump2.sym.
|
// This file is used to generate minidump2.dmp and minidump2.sym.
|
||||||
// cl /Zi /Fetest_app.exe test_app.cc dbghelp.lib
|
// cl /Zi test_app.cc /Fetest_app.exe /I airbag/src \
|
||||||
|
// airbag/src/client/windows/releasestaticcrt/exception_handler.lib \
|
||||||
|
// ole32.lib
|
||||||
// Then run test_app to generate a dump, and dump_syms to create the .sym file.
|
// Then run test_app to generate a dump, and dump_syms to create the .sym file.
|
||||||
|
|
||||||
#include <windows.h>
|
#include <cstdio>
|
||||||
#include <dbghelp.h>
|
|
||||||
|
|
||||||
static LONG HandleException(EXCEPTION_POINTERS *exinfo) {
|
#include "client/windows/handler/exception_handler.h"
|
||||||
HANDLE dump_file = CreateFile("dump.dmp",
|
|
||||||
GENERIC_WRITE,
|
|
||||||
FILE_SHARE_WRITE,
|
|
||||||
NULL,
|
|
||||||
CREATE_ALWAYS,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
MINIDUMP_EXCEPTION_INFORMATION except_info;
|
void callback(const std::wstring &id, void *context, bool succeeded) {
|
||||||
except_info.ThreadId = GetCurrentThreadId();
|
if (succeeded) {
|
||||||
except_info.ExceptionPointers = exinfo;
|
printf("dump guid is %ws\n", id.c_str());
|
||||||
except_info.ClientPointers = false;
|
} else {
|
||||||
|
printf("dump failed\n");
|
||||||
MiniDumpWriteDump(GetCurrentProcess(),
|
}
|
||||||
GetCurrentProcessId(),
|
exit(1);
|
||||||
dump_file,
|
|
||||||
MiniDumpNormal,
|
|
||||||
&except_info,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
CloseHandle(dump_file);
|
|
||||||
return EXCEPTION_EXECUTE_HANDLER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashFunction() {
|
void CrashFunction() {
|
||||||
int *i = NULL;
|
int *i = reinterpret_cast<int*>(0x45);
|
||||||
*i = 5; // crash!
|
*i = 5; // crash!
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char **argv) {
|
||||||
__try {
|
google_airbag::ExceptionHandler eh(L".", callback, NULL, true);
|
||||||
CrashFunction();
|
CrashFunction();
|
||||||
} __except(HandleException(GetExceptionInformation())) {
|
printf("did not crash?\n");
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue