aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/breakpad_googletest_includes.h36
-rw-r--r--src/google_breakpad/processor/minidump.h15
-rw-r--r--src/google_breakpad/processor/minidump_processor.h71
-rw-r--r--src/processor/minidump_processor.cc172
-rw-r--r--src/processor/minidump_processor_unittest.cc81
-rw-r--r--src/processor/minidump_stackwalk.cc2
6 files changed, 259 insertions, 118 deletions
diff --git a/src/breakpad_googletest_includes.h b/src/breakpad_googletest_includes.h
new file mode 100644
index 00000000..48a4cc1b
--- /dev/null
+++ b/src/breakpad_googletest_includes.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2009, 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.
+
+#ifndef BREAKPAD_GOOGLETEST_INCLUDES_H__
+#define BREAKPAD_GOOGLETEST_INCLUDES_H__
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/include/gmock/gmock.h"
+
+#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__
diff --git a/src/google_breakpad/processor/minidump.h b/src/google_breakpad/processor/minidump.h
index 5c5dccc9..f08a1c18 100644
--- a/src/google_breakpad/processor/minidump.h
+++ b/src/google_breakpad/processor/minidump.h
@@ -336,7 +336,9 @@ class MinidumpThreadList : public MinidumpStream {
}
static u_int32_t max_threads() { return max_threads_; }
- unsigned int thread_count() const { return valid_ ? thread_count_ : 0; }
+ unsigned int thread_count() const {
+ return valid_ ? thread_count_ : 0;
+ }
// Sequential access to threads.
MinidumpThread* GetThreadAtIndex(unsigned int index) const;
@@ -755,8 +757,11 @@ class Minidump {
// path is the pathname of a file containing the minidump.
explicit Minidump(const string& path);
- ~Minidump();
+ virtual ~Minidump();
+ virtual string path() const {
+ return path_;
+ }
static void set_max_streams(u_int32_t max_streams) {
max_streams_ = max_streams;
}
@@ -767,19 +772,19 @@ class Minidump {
}
static u_int32_t max_string_length() { return max_string_length_; }
- const MDRawHeader* header() const { return valid_ ? &header_ : NULL; }
+ virtual 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
// header. Read() sets up the stream list and map, and validates the
// Minidump object.
- bool Read();
+ virtual bool Read();
// 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).
- MinidumpThreadList* GetThreadList();
+ virtual MinidumpThreadList* GetThreadList();
MinidumpModuleList* GetModuleList();
MinidumpMemoryList* GetMemoryList();
MinidumpException* GetException();
diff --git a/src/google_breakpad/processor/minidump_processor.h b/src/google_breakpad/processor/minidump_processor.h
index 73447f97..f313bf71 100644
--- a/src/google_breakpad/processor/minidump_processor.h
+++ b/src/google_breakpad/processor/minidump_processor.h
@@ -30,6 +30,7 @@
#ifndef GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__
#define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__
+#include <cassert>
#include <string>
#include "google_breakpad/common/breakpad_types.h"
@@ -42,16 +43,53 @@ class ProcessState;
class SourceLineResolverInterface;
class SymbolSupplier;
class SystemInfo;
+// Return type for Process()
+enum ProcessResult {
+ PROCESS_OK, // The minidump was
+ // processed
+ // successfully.
+
+ PROCESS_ERROR_MINIDUMP_NOT_FOUND, // The minidump file
+ // was not found.
+
+ PROCESS_ERROR_NO_MINIDUMP_HEADER, // The minidump file
+ // had no header
+
+ PROCESS_ERROR_NO_THREAD_LIST, // The minidump file
+ // had no thread list.
+
+ PROCESS_ERROR_GETTING_THREAD, // There was an error
+ // getting one
+ // thread's data from
+ // the minidump.
+
+ PROCESS_ERROR_GETTING_THREAD_ID, // There was an error
+ // getting a thread id
+ // from the thread's
+ // data.
+
+ PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than
+ // one requesting
+ // thread.
+
+ PROCESS_ERROR_NO_MEMORY_FOR_THREAD, // A thread had no
+ // memory region.
+
+ PROCESS_ERROR_NO_STACKWALKER_FOR_THREAD, // We couldn't
+ // determine the
+ // StackWalker to walk
+ // the minidump's
+ // threads.
+
+ PROCESS_SYMBOL_SUPPLIER_INTERRUPTED // The minidump
+ // processing was
+ // interrupted by the
+ // SymbolSupplier(not
+ // fatal)
+};
class MinidumpProcessor {
public:
- // Return type for Process()
- enum ProcessResult {
- PROCESS_OK, // the minidump was processed successfully
- PROCESS_ERROR, // there was an error processing the minidump
- PROCESS_INTERRUPTED // processing was interrupted by the SymbolSupplier
- };
-
// Initializes this MinidumpProcessor. supplier should be an
// implementation of the SymbolSupplier abstract base class.
MinidumpProcessor(SymbolSupplier *supplier,
@@ -62,6 +100,10 @@ class MinidumpProcessor {
ProcessResult Process(const string &minidump_file,
ProcessState *process_state);
+ // Processes the minidump structure and fills process_state with the
+ // result.
+ ProcessResult Process(Minidump *minidump,
+ ProcessState *process_state);
// Populates the cpu_* fields of the |info| parameter with textual
// representations of the CPU type that the minidump in |dump| was
// produced on. Returns false if this information is not available in
@@ -84,6 +126,21 @@ class MinidumpProcessor {
// was caused by a memory access violation.
static string GetCrashReason(Minidump *dump, u_int64_t *address);
+ // This function returns true if the passed-in error code is
+ // something unrecoverable(i.e. retry should not happen). For
+ // instance, if the minidump is corrupt, then it makes no sense to
+ // retry as we won't be able to glean additional information.
+ // However, as an example of the other case, the symbol supplier can
+ // return an error code indicating it was 'interrupted', which can
+ // happen of the symbols are fetched from a remote store, and a
+ // retry might be successful later on.
+ // You should not call this method with PROCESS_OK! Test for
+ // that separately before calling this.
+ static bool IsErrorUnrecoverable(ProcessResult p) {
+ assert(p != PROCESS_OK);
+ return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED);
+ }
+
private:
SymbolSupplier *supplier_;
SourceLineResolverInterface *resolver_;
diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
index 3c609170..5ca41655 100644
--- a/src/processor/minidump_processor.cc
+++ b/src/processor/minidump_processor.cc
@@ -47,48 +47,45 @@ MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier,
MinidumpProcessor::~MinidumpProcessor() {
}
-MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
- const string &minidump_file, ProcessState *process_state) {
- BPLOG(INFO) << "Processing minidump in file " << minidump_file;
-
- Minidump dump(minidump_file);
- if (!dump.Read()) {
- BPLOG(ERROR) << "Minidump " << minidump_file << " could not be read";
- return PROCESS_ERROR;
- }
+ProcessResult MinidumpProcessor::Process(
+ Minidump *dump, ProcessState *process_state) {
+ assert(dump);
+ assert(process_state);
process_state->Clear();
- const MDRawHeader *header = dump.header();
- BPLOG_IF(ERROR, !header) << "Minidump " << minidump_file << " has no header";
- assert(header);
+ const MDRawHeader *header = dump->header();
+ if (!header) {
+ BPLOG(ERROR) << "Minidump " << dump->path() << " has no header";
+ return PROCESS_ERROR_NO_MINIDUMP_HEADER;
+ }
process_state->time_date_stamp_ = header->time_date_stamp;
- bool has_cpu_info = GetCPUInfo(&dump, &process_state->system_info_);
- bool has_os_info = GetOSInfo(&dump, &process_state->system_info_);
+ bool has_cpu_info = GetCPUInfo(dump, &process_state->system_info_);
+ bool has_os_info = GetOSInfo(dump, &process_state->system_info_);
u_int32_t dump_thread_id = 0;
bool has_dump_thread = false;
u_int32_t requesting_thread_id = 0;
bool has_requesting_thread = false;
- MinidumpBreakpadInfo *breakpad_info = dump.GetBreakpadInfo();
+ MinidumpBreakpadInfo *breakpad_info = dump->GetBreakpadInfo();
if (breakpad_info) {
has_dump_thread = breakpad_info->GetDumpThreadID(&dump_thread_id);
has_requesting_thread =
breakpad_info->GetRequestingThreadID(&requesting_thread_id);
}
- MinidumpException *exception = dump.GetException();
+ MinidumpException *exception = dump->GetException();
if (exception) {
process_state->crashed_ = true;
has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
process_state->crash_reason_ = GetCrashReason(
- &dump, &process_state->crash_address_);
+ dump, &process_state->crash_address_);
}
- MinidumpModuleList *module_list = dump.GetModuleList();
+ MinidumpModuleList *module_list = dump->GetModuleList();
// Put a copy of the module list into ProcessState object. This is not
// necessarily a MinidumpModuleList, but it adheres to the CodeModules
@@ -96,21 +93,21 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
if (module_list)
process_state->modules_ = module_list->Copy();
- MinidumpThreadList *threads = dump.GetThreadList();
+ MinidumpThreadList *threads = dump->GetThreadList();
if (!threads) {
- BPLOG(ERROR) << "Minidump " << minidump_file << " has no thread list";
- return PROCESS_ERROR;
+ BPLOG(ERROR) << "Minidump " << dump->path() << " has no thread list";
+ return PROCESS_ERROR_NO_THREAD_LIST;
}
- BPLOG(INFO) << "Minidump " << minidump_file << " has " <<
- (has_cpu_info ? "" : "no ") << "CPU info, " <<
- (has_os_info ? "" : "no ") << "OS info, " <<
- (breakpad_info != NULL ? "" : "no ") << "Breakpad info, " <<
- (exception != NULL ? "" : "no ") << "exception, " <<
- (module_list != NULL ? "" : "no ") << "module list, " <<
- (threads != NULL ? "" : "no ") << "thread list, " <<
- (has_dump_thread ? "" : "no ") << "dump thread, and " <<
- (has_requesting_thread ? "" : "no ") << "requesting thread";
+ BPLOG(INFO) << "Minidump " << dump->path() << " has " <<
+ (has_cpu_info ? "" : "no ") << "CPU info, " <<
+ (has_os_info ? "" : "no ") << "OS info, " <<
+ (breakpad_info != NULL ? "" : "no ") << "Breakpad info, " <<
+ (exception != NULL ? "" : "no ") << "exception, " <<
+ (module_list != NULL ? "" : "no ") << "module list, " <<
+ (threads != NULL ? "" : "no ") << "thread list, " <<
+ (has_dump_thread ? "" : "no ") << "dump thread, and " <<
+ (has_requesting_thread ? "" : "no ") << "requesting thread";
bool interrupted = false;
bool found_requesting_thread = false;
@@ -121,18 +118,18 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
char thread_string_buffer[64];
snprintf(thread_string_buffer, sizeof(thread_string_buffer), "%d/%d",
thread_index, thread_count);
- string thread_string = minidump_file + ":" + thread_string_buffer;
+ string thread_string = dump->path() + ":" + thread_string_buffer;
MinidumpThread *thread = threads->GetThreadAtIndex(thread_index);
if (!thread) {
BPLOG(ERROR) << "Could not get thread for " << thread_string;
- return PROCESS_ERROR;
+ return PROCESS_ERROR_GETTING_THREAD;
}
u_int32_t thread_id;
if (!thread->GetThreadID(&thread_id)) {
BPLOG(ERROR) << "Could not get thread ID for " << thread_string;
- return PROCESS_ERROR;
+ return PROCESS_ERROR_GETTING_THREAD_ID;
}
thread_string += " id " + HexString(thread_id);
@@ -152,7 +149,7 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
if (found_requesting_thread) {
// There can't be more than one requesting thread.
BPLOG(ERROR) << "Duplicate requesting thread: " << thread_string;
- return PROCESS_ERROR;
+ return PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS;
}
// Use processed_state->threads_.size() instead of thread_index.
@@ -179,7 +176,7 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
if (!thread_memory) {
BPLOG(ERROR) << "No memory region for " << thread_string;
- return PROCESS_ERROR;
+ return PROCESS_ERROR_NO_MEMORY_FOR_THREAD;
}
// Use process_state->modules_ instead of module_list, because the
@@ -199,36 +196,49 @@ MinidumpProcessor::ProcessResult MinidumpProcessor::Process(
resolver_));
if (!stackwalker.get()) {
BPLOG(ERROR) << "No stackwalker for " << thread_string;
- return PROCESS_ERROR;
+ return PROCESS_ERROR_NO_STACKWALKER_FOR_THREAD;
}
scoped_ptr<CallStack> stack(new CallStack());
if (!stackwalker->Walk(stack.get())) {
BPLOG(INFO) << "Stackwalker interrupt (missing symbols?) at " <<
- thread_string;
+ thread_string;
interrupted = true;
}
process_state->threads_.push_back(stack.release());
}
if (interrupted) {
- BPLOG(INFO) << "Processing interrupted for " << minidump_file;
- return PROCESS_INTERRUPTED;
+ BPLOG(INFO) << "Processing interrupted for " << dump->path();
+ return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED;
}
// If a requesting thread was indicated, it must be present.
if (has_requesting_thread && !found_requesting_thread) {
// Don't mark as an error, but invalidate the requesting thread
BPLOG(ERROR) << "Minidump indicated requesting thread " <<
- HexString(requesting_thread_id) << ", not found in " <<
- minidump_file;
+ HexString(requesting_thread_id) << ", not found in " <<
+ dump->path();
process_state->requesting_thread_ = -1;
}
- BPLOG(INFO) << "Processed " << minidump_file;
+ BPLOG(INFO) << "Processed " << dump->path();
return PROCESS_OK;
}
+ProcessResult MinidumpProcessor::Process(
+ const string &minidump_file, ProcessState *process_state) {
+ BPLOG(INFO) << "Processing minidump in file " << minidump_file;
+
+ Minidump dump(minidump_file);
+ if (!dump.Read()) {
+ BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read";
+ return PROCESS_ERROR_MINIDUMP_NOT_FOUND;
+ }
+
+ return Process(&dump, process_state);
+}
+
// 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.
@@ -260,7 +270,7 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
switch (raw_system_info->processor_architecture) {
case MD_CPU_ARCHITECTURE_X86:
case MD_CPU_ARCHITECTURE_AMD64: {
- if (raw_system_info->processor_architecture ==
+ if (raw_system_info->processor_architecture ==
MD_CPU_ARCHITECTURE_X86)
info->cpu = "x86";
else
@@ -861,118 +871,118 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, u_int64_t *address) {
break;
case MD_EXCEPTION_CODE_SOL_SIGQUIT:
reason = "SIGQUIT";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGILL:
reason = "SIGILL";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGTRAP:
reason = "SIGTRAP";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGIOT:
reason = "SIGIOT | SIGABRT";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGEMT:
reason = "SIGEMT";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGFPE:
reason = "SIGFPE";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGKILL:
reason = "SIGKILL";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGBUS:
reason = "SIGBUS";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGSEGV:
reason = "SIGSEGV";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGSYS:
reason = "SIGSYS";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGPIPE:
reason = "SIGPIPE";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGALRM:
reason = "SIGALRM";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGTERM:
reason = "SIGTERM";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGUSR1:
reason = "SIGUSR1";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGUSR2:
reason = "SIGUSR2";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGCLD:
reason = "SIGCLD | SIGCHLD";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGPWR:
reason = "SIGPWR";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGWINCH:
reason = "SIGWINCH";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGURG:
reason = "SIGURG";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGPOLL:
reason = "SIGPOLL | SIGIO";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGSTOP:
reason = "SIGSTOP";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGTSTP:
reason = "SIGTSTP";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGCONT:
reason = "SIGCONT";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGTTIN:
reason = "SIGTTIN";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGTTOU:
reason = "SIGTTOU";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGVTALRM:
reason = "SIGVTALRM";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGPROF:
reason = "SIGPROF";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGXCPU:
reason = "SIGXCPU";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGXFSZ:
reason = "SIGXFSZ";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGWAITING:
reason = "SIGWAITING";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGLWP:
reason = "SIGLWP";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGFREEZE:
reason = "SIGFREEZE";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGTHAW:
reason = "SIGTHAW";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGCANCEL:
reason = "SIGCANCEL";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGLOST:
reason = "SIGLOST";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGXRES:
reason = "SIGXRES";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGJVM1:
reason = "SIGJVM1";
- break;
+ break;
case MD_EXCEPTION_CODE_SOL_SIGJVM2:
reason = "SIGJVM2";
- break;
+ break;
default:
BPLOG(INFO) << "Unknown exception reason " << reason;
break;
diff --git a/src/processor/minidump_processor_unittest.cc b/src/processor/minidump_processor_unittest.cc
index bf8a8989..9d63ed8a 100644
--- a/src/processor/minidump_processor_unittest.cc
+++ b/src/processor/minidump_processor_unittest.cc
@@ -34,10 +34,12 @@
#include <string>
#include <iostream>
#include <fstream>
+#include "breakpad_googletest_includes.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/code_modules.h"
+#include "google_breakpad/processor/minidump.h"
#include "google_breakpad/processor/minidump_processor.h"
#include "google_breakpad/processor/process_state.h"
#include "google_breakpad/processor/stack_frame.h"
@@ -45,17 +47,34 @@
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
+namespace google_breakpad {
+class MockMinidump : public Minidump {
+ public:
+ MockMinidump() : Minidump("") {
+ }
+
+ MOCK_METHOD0(Read,bool());
+ MOCK_CONST_METHOD0(path, string());
+ MOCK_CONST_METHOD0(header,const MDRawHeader*());
+ MOCK_METHOD0(GetThreadList,MinidumpThreadList*());
+};
+}
+
namespace {
-using std::string;
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::CodeModule;
using google_breakpad::MinidumpProcessor;
+using google_breakpad::MinidumpThreadList;
+using google_breakpad::MinidumpThread;
+using google_breakpad::MockMinidump;
using google_breakpad::ProcessState;
using google_breakpad::scoped_ptr;
using google_breakpad::SymbolSupplier;
using google_breakpad::SystemInfo;
+using std::string;
+using ::testing::Return;
static const char *kSystemInfoOS = "Windows NT";
static const char *kSystemInfoOSShort = "windows";
@@ -64,17 +83,6 @@ static const char *kSystemInfoCPU = "x86";
static const char *kSystemInfoCPUInfo =
"GenuineIntel family 6 model 13 stepping 8";
-#define ASSERT_TRUE(cond) \
- if (!(cond)) { \
- fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
- return false; \
- }
-
-#define ASSERT_FALSE(cond) ASSERT_TRUE(!(cond))
-
-#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
-
-// Use ASSERT_*_ABORT in functions that can't return a boolean.
#define ASSERT_TRUE_ABORT(cond) \
if (!(cond)) { \
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
@@ -147,7 +155,38 @@ SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
return s;
}
-static bool RunTests() {
+
+class MinidumpProcessorTest : public ::testing::Test {
+
+};
+
+TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) {
+ MockMinidump dump;
+ TestSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ MinidumpProcessor processor(&supplier, &resolver);
+ ProcessState state;
+
+ EXPECT_EQ(processor.Process("nonexistant minidump", &state),
+ google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND);
+
+ EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
+ EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
+
+ MDRawHeader fakeHeader;
+ fakeHeader.time_date_stamp = 0;
+ EXPECT_CALL(dump, header()).WillOnce(Return((MDRawHeader*)NULL)).
+ WillRepeatedly(Return(&fakeHeader));
+ EXPECT_EQ(processor.Process(&dump, &state),
+ google_breakpad::PROCESS_ERROR_NO_MINIDUMP_HEADER);
+
+ EXPECT_CALL(dump, GetThreadList()).
+ WillOnce(Return((MinidumpThreadList*)NULL));
+ EXPECT_EQ(processor.Process(&dump, &state),
+ google_breakpad::PROCESS_ERROR_NO_THREAD_LIST);
+}
+
+TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
TestSymbolSupplier supplier;
BasicSourceLineResolver resolver;
MinidumpProcessor processor(&supplier, &resolver);
@@ -157,7 +196,7 @@ static bool RunTests() {
ProcessState state;
ASSERT_EQ(processor.Process(minidump_file, &state),
- MinidumpProcessor::PROCESS_OK);
+ google_breakpad::PROCESS_OK);
ASSERT_EQ(state.system_info()->os, kSystemInfoOS);
ASSERT_EQ(state.system_info()->os_short, kSystemInfoOSShort);
ASSERT_EQ(state.system_info()->os_version, kSystemInfoOSVersion);
@@ -221,19 +260,13 @@ static bool RunTests() {
state.Clear();
supplier.set_interrupt(true);
ASSERT_EQ(processor.Process(minidump_file, &state),
- MinidumpProcessor::PROCESS_INTERRUPTED);
-
- return true;
+ google_breakpad::PROCESS_SYMBOL_SUPPLIER_INTERRUPTED
+ );
}
-
} // namespace
int main(int argc, char *argv[]) {
BPLOG_INIT(&argc, &argv);
-
- if (!RunTests()) {
- return 1;
- }
-
- return 0;
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
index 446d1576..d3a830e3 100644
--- a/src/processor/minidump_stackwalk.cc
+++ b/src/processor/minidump_stackwalk.cc
@@ -447,7 +447,7 @@ static bool PrintMinidumpProcess(const string &minidump_file,
// Process the minidump.
ProcessState process_state;
if (minidump_processor.Process(minidump_file, &process_state) !=
- MinidumpProcessor::PROCESS_OK) {
+ google_breakpad::PROCESS_OK) {
BPLOG(ERROR) << "MinidumpProcessor::Process failed";
return false;
}