diff options
Diffstat (limited to 'src/processor/minidump_processor.cc')
-rw-r--r-- | src/processor/minidump_processor.cc | 316 |
1 files changed, 302 insertions, 14 deletions
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<ProcessState> 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) { - return NULL; + 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( + Stackwalker::StackwalkerForCPU(exception->GetContext(), + thread_memory, + dump.GetModuleList(), + supplier_)); + if (!stackwalker.get()) { + return NULL; + } + + scoped_ptr<CallStack> stack(stackwalker->Walk()); + if (!stack.get()) { + return NULL; + } + + process_state->threads_.push_back(stack.release()); } - MinidumpMemoryRegion *thread_memory = thread->GetMemory(); - if (!thread_memory) { + // If the process crashed, there must be a crash thread. + if (process_state->crashed_ && !found_crash_thread) { return NULL; } - scoped_ptr<Stackwalker> walker( - Stackwalker::StackwalkerForCPU(exception->GetContext(), thread_memory, - dump.GetModuleList(), supplier_)); - if (!walker.get()) { + 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; + } + } + + 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; + } + } + + 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 walker->Walk(); + return reason; } } // namespace google_airbag |