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:
mmentovai 2006-11-06 23:00:19 +00:00
parent 5ac32b6534
commit 76f052f8fb
16 changed files with 22370 additions and 15417 deletions

View file

@ -280,8 +280,9 @@ class MinidumpThread : public MinidumpObject {
// 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
// MDRawThread structure.
u_int32_t GetThreadID();
// MDRawThread structure. Returns false if the thread ID cannot be
// determined.
bool GetThreadID(u_int32_t *thread_id) const;
// Print a human-readable representation of the object to stdout.
void Print();
@ -349,9 +350,10 @@ class MinidumpModule : public MinidumpObject {
public:
~MinidumpModule();
const MDRawModule* module() const { return valid_ ? &module_ : 0; }
const MDRawModule* module() const { return valid_ ? &module_ : NULL; }
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; }
// The name of the file containing this module's code (exe, dll, so,
@ -513,12 +515,14 @@ class MinidumpException : public MinidumpStream {
~MinidumpException();
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,
// so a special getter is provided to retrieve this data from the
// MDRawExceptionStream structure.
u_int32_t GetThreadID();
// MDRawExceptionStream structure. Returns false if the thread ID cannot
// be determined.
bool GetThreadID(u_int32_t *thread_id) const;
MinidumpContext* GetContext();
@ -546,7 +550,8 @@ class MinidumpSystemInfo : public MinidumpStream {
~MinidumpSystemInfo();
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
// returning a textual representation of the OS service pack. On other
@ -587,7 +592,9 @@ class MinidumpSystemInfo : public MinidumpStream {
// information. See also MinidumpSystemInfo.
class MinidumpMiscInfo : public MinidumpStream {
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.
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
// and provides access to the minidump's top-level stream directory.
class Minidump {
@ -614,7 +653,7 @@ class 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.
// The minidump is expected to be positioned at the beginning of the
@ -622,7 +661,7 @@ class Minidump {
// Minidump object.
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
// to avoid exposing an ugly API (GetStream needs to accept a garbage
// parameter).
@ -632,6 +671,7 @@ class Minidump {
MinidumpException* GetException();
MinidumpSystemInfo* GetSystemInfo();
MinidumpMiscInfo* GetMiscInfo();
MinidumpAirbagInfo* GetAirbagInfo();
// The next set of methods are provided for users who wish to access
// data in minidump files directly, while leveraging the rest of
@ -639,7 +679,8 @@ class Minidump {
// structure and known stream types.
unsigned int GetDirectoryEntryCount() const {
return valid_ ? header_.stream_count : 0; }
return valid_ ? header_.stream_count : 0;
}
const MDRawDirectory* GetDirectoryEntryAtIndex(unsigned int index) const;
// The next 2 methods are lower-level I/O routines. They use fd_.

View file

@ -52,7 +52,7 @@ class ProcessState {
bool crashed() const { return crashed_; }
string crash_reason() const { return crash_reason_; }
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_; }
string os() const { return os_; }
string os_version() const { return os_version_; }
@ -65,7 +65,7 @@ class ProcessState {
// Disallow instantiation other than by friends.
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_() {}
// 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.
u_int64_t crash_address_;
// If the process crashed, the index of the crashed thread's stack
// in the threads vector.
unsigned int crash_thread_;
// The index of the thread that requested a dump be written in the
// threads vector. If a dump was produced as a result of a crash, this
// 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
// thread) at the time of the crash.

View file

@ -661,7 +661,8 @@ const u_int8_t* MinidumpMemoryRegion::GetMemory() {
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() {
return valid_ ? thread_.thread_id : (u_int32_t)-1;
bool MinidumpThread::GetThreadID(u_int32_t *thread_id) const {
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())
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)) {
// Another thread with this ID is already in the list. Data error.
return false;
@ -1710,8 +1718,12 @@ bool MinidumpException::Read(u_int32_t expected_size) {
}
u_int32_t MinidumpException::GetThreadID() {
return valid_ ? exception_.thread_id : 0;
bool MinidumpException::GetThreadID(u_int32_t *thread_id) const {
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",
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_EXCEPTION_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()) {
// Another stream with this type was already found. A minidump
// 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() {
if (!valid_)
return;
@ -2235,7 +2333,7 @@ void Minidump::Print() {
++iterator) {
u_int32_t stream_type = iterator->first;
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");
}

View file

@ -32,81 +32,83 @@
//
// Author: Mark Mentovai
#include <stdlib.h>
#include <stdio.h>
#include <string>
#include <cstdio>
#include "google_airbag/processor/minidump.h"
using std::string;
using namespace google_airbag;
int main(int argc, char** argv) {
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "usage: %s <file>\n", argv[0]);
exit(1);
return 1;
}
Minidump minidump(argv[1]);
if (!minidump.Read()) {
printf("minidump.Read() failed\n");
exit(1);
return 1;
}
minidump.Print();
int error = 0;
int errors = 0;
MinidumpThreadList* threadList = minidump.GetThreadList();
if (!threadList) {
error |= 1 << 2;
MinidumpThreadList *thread_list = minidump.GetThreadList();
if (!thread_list) {
++errors;
printf("minidump.GetThreadList() failed\n");
} else {
threadList->Print();
thread_list->Print();
}
MinidumpModuleList* moduleList = minidump.GetModuleList();
if (!moduleList) {
error |= 1 << 3;
MinidumpModuleList *module_list = minidump.GetModuleList();
if (!module_list) {
++errors;
printf("minidump.GetModuleList() failed\n");
} else {
moduleList->Print();
module_list->Print();
}
MinidumpMemoryList* memoryList = minidump.GetMemoryList();
if (!memoryList) {
error |= 1 << 4;
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
if (!memory_list) {
++errors;
printf("minidump.GetMemoryList() failed\n");
} else {
memoryList->Print();
memory_list->Print();
}
MinidumpException* exception = minidump.GetException();
MinidumpException *exception = minidump.GetException();
if (!exception) {
error |= 1 << 5;
// Exception info is optional, so don't treat this as an error.
printf("minidump.GetException() failed\n");
} else {
exception->Print();
}
MinidumpSystemInfo* systemInfo = minidump.GetSystemInfo();
if (!systemInfo) {
error |= 1 << 6;
MinidumpSystemInfo *system_info = minidump.GetSystemInfo();
if (!system_info) {
++errors;
printf("minidump.GetSystemInfo() failed\n");
} else {
systemInfo->Print();
system_info->Print();
}
MinidumpMiscInfo* miscInfo = minidump.GetMiscInfo();
if (!miscInfo) {
error |= 1 << 7;
MinidumpMiscInfo *misc_info = minidump.GetMiscInfo();
if (!misc_info) {
++errors;
printf("minidump.GetMiscInfo() failed\n");
} 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.
return(error);
return errors == 0 ? 0 : 1;
}

View file

@ -30,7 +30,7 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
testdata_dir=$srcdir/src/processor/testdata
./src/processor/minidump_dump $testdata_dir/minidump1.dmp | \
tr -s '\015' '\012' | \
diff -u $testdata_dir/minidump1.out -
./src/processor/minidump_dump $testdata_dir/minidump2.dmp | \
tr -d '\015' | \
diff -u $testdata_dir/minidump2.dump.out -
exit $?

View file

@ -54,11 +54,22 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
process_state->cpu_ = GetCPUInfo(&dump, &process_state->cpu_info_);
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();
if (exception) {
process_state->crashed_ = true;
exception_thread_id = exception->GetThreadID();
has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
process_state->crash_reason_ = GetCrashReason(
&dump, &process_state->crash_address_);
@ -69,7 +80,7 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
return NULL;
}
bool found_crash_thread = false;
bool found_requesting_thread = false;
unsigned int thread_count = threads->thread_count();
for (unsigned int thread_index = 0;
thread_index < thread_count;
@ -79,24 +90,46 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
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();
if (process_state->crashed_ &&
thread->GetThreadID() == exception_thread_id) {
if (found_crash_thread) {
// There can't be more than one crash thread.
if (has_requesting_thread && thread_id == requesting_thread_id) {
if (found_requesting_thread) {
// There can't be more than one requesting thread.
return NULL;
}
// 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();
// Use processed_state->threads_.size() instead of thread_index.
// thread_index points to the thread index in the minidump, which
// might be greater than the thread index in the threads vector if
// any of the minidump's threads are skipped and not placed into the
// processed threads vector. The thread vector's current size will
// 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_crash_thread = true;
found_requesting_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();
@ -121,8 +154,8 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) {
process_state->threads_.push_back(stack.release());
}
// If the process crashed, there must be a crash thread.
if (process_state->crashed_ && !found_crash_thread) {
// If a requesting thread was indicated, it must be present.
if (has_requesting_thread && !found_requesting_thread) {
return NULL;
}

View file

@ -66,7 +66,7 @@ string TestSymbolSupplier::GetSymbolFile(MinidumpModule *module) {
// reached by a SimpleSymbolSupplier.
return string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/symbols/"
"test_app.pdb/63FE4780728D49379B9D7BB6460CB42A1/test_app.sym";
"test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1/test_app.sym";
}
return "";
@ -91,9 +91,9 @@ static bool RunTests() {
ASSERT_EQ(state->os_version(), "5.1.2600 Service Pack 2");
ASSERT_TRUE(state->crashed());
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->crash_thread(), 0);
ASSERT_EQ(state->requesting_thread(), 0);
CallStack *stack = state->threads()->at(0);
ASSERT_TRUE(stack);
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)->function_name, "CrashFunction()");
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_name, "c:\\test_app.exe");
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_line, 70);
ASSERT_EQ(stack->frames()->at(1)->source_line, 56);
// This comes from the CRT
ASSERT_EQ(stack->frames()->at(2)->module_base, 0x400000);

View file

@ -193,19 +193,21 @@ static bool PrintMinidumpProcess(const string &minidump_file,
printf("No crash\n");
}
// If there's a crash thread, print it first.
int crash_thread = -1;
if (process_state->crashed()) {
crash_thread = process_state->crash_thread();
// 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 (crashed)\n", crash_thread);
PrintStack(process_state->threads()->at(crash_thread), cpu);
printf("Thread %d (%s)\n",
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.
int thread_count = process_state->threads()->size();
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.
printf("\n");
printf("Thread %d\n", thread_index);

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -4,16 +4,16 @@ CPU: x86
GenuineIntel family 6 model 13 stepping 8
Crash reason: EXCEPTION_ACCESS_VIOLATION
Crash address: 0x0
Crash address: 0x45
Thread 0 (crashed)
0 test_app.exe!CrashFunction() [test_app.cc : 65 + 0x3]
eip = 0x0040102e esp = 0x0012ff3c ebp = 0x0012ff40 ebx = 0x7c80abc1
esi = 0x00000002 edi = 0x00000a28 eax = 0x00000000 ecx = 0x00000001
edx = 0x0041c888 efl = 0x00010286
1 test_app.exe!main [test_app.cc : 70 + 0x4]
eip = 0x0040107f esp = 0x0012ff48 ebp = 0x0012ff70
0 test_app.exe!CrashFunction() [test_app.cc : 51 + 0x3]
eip = 0x0040208e esp = 0x0012feec ebp = 0x0012fef0 ebx = 0x7c80abc1
esi = 0x00000002 edi = 0x00000a28 eax = 0x00000045 ecx = 0x0012fefc
edx = 0x7c90eb94 efl = 0x00010246
1 test_app.exe!main [test_app.cc : 56 + 0x4]
eip = 0x004020df esp = 0x0012fef8 ebp = 0x0012ff70
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
eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0

File diff suppressed because it is too large Load diff

View file

@ -28,47 +28,32 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 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.
#include <windows.h>
#include <dbghelp.h>
#include <cstdio>
static LONG HandleException(EXCEPTION_POINTERS *exinfo) {
HANDLE dump_file = CreateFile("dump.dmp",
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
#include "client/windows/handler/exception_handler.h"
MINIDUMP_EXCEPTION_INFORMATION except_info;
except_info.ThreadId = GetCurrentThreadId();
except_info.ExceptionPointers = exinfo;
except_info.ClientPointers = false;
MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
dump_file,
MiniDumpNormal,
&except_info,
NULL,
NULL);
CloseHandle(dump_file);
return EXCEPTION_EXECUTE_HANDLER;
void callback(const std::wstring &id, void *context, bool succeeded) {
if (succeeded) {
printf("dump guid is %ws\n", id.c_str());
} else {
printf("dump failed\n");
}
exit(1);
}
void CrashFunction() {
int *i = NULL;
int *i = reinterpret_cast<int*>(0x45);
*i = 5; // crash!
}
int main(int argc, char *argv[]) {
__try {
CrashFunction();
} __except(HandleException(GetExceptionInformation())) {
}
int main(int argc, char **argv) {
google_airbag::ExceptionHandler eh(L".", callback, NULL, true);
CrashFunction();
printf("did not crash?\n");
return 0;
}