aboutsummaryrefslogtreecommitdiff
path: root/src/processor
diff options
context:
space:
mode:
Diffstat (limited to 'src/processor')
-rw-r--r--src/processor/call_stack.cc1
-rw-r--r--src/processor/minidump.cc52
-rw-r--r--src/processor/minidump.h14
-rw-r--r--src/processor/minidump_processor.cc316
-rw-r--r--src/processor/minidump_processor_unittest.cc17
-rw-r--r--src/processor/process_state.cc49
-rw-r--r--src/processor/testdata/minidump1.out1
7 files changed, 426 insertions, 24 deletions
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<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
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 <string>
#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<CallStack> stack(processor.Process(minidump_file));
- ASSERT_TRUE(stack.get());
+ scoped_ptr<ProcessState> 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<CallStack *>::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