diff --git a/Makefile.am b/Makefile.am index 315a4610..a603771c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,6 +53,7 @@ src_libairbag_la_SOURCES = \ src/google/airbag_types.h \ src/google/call_stack.h \ src/google/minidump_processor.h \ + src/google/process_state.h \ src/google/stack_frame.h \ src/google/stack_frame_cpu.h \ src/google/symbol_supplier.h \ @@ -69,6 +70,7 @@ src_libairbag_la_SOURCES = \ src/processor/minidump_processor.cc \ src/processor/postfix_evaluator.h \ src/processor/postfix_evaluator-inl.h \ + src/processor/process_state.cc \ src/processor/range_map.h \ src/processor/range_map-inl.h \ src/processor/scoped_ptr.h \ @@ -122,6 +124,7 @@ src_processor_minidump_processor_unittest_LDADD = \ src/processor/call_stack.lo \ src/processor/minidump_processor.lo \ src/processor/minidump.lo \ + src/processor/process_state.lo \ src/processor/stackwalker.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_x86.lo \ diff --git a/Makefile.in b/Makefile.in index d693a496..c7589841 100644 --- a/Makefile.in +++ b/Makefile.in @@ -111,6 +111,7 @@ src_libairbag_la_LIBADD = am__dirstamp = $(am__leading_dot)dirstamp am_src_libairbag_la_OBJECTS = src/processor/call_stack.lo \ src/processor/minidump.lo src/processor/minidump_processor.lo \ + src/processor/process_state.lo \ src/processor/source_line_resolver.lo \ src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_x86.lo @@ -141,7 +142,8 @@ src_processor_minidump_processor_unittest_OBJECTS = \ src_processor_minidump_processor_unittest_DEPENDENCIES = \ src/processor/call_stack.lo \ src/processor/minidump_processor.lo src/processor/minidump.lo \ - src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \ + src/processor/process_state.lo src/processor/stackwalker.lo \ + src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_x86.lo \ src/processor/source_line_resolver.lo am_src_processor_minidump_stackwalk_OBJECTS = \ @@ -353,6 +355,7 @@ src_libairbag_la_SOURCES = \ src/google/airbag_types.h \ src/google/call_stack.h \ src/google/minidump_processor.h \ + src/google/process_state.h \ src/google/stack_frame.h \ src/google/stack_frame_cpu.h \ src/google/symbol_supplier.h \ @@ -369,6 +372,7 @@ src_libairbag_la_SOURCES = \ src/processor/minidump_processor.cc \ src/processor/postfix_evaluator.h \ src/processor/postfix_evaluator-inl.h \ + src/processor/process_state.cc \ src/processor/range_map.h \ src/processor/range_map-inl.h \ src/processor/scoped_ptr.h \ @@ -401,6 +405,7 @@ src_processor_minidump_processor_unittest_LDADD = \ src/processor/call_stack.lo \ src/processor/minidump_processor.lo \ src/processor/minidump.lo \ + src/processor/process_state.lo \ src/processor/stackwalker.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_x86.lo \ @@ -551,6 +556,8 @@ src/processor/minidump.lo: src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/minidump_processor.lo: src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/process_state.lo: src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/source_line_resolver.lo: src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/stackwalker.lo: src/processor/$(am__dirstamp) \ @@ -674,6 +681,8 @@ mostlyclean-compile: -rm -f src/processor/minidump_processor_unittest.$(OBJEXT) -rm -f src/processor/minidump_stackwalk.$(OBJEXT) -rm -f src/processor/postfix_evaluator_unittest.$(OBJEXT) + -rm -f src/processor/process_state.$(OBJEXT) + -rm -f src/processor/process_state.lo -rm -f src/processor/range_map_unittest.$(OBJEXT) -rm -f src/processor/source_line_resolver.$(OBJEXT) -rm -f src/processor/source_line_resolver.lo @@ -698,6 +707,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_stackwalk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/postfix_evaluator_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/process_state.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver_unittest.Po@am__quote@ diff --git a/src/google/call_stack.h b/src/google/call_stack.h index 6e8f5f13..f51d0ca7 100644 --- a/src/google/call_stack.h +++ b/src/google/call_stack.h @@ -64,6 +64,9 @@ class CallStack { // Stackwalker is responsible for building the frames_ vector. friend class Stackwalker; + // Disallow instantiation other than by friends. + CallStack() : frames_() {} + // Storage for pushed frames. vector frames_; }; diff --git a/src/google/minidump_processor.h b/src/google/minidump_processor.h index 6a126022..2a19ba1a 100644 --- a/src/google/minidump_processor.h +++ b/src/google/minidump_processor.h @@ -36,7 +36,8 @@ namespace google_airbag { using std::string; -class CallStack; +class Minidump; +class ProcessState; class SymbolSupplier; class MinidumpProcessor { @@ -46,9 +47,34 @@ class MinidumpProcessor { MinidumpProcessor(SymbolSupplier *supplier); ~MinidumpProcessor(); - // Returns a new CallStack produced by processing the minidump file. The - // caller takes ownership of the CallStack. Returns NULL on failure. - CallStack* Process(const string &minidump_file); + // Returns a new ProcessState object produced by processing the minidump + // file. The caller takes ownership of the ProcessState. Returns NULL on + // failure. + ProcessState* Process(const string &minidump_file); + + // Returns a textual representation of the base CPU type that the minidump + // in dump was produced on. Returns an empty string if this information + // cannot be determined. If cpu_info is non-NULL, it will be set to + // contain additional identifying information about the CPU, or it will + // be set empty if additional information cannot be determined. + static string GetCPUInfo(Minidump *dump, string *cpu_info); + + // Returns a textual representation of the operating system that the + // minidump in dump was produced on. Returns an empty string if this + // information cannot be determined. If os_version is non-NULL, it + // will be set to contain information about the specific version of the + // OS, or it will be set empty if version information cannot be determined. + static string GetOSInfo(Minidump *dump, string *os_version); + + // Returns a textual representation of the reason that a crash occurred, + // if the minidump in dump was produced as a result of a crash. Returns + // an empty string if this information cannot be determined. If address + // is non-NULL, it will be set to contain the address that caused the + // exception, if this information is available. This will be a code + // address when the crash was caused by problems such as illegal + // instructions or divisions by zero, or a data address when the crash + // was caused by a memory access violation. + static string GetCrashReason(Minidump *dump, u_int64_t *address); private: SymbolSupplier *supplier_; diff --git a/src/google/process_state.h b/src/google/process_state.h new file mode 100644 index 00000000..b233b390 --- /dev/null +++ b/src/google/process_state.h @@ -0,0 +1,121 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// process_state.h: A snapshot of a process, in a fully-digested state. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_PROCESS_STATE_H__ +#define GOOGLE_PROCESS_STATE_H__ + +#include +#include + +namespace google_airbag { + +using std::string; +using std::vector; + +class CallStack; + +class ProcessState { + public: + ~ProcessState(); + + // Accessors. See the data declarations below. + 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_; } + const vector* threads() const { return &threads_; } + string os() const { return os_; } + string os_version() const { return os_version_; } + string cpu() const { return cpu_; } + string cpu_info() const { return cpu_info_; } + + private: + // MinidumpProcessor is responsible for building ProcessState objects. + friend class MinidumpProcessor; + + // Disallow instantiation other than by friends. + ProcessState() : crashed_(false), crash_reason_(), crash_address_(0), + crash_thread_(0), threads_(), os_(), os_version_(), + cpu_(), cpu_info_() {} + + // True if the process crashed, false if the dump was produced outside + // of an exception handler. + bool crashed_; + + // If the process crashed, the type of crash. OS- and possibly CPU- + // specific. For example, "EXCEPTION_ACCESS_VIOLATION" (Windows), + // "EXC_BAD_ACCESS / KERN_INVALID_ADDRESS" (Mac OS X), "SIGSEGV" + // (other Unix). + string crash_reason_; + + // If the process crashed, and if crash_reason implicates memory, + // the memory address that caused the crash. For data access errors, + // this will be the data address that caused the fault. For code errors, + // 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_; + + // Stacks for each thread (except possibly the exception handler + // thread) at the time of the crash. + vector threads_; + + // A string identifying the operating system, such as "Windows NT", + // "Mac OS X", or "Linux". If the information is present in the dump but + // its value is unknown, this field will contain a numeric value. If + // the information is not present in the dump, this field will be empty. + string os_; + + // A string identifying the version of the operating system, such as + // "5.1.2600 Service Pack 2" or "10.4.8 8L2127". If the dump does not + // contain this information, this field will be empty. + string os_version_; + + // A string identifying the basic CPU family, such as "x86" or "ppc". + // If this information is present in the dump but its value is unknown, + // this field will contain a numeric value. If the information is not + // present in the dump, this field will be empty. + string cpu_; + + // A string further identifying the specific CPU, such as + // "GenuineIntel level 6 model 13 stepping 8". If the information is not + // present in the dump, or additional identifying information is not + // defined for the CPU family, this field will be empty. + string cpu_info_; +}; + +} // namespace google_airbag + +#endif // GOOGLE_CALL_STACK_H__ diff --git a/src/processor/call_stack.cc b/src/processor/call_stack.cc index 27d62799..1c31b5a3 100644 --- a/src/processor/call_stack.cc +++ b/src/processor/call_stack.cc @@ -35,7 +35,6 @@ #include "google/call_stack.h" #include "google/stack_frame.h" -#include "processor/linked_ptr.h" namespace google_airbag { diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 068f47d0..c99c432e 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -1779,12 +1779,14 @@ void MinidumpException::Print() { MinidumpSystemInfo::MinidumpSystemInfo(Minidump* minidump) : MinidumpStream(minidump), system_info_(), - csd_version_(NULL) { + csd_version_(NULL), + cpu_vendor_(NULL) { } MinidumpSystemInfo::~MinidumpSystemInfo() { delete csd_version_; + delete cpu_vendor_; } @@ -1792,6 +1794,8 @@ bool MinidumpSystemInfo::Read(u_int32_t expected_size) { // Invalidate cached data. delete csd_version_; csd_version_ = NULL; + delete cpu_vendor_; + cpu_vendor_ = NULL; valid_ = false; @@ -1844,6 +1848,36 @@ const string* MinidumpSystemInfo::GetCSDVersion() { } +const string* MinidumpSystemInfo::GetCPUVendor() { + if (!valid_) + return NULL; + + // CPU vendor information can only be determined from x86 minidumps. + if (!cpu_vendor_ && + (system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86 || + system_info_.processor_architecture == MD_CPU_ARCHITECTURE_X86_WIN64)) { + char cpu_vendor_string[13]; + snprintf(cpu_vendor_string, sizeof(cpu_vendor_string), + "%c%c%c%c%c%c%c%c%c%c%c%c", + system_info_.cpu.x86_cpu_info.vendor_id[0] & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 8) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 16) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[0] >> 24) & 0xff, + system_info_.cpu.x86_cpu_info.vendor_id[1] & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 8) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 16) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[1] >> 24) & 0xff, + system_info_.cpu.x86_cpu_info.vendor_id[2] & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 8) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 16) & 0xff, + (system_info_.cpu.x86_cpu_info.vendor_id[2] >> 24) & 0xff); + cpu_vendor_ = new string(cpu_vendor_string); + } + + return cpu_vendor_; +} + + void MinidumpSystemInfo::Print() { if (!valid_) return; @@ -1881,12 +1915,20 @@ void MinidumpSystemInfo::Print() { system_info_.cpu.x86_cpu_info.feature_information); printf(" cpu.x86_cpu_info.amd_extended_cpu_features = 0x%x\n", system_info_.cpu.x86_cpu_info.amd_extended_cpu_features); - const char* csd_version = GetCSDVersion()->c_str(); - if (csd_version) + const string* csd_version = GetCSDVersion(); + if (csd_version) { printf(" (csd_version) = \"%s\"\n", - csd_version); - else + csd_version->c_str()); + } else { printf(" (csd_version) = (null)\n"); + } + const string* cpu_vendor = GetCPUVendor(); + if (cpu_vendor) { + printf(" (cpu_vendor) = \"%s\"\n", + cpu_vendor->c_str()); + } else { + printf(" (cpu_vendor) = (null)\n"); + } printf("\n"); } diff --git a/src/processor/minidump.h b/src/processor/minidump.h index 1861213d..f67cd35e 100644 --- a/src/processor/minidump.h +++ b/src/processor/minidump.h @@ -550,9 +550,16 @@ class MinidumpSystemInfo : public MinidumpStream { return valid_ ? &system_info_ : 0; } // I don't know what CSD stands for, but this field is documented as - // returning a textual representation of the OS service pack. + // returning a textual representation of the OS service pack. On other + // platforms, this provides additional information about an OS version + // level beyond major.minor.micro. Returns NULL if unknown. const string* GetCSDVersion(); + // If a CPU vendor string can be determined, returns a pointer to it, + // otherwise, returns NULL. CPU vendor strings can be determined from + // x86 CPUs with CPUID 0. + const string* GetCPUVendor(); + // Print a human-readable representation of the object to stdout. void Print(); @@ -569,7 +576,10 @@ class MinidumpSystemInfo : public MinidumpStream { // Textual representation of the OS service pack, for minidumps produced // by MiniDumpWriteDump on Windows. - const string* csd_version_; + const string* csd_version_; + + // A string identifying the CPU vendor, if known. + const string* cpu_vendor_; }; diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc index 8f6497e2..62ad0092 100644 --- a/src/processor/minidump_processor.cc +++ b/src/processor/minidump_processor.cc @@ -28,6 +28,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "google/minidump_processor.h" +#include "google/call_stack.h" +#include "google/process_state.h" #include "processor/minidump.h" #include "processor/scoped_ptr.h" #include "processor/stackwalker_x86.h" @@ -41,15 +43,25 @@ MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier) MinidumpProcessor::~MinidumpProcessor() { } -CallStack* MinidumpProcessor::Process(const string &minidump_file) { +ProcessState* MinidumpProcessor::Process(const string &minidump_file) { Minidump dump(minidump_file); if (!dump.Read()) { return NULL; } + scoped_ptr process_state(new ProcessState()); + + 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; MinidumpException *exception = dump.GetException(); - if (!exception) { - return NULL; + if (exception) { + process_state->crashed_ = true; + exception_thread_id = exception->GetThreadID(); + + process_state->crash_reason_ = GetCrashReason( + &dump, &process_state->crash_address_); } MinidumpThreadList *threads = dump.GetThreadList(); @@ -57,25 +69,301 @@ CallStack* MinidumpProcessor::Process(const string &minidump_file) { return NULL; } - // TODO(bryner): get all the threads - MinidumpThread *thread = threads->GetThreadByID(exception->GetThreadID()); - if (!thread) { + bool found_crash_thread = false; + unsigned int thread_count = threads->thread_count(); + for (unsigned int thread_index = 0; + thread_index < thread_count; + ++thread_index) { + MinidumpThread *thread = threads->GetThreadAtIndex(thread_index); + if (!thread) { + return NULL; + } + + if (process_state->crashed_ && + thread->GetThreadID() == exception_thread_id) { + if (found_crash_thread) { + // There can't be more than one crash thread. + return NULL; + } + + process_state->crash_thread_ = thread_index; + found_crash_thread = true; + } + + MinidumpMemoryRegion *thread_memory = thread->GetMemory(); + if (!thread_memory) { + return NULL; + } + + scoped_ptr stackwalker( + Stackwalker::StackwalkerForCPU(exception->GetContext(), + thread_memory, + dump.GetModuleList(), + supplier_)); + if (!stackwalker.get()) { + return NULL; + } + + scoped_ptr stack(stackwalker->Walk()); + if (!stack.get()) { + return NULL; + } + + process_state->threads_.push_back(stack.release()); + } + + // If the process crashed, there must be a crash thread. + if (process_state->crashed_ && !found_crash_thread) { return NULL; } - MinidumpMemoryRegion *thread_memory = thread->GetMemory(); - if (!thread_memory) { + return process_state.release(); +} + +// Returns the MDRawSystemInfo from a minidump, or NULL if system info is +// not available from the minidump. If system_info is non-NULL, it is used +// to pass back the MinidumpSystemInfo object. +static const MDRawSystemInfo* GetSystemInfo(Minidump *dump, + MinidumpSystemInfo **system_info) { + MinidumpSystemInfo *minidump_system_info = dump->GetSystemInfo(); + if (!minidump_system_info) return NULL; + + if (system_info) + *system_info = minidump_system_info; + + return minidump_system_info->system_info(); +} + +// static +string MinidumpProcessor::GetCPUInfo(Minidump *dump, string *cpu_info) { + if (cpu_info) + cpu_info->clear(); + + MinidumpSystemInfo *system_info; + const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info); + if (!raw_system_info) + return ""; + + string cpu; + switch (raw_system_info->processor_architecture) { + case MD_CPU_ARCHITECTURE_X86: { + cpu = "x86"; + if (cpu_info) { + const string *cpu_vendor = system_info->GetCPUVendor(); + if (cpu_vendor) { + cpu_info->assign(*cpu_vendor); + cpu_info->append(" "); + } + + char x86_info[36]; + snprintf(x86_info, sizeof(x86_info), "family %u model %u stepping %u", + raw_system_info->processor_level, + raw_system_info->processor_revision >> 8, + raw_system_info->processor_revision & 0xff); + cpu_info->append(x86_info); + } + break; + } + + case MD_CPU_ARCHITECTURE_PPC: { + cpu = "ppc"; + break; + } + + default: { + // Assign the numeric architecture ID into the CPU string. + char cpu_string[7]; + snprintf(cpu_string, sizeof(cpu_string), "0x%04x", + raw_system_info->processor_architecture); + cpu = cpu_string; + break; + } } - scoped_ptr walker( - Stackwalker::StackwalkerForCPU(exception->GetContext(), thread_memory, - dump.GetModuleList(), supplier_)); - if (!walker.get()) { - return NULL; + return cpu; +} + +// static +string MinidumpProcessor::GetOSInfo(Minidump *dump, string *os_version) { + if (os_version) + os_version->clear(); + + MinidumpSystemInfo *system_info; + const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, &system_info); + if (!raw_system_info) + return ""; + + string os; + switch (raw_system_info->platform_id) { + case MD_OS_WIN32_NT: { + os = "Windows NT"; + break; + } + + case MD_OS_WIN32_WINDOWS: { + os = "Windows"; + break; + } + + case MD_OS_MAC_OS_X: { + os = "Mac OS X"; + break; + } + + case MD_OS_LINUX: { + os = "Linux"; + break; + } + + default: { + // Assign the numeric platform ID into the OS string. + char os_string[11]; + snprintf(os_string, sizeof(os_string), "0x%08x", + raw_system_info->platform_id); + os = os_string; + break; + } } - return walker->Walk(); + if (os_version) { + char os_version_string[33]; + snprintf(os_version_string, sizeof(os_version_string), "%u.%u.%u", + raw_system_info->major_version, + raw_system_info->minor_version, + raw_system_info->build_number); + os_version->assign(os_version_string); + + const string *csd_version = system_info->GetCSDVersion(); + if (csd_version) { + os_version->append(" "); + os_version->append(*csd_version); + } + } + + return os; +} + +// static +string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) { + MinidumpException *exception = dump->GetException(); + if (!exception) + return ""; + + const MDRawExceptionStream *raw_exception = exception->exception(); + if (!raw_exception) + return ""; + + if (address) + *address = raw_exception->exception_record.exception_address; + + // The reason value is OS-specific and possibly CPU-specific. Set up + // sensible numeric defaults for the reason string in case we can't + // map the codes to a string (because there's no system info, or because + // it's an unrecognized platform, or because it's an unrecognized code.) + char reason_string[24]; + snprintf(reason_string, sizeof(reason_string), "0x%08x / 0x%08x", + raw_exception->exception_record.exception_code, + raw_exception->exception_record.exception_flags); + string reason = reason_string; + + const MDRawSystemInfo *raw_system_info = GetSystemInfo(dump, NULL); + if (!raw_system_info) + return reason; + + switch(raw_system_info->platform_id) { + case MD_OS_WIN32_NT: + case MD_OS_WIN32_WINDOWS: { + switch (raw_exception->exception_record.exception_code) { + case MD_EXCEPTION_CODE_WIN_CONTROL_C: + reason = "DBG_CONTROL_C"; + break; + case MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION: + reason = "EXCEPTION_GUARD_PAGE"; + break; + case MD_EXCEPTION_CODE_WIN_DATATYPE_MISALIGNMENT: + reason = "EXCEPTION_DATATYPE_MISALIGNMENT"; + break; + case MD_EXCEPTION_CODE_WIN_BREAKPOINT: + reason = "EXCEPTION_BREAKPOINT"; + break; + case MD_EXCEPTION_CODE_WIN_SINGLE_STEP: + reason = "EXCEPTION_SINGLE_STEP"; + break; + case MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION: + // For EXCEPTION_ACCESS_VIOLATION, Windows puts the address that + // caused the fault in exception_information[1]. + // exception_information[0] is 0 if the violation was caused by + // an attempt to read data and 1 if it was an attempt to write + // data. + // This information is useful in addition to the code address, which + // will be present in the crash thread's instruction field anyway. + reason = "EXCEPTION_ACCESS_VIOLATION"; + if (address && + raw_exception->exception_record.number_parameters >= 2) { + *address = + raw_exception->exception_record.exception_information[1]; + } + break; + case MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR: + reason = "EXCEPTION_IN_PAGE_ERROR"; + break; + case MD_EXCEPTION_CODE_WIN_INVALID_HANDLE: + reason = "EXCEPTION_INVALID_HANDLE"; + break; + case MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION: + reason = "EXCEPTION_ILLEGAL_INSTRUCTION"; + break; + case MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION: + reason = "EXCEPTION_NONCONTINUABLE_EXCEPTION"; + break; + case MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION: + reason = "EXCEPTION_INVALID_DISPOSITION"; + break; + case MD_EXCEPTION_CODE_WIN_ARRAY_BOUNDS_EXCEEDED: + reason = "EXCEPTION_BOUNDS_EXCEEDED"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_DENORMAL_OPERAND: + reason = "EXCEPTION_FLT_DENORMAL_OPERAND"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO: + reason = "EXCEPTION_FLT_DIVIDE_BY_ZERO"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT: + reason = "EXCEPTION_FLT_INEXACT_RESULT"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION: + reason = "EXCEPTION_FLT_INVALID_OPERATION"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW: + reason = "EXCEPTION_FLT_OVERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_STACK_CHECK: + reason = "EXCEPTION_FLT_STACK_CHECK"; + break; + case MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW: + reason = "EXCEPTION_FLT_UNDERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO: + reason = "EXCEPTION_INT_DIVIDE_BY_ZERO"; + break; + case MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW: + reason = "EXCEPTION_INT_OVERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION: + reason = "EXCEPTION_PRIV_INSTRUCTION"; + break; + case MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW: + reason = "EXCEPTION_STACK_OVERFLOW"; + break; + case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK: + reason = "EXCEPTION_POSSIBLE_DEADLOCK"; + break; + } + } + } + + return reason; } } // namespace google_airbag diff --git a/src/processor/minidump_processor_unittest.cc b/src/processor/minidump_processor_unittest.cc index 283b67ed..b3c69930 100644 --- a/src/processor/minidump_processor_unittest.cc +++ b/src/processor/minidump_processor_unittest.cc @@ -33,6 +33,7 @@ #include #include "google/call_stack.h" #include "google/minidump_processor.h" +#include "google/process_state.h" #include "google/stack_frame.h" #include "google/symbol_supplier.h" #include "processor/minidump.h" @@ -41,6 +42,7 @@ using std::string; using google_airbag::CallStack; using google_airbag::MinidumpProcessor; +using google_airbag::ProcessState; using google_airbag::scoped_ptr; #define ASSERT_TRUE(cond) \ @@ -79,8 +81,19 @@ static bool RunTests() { string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + "/src/processor/testdata/minidump2.dmp"; - scoped_ptr stack(processor.Process(minidump_file)); - ASSERT_TRUE(stack.get()); + scoped_ptr state(processor.Process(minidump_file)); + ASSERT_TRUE(state.get()); + ASSERT_EQ(state->cpu(), "x86"); + ASSERT_EQ(state->cpu_info(), "GenuineIntel family 6 model 13 stepping 8"); + ASSERT_EQ(state->os(), "Windows NT"); + 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->threads()->size(), 1); + ASSERT_EQ(state->crash_thread(), 0); + CallStack *stack = state->threads()->at(0); + ASSERT_TRUE(stack); ASSERT_EQ(stack->frames()->size(), 4); ASSERT_EQ(stack->frames()->at(0)->module_base, 0x400000); diff --git a/src/processor/process_state.cc b/src/processor/process_state.cc new file mode 100644 index 00000000..0099af15 --- /dev/null +++ b/src/processor/process_state.cc @@ -0,0 +1,49 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// process_state.cc: A snapshot of a process, in a fully-digested state. +// +// See process_state.h for documentation. +// +// Author: Mark Mentovai + +#include "google/process_state.h" +#include "google/call_stack.h" + +namespace google_airbag { + +ProcessState::~ProcessState() { + for (vector::const_iterator iterator = threads_.begin(); + iterator != threads_.end(); + ++iterator) { + delete *iterator; + } +} + +} // namespace google_airbag diff --git a/src/processor/testdata/minidump1.out b/src/processor/testdata/minidump1.out index bd9c29b2..bb96de00 100644 --- a/src/processor/testdata/minidump1.out +++ b/src/processor/testdata/minidump1.out @@ -3725,6 +3725,7 @@ MDRawSystemInfo cpu.x86_cpu_info.feature_information = 0xafe9fbff cpu.x86_cpu_info.amd_extended_cpu_features = 0xffffffff (csd_version) = "Service Pack 2" + (cpu_vendor) = "GenuineIntel" MDRawMiscInfo size_of_info = 24 flags1 = 0x3