aboutsummaryrefslogtreecommitdiff
path: root/src/processor
diff options
context:
space:
mode:
authorted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-11-06 16:50:01 +0000
committerted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2012-11-06 16:50:01 +0000
commitfc6f700bb56d25919e40b13bd141832dcb8c6a7f (patch)
tree634325dd248493ef63784b3e2593b36236fff85a /src/processor
parentWrong %ebp after skipping a frame for which the instruction pointer is not in... (diff)
downloadbreakpad-fc6f700bb56d25919e40b13bd141832dcb8c6a7f.tar.xz
Allow processing dumps with missing stack memory for some threads
r=mkrebs at https://breakpad.appspot.com/413002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1077 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor')
-rw-r--r--src/processor/minidump.cc8
-rw-r--r--src/processor/minidump_processor.cc18
-rw-r--r--src/processor/minidump_processor_unittest.cc209
-rw-r--r--src/processor/minidump_stackwalk.cc3
-rw-r--r--src/processor/minidump_unittest.cc87
-rw-r--r--src/processor/stackwalker_amd64.cc4
-rw-r--r--src/processor/stackwalker_amd64_unittest.cc18
-rw-r--r--src/processor/stackwalker_arm.cc4
-rw-r--r--src/processor/stackwalker_arm_unittest.cc15
-rw-r--r--src/processor/stackwalker_ppc.cc6
-rw-r--r--src/processor/stackwalker_sparc.cc4
-rw-r--r--src/processor/stackwalker_x86.cc6
-rw-r--r--src/processor/stackwalker_x86_unittest.cc17
-rw-r--r--src/processor/synth_minidump.h2
14 files changed, 360 insertions, 41 deletions
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
index 19ba0354..0c13e00e 100644
--- a/src/processor/minidump.cc
+++ b/src/processor/minidump.cc
@@ -1354,15 +1354,15 @@ bool MinidumpThread::Read() {
if (thread_.stack.memory.data_size == 0 ||
thread_.stack.memory.data_size > numeric_limits<u_int64_t>::max() -
thread_.stack.start_of_memory_range) {
+ // This is ok, but log an error anyway.
BPLOG(ERROR) << "MinidumpThread has a memory region problem, " <<
HexString(thread_.stack.start_of_memory_range) << "+" <<
HexString(thread_.stack.memory.data_size);
- return false;
+ } else {
+ memory_ = new MinidumpMemoryRegion(minidump_);
+ memory_->SetDescriptor(&thread_.stack);
}
- memory_ = new MinidumpMemoryRegion(minidump_);
- memory_->SetDescriptor(&thread_.stack);
-
valid_ = true;
return true;
}
diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
index a197cd10..db5b271a 100644
--- a/src/processor/minidump_processor.cc
+++ b/src/processor/minidump_processor.cc
@@ -208,7 +208,6 @@ ProcessResult MinidumpProcessor::Process(
MinidumpMemoryRegion *thread_memory = thread->GetMemory();
if (!thread_memory) {
BPLOG(ERROR) << "No memory region for " << thread_string;
- return PROCESS_ERROR_NO_MEMORY_FOR_THREAD;
}
// Use process_state->modules_ instead of module_list, because the
@@ -225,16 +224,19 @@ ProcessResult MinidumpProcessor::Process(
thread_memory,
process_state->modules_,
frame_symbolizer_));
- if (!stackwalker.get()) {
- BPLOG(ERROR) << "No stackwalker for " << thread_string;
- 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 " <<
+ if (stackwalker.get()) {
+ if (!stackwalker->Walk(stack.get())) {
+ BPLOG(INFO) << "Stackwalker interrupt (missing symbols?) at " <<
thread_string;
- interrupted = true;
+ interrupted = true;
+ }
+ } else {
+ // Threads with missing CPU contexts will hit this, but
+ // don't abort processing the rest of the dump just for
+ // one bad thread.
+ BPLOG(ERROR) << "No stackwalker for " << thread_string;
}
process_state->threads_.push_back(stack.release());
process_state->thread_memory_regions_.push_back(thread_memory);
diff --git a/src/processor/minidump_processor_unittest.cc b/src/processor/minidump_processor_unittest.cc
index a8e1208e..db96efb7 100644
--- a/src/processor/minidump_processor_unittest.cc
+++ b/src/processor/minidump_processor_unittest.cc
@@ -51,6 +51,7 @@
#include "google_breakpad/processor/symbol_supplier.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
+#include "processor/stackwalker_unittest_utils.h"
using std::map;
@@ -64,27 +65,86 @@ class MockMinidump : public Minidump {
MOCK_CONST_METHOD0(path, string());
MOCK_CONST_METHOD0(header, const MDRawHeader*());
MOCK_METHOD0(GetThreadList, MinidumpThreadList*());
+ MOCK_METHOD0(GetSystemInfo, MinidumpSystemInfo*());
+ MOCK_METHOD0(GetBreakpadInfo, MinidumpBreakpadInfo*());
+ MOCK_METHOD0(GetException, MinidumpException*());
+ MOCK_METHOD0(GetAssertion, MinidumpAssertion*());
+ MOCK_METHOD0(GetModuleList, MinidumpModuleList*());
};
-}
+
+class MockMinidumpThreadList : public MinidumpThreadList {
+ public:
+ MockMinidumpThreadList() : MinidumpThreadList(NULL) {}
+
+ MOCK_CONST_METHOD0(thread_count, unsigned int());
+ MOCK_CONST_METHOD1(GetThreadAtIndex, MinidumpThread*(unsigned int));
+};
+
+class MockMinidumpThread : public MinidumpThread {
+ public:
+ MockMinidumpThread() : MinidumpThread(NULL) {}
+
+ MOCK_CONST_METHOD1(GetThreadID, bool(u_int32_t*));
+ MOCK_METHOD0(GetContext, MinidumpContext*());
+ MOCK_METHOD0(GetMemory, MinidumpMemoryRegion*());
+};
+
+// This is crappy, but MinidumpProcessor really does want a
+// MinidumpMemoryRegion.
+class MockMinidumpMemoryRegion : public MinidumpMemoryRegion {
+ public:
+ MockMinidumpMemoryRegion(u_int64_t base, const string& contents) :
+ MinidumpMemoryRegion(NULL) {
+ region_.Init(base, contents);
+ }
+
+ u_int64_t GetBase() const { return region_.GetBase(); }
+ u_int32_t GetSize() const { return region_.GetSize(); }
+
+ bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
+ return region_.GetMemoryAtAddress(address, value);
+ }
+ bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
+ return region_.GetMemoryAtAddress(address, value);
+ }
+ bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
+ return region_.GetMemoryAtAddress(address, value);
+ }
+ bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
+ return region_.GetMemoryAtAddress(address, value);
+ }
+
+ MockMemoryRegion region_;
+};
+
+} // namespace google_breakpad
namespace {
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::CodeModule;
+using google_breakpad::MinidumpContext;
+using google_breakpad::MinidumpMemoryRegion;
using google_breakpad::MinidumpProcessor;
+using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpThreadList;
using google_breakpad::MinidumpThread;
using google_breakpad::MockMinidump;
+using google_breakpad::MockMinidumpMemoryRegion;
+using google_breakpad::MockMinidumpThread;
+using google_breakpad::MockMinidumpThreadList;
using google_breakpad::ProcessState;
using google_breakpad::scoped_ptr;
using google_breakpad::SymbolSupplier;
using google_breakpad::SystemInfo;
using ::testing::_;
+using ::testing::DoAll;
using ::testing::Mock;
using ::testing::Ne;
using ::testing::Property;
using ::testing::Return;
+using ::testing::SetArgumentPointee;
static const char *kSystemInfoOS = "Windows NT";
static const char *kSystemInfoOSShort = "windows";
@@ -206,23 +266,27 @@ void TestSymbolSupplier::FreeSymbolData(const CodeModule *module) {
}
}
-// A mock symbol supplier that always returns NOT_FOUND; one current
-// use for testing the processor's caching of symbol lookups.
-class MockSymbolSupplier : public SymbolSupplier {
+// A test system info stream, just returns values from the
+// MDRawSystemInfo fed to it.
+class TestMinidumpSystemInfo : public MinidumpSystemInfo {
public:
- MockSymbolSupplier() { }
- MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule*,
- const SystemInfo*,
- string*));
- MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule*,
- const SystemInfo*,
- string*,
- string*));
- MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule*,
- const SystemInfo*,
- string*,
- char**));
- MOCK_METHOD1(FreeSymbolData, void(const CodeModule*));
+ TestMinidumpSystemInfo(MDRawSystemInfo info) :
+ MinidumpSystemInfo(NULL) {
+ valid_ = true;
+ system_info_ = info;
+ csd_version_ = new string("");
+ }
+};
+
+// A test minidump context, just returns the MDRawContextX86
+// fed to it.
+class TestMinidumpContext : public MinidumpContext {
+public:
+ TestMinidumpContext(const MDRawContextX86& context) : MinidumpContext(NULL) {
+ valid_ = true;
+ context_.x86 = new MDRawContextX86(context);
+ context_flags_ = MD_CONTEXT_X86;
+ }
};
class MinidumpProcessorTest : public ::testing::Test {
@@ -245,11 +309,15 @@ TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) {
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_CALL(dump, GetSystemInfo()).
+ WillRepeatedly(Return((MinidumpSystemInfo*)NULL));
+
EXPECT_EQ(processor.Process(&dump, &state),
google_breakpad::PROCESS_ERROR_NO_THREAD_LIST);
}
@@ -372,6 +440,113 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_SYMBOL_SUPPLIER_INTERRUPTED);
}
+
+TEST_F(MinidumpProcessorTest, TestThreadMissingMemory) {
+ MockMinidump dump;
+ EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
+ EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
+
+ MDRawHeader fake_header;
+ fake_header.time_date_stamp = 0;
+ EXPECT_CALL(dump, header()).WillRepeatedly(Return(&fake_header));
+
+ MDRawSystemInfo raw_system_info;
+ memset(&raw_system_info, 0, sizeof(raw_system_info));
+ raw_system_info.processor_architecture = MD_CPU_ARCHITECTURE_X86;
+ raw_system_info.platform_id = MD_OS_WIN32_NT;
+ TestMinidumpSystemInfo dump_system_info(raw_system_info);
+
+ EXPECT_CALL(dump, GetSystemInfo()).
+ WillRepeatedly(Return(&dump_system_info));
+
+ MockMinidumpThreadList thread_list;
+ EXPECT_CALL(dump, GetThreadList()).
+ WillOnce(Return(&thread_list));
+
+ // Return a thread missing stack memory.
+ MockMinidumpThread no_memory_thread;
+ EXPECT_CALL(no_memory_thread, GetThreadID(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(1),
+ Return(true)));
+ EXPECT_CALL(no_memory_thread, GetMemory()).
+ WillRepeatedly(Return((MinidumpMemoryRegion*)NULL));
+
+ MDRawContextX86 no_memory_thread_raw_context;
+ memset(&no_memory_thread_raw_context, 0,
+ sizeof(no_memory_thread_raw_context));
+ no_memory_thread_raw_context.context_flags = MD_CONTEXT_X86_FULL;
+ const u_int32_t kExpectedEIP = 0xabcd1234;
+ no_memory_thread_raw_context.eip = kExpectedEIP;
+ TestMinidumpContext no_memory_thread_context(no_memory_thread_raw_context);
+ EXPECT_CALL(no_memory_thread, GetContext()).
+ WillRepeatedly(Return(&no_memory_thread_context));
+
+ EXPECT_CALL(thread_list, thread_count()).
+ WillRepeatedly(Return(1));
+ EXPECT_CALL(thread_list, GetThreadAtIndex(0)).
+ WillOnce(Return(&no_memory_thread));
+
+ MinidumpProcessor processor((SymbolSupplier*)NULL, NULL);
+ ProcessState state;
+ EXPECT_EQ(processor.Process(&dump, &state),
+ google_breakpad::PROCESS_OK);
+
+ // Should have a single thread with a single frame in it.
+ ASSERT_EQ(1, state.threads()->size());
+ ASSERT_EQ(1, state.threads()->at(0)->frames()->size());
+ ASSERT_EQ(kExpectedEIP, state.threads()->at(0)->frames()->at(0)->instruction);
+}
+
+TEST_F(MinidumpProcessorTest, TestThreadMissingContext) {
+ MockMinidump dump;
+ EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
+ EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
+
+ MDRawHeader fake_header;
+ fake_header.time_date_stamp = 0;
+ EXPECT_CALL(dump, header()).WillRepeatedly(Return(&fake_header));
+
+ MDRawSystemInfo raw_system_info;
+ memset(&raw_system_info, 0, sizeof(raw_system_info));
+ raw_system_info.processor_architecture = MD_CPU_ARCHITECTURE_X86;
+ raw_system_info.platform_id = MD_OS_WIN32_NT;
+ TestMinidumpSystemInfo dump_system_info(raw_system_info);
+
+ EXPECT_CALL(dump, GetSystemInfo()).
+ WillRepeatedly(Return(&dump_system_info));
+
+ MockMinidumpThreadList thread_list;
+ EXPECT_CALL(dump, GetThreadList()).
+ WillOnce(Return(&thread_list));
+
+ // Return a thread missing a thread context.
+ MockMinidumpThread no_context_thread;
+ EXPECT_CALL(no_context_thread, GetThreadID(_)).
+ WillRepeatedly(DoAll(SetArgumentPointee<0>(1),
+ Return(true)));
+ EXPECT_CALL(no_context_thread, GetContext()).
+ WillRepeatedly(Return((MinidumpContext*)NULL));
+
+ // The memory contents don't really matter here, since it won't be used.
+ MockMinidumpMemoryRegion no_context_thread_memory(0x1234, "xxx");
+ EXPECT_CALL(no_context_thread, GetMemory()).
+ WillRepeatedly(Return(&no_context_thread_memory));
+
+ EXPECT_CALL(thread_list, thread_count()).
+ WillRepeatedly(Return(1));
+ EXPECT_CALL(thread_list, GetThreadAtIndex(0)).
+ WillOnce(Return(&no_context_thread));
+
+ MinidumpProcessor processor((SymbolSupplier*)NULL, NULL);
+ ProcessState state;
+ EXPECT_EQ(processor.Process(&dump, &state),
+ google_breakpad::PROCESS_OK);
+
+ // Should have a single thread with zero frames.
+ ASSERT_EQ(1, state.threads()->size());
+ ASSERT_EQ(0, state.threads()->at(0)->frames()->size());
+}
+
} // namespace
int main(int argc, char *argv[]) {
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
index b37148f4..2ff7a57c 100644
--- a/src/processor/minidump_stackwalk.cc
+++ b/src/processor/minidump_stackwalk.cc
@@ -137,6 +137,9 @@ static string StripSeparator(const string &original) {
// frame printed is also output, if available.
static void PrintStack(const CallStack *stack, const string &cpu) {
int frame_count = stack->frames()->size();
+ if (frame_count == 0) {
+ printf(" <no frames>\n");
+ }
for (int frame_index = 0; frame_index < frame_count; ++frame_index) {
const StackFrame *frame = stack->frames()->at(frame_index);
printf("%2d ", frame_index);
diff --git a/src/processor/minidump_unittest.cc b/src/processor/minidump_unittest.cc
index 60cb37a6..eb203ec6 100644
--- a/src/processor/minidump_unittest.cc
+++ b/src/processor/minidump_unittest.cc
@@ -298,6 +298,93 @@ TEST(Dump, OneThread) {
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
+TEST(Dump, ThreadMissingMemory) {
+ Dump dump(0, kLittleEndian);
+ Memory stack(dump, 0x2326a0fa);
+ // Stack has no contents.
+
+ MDRawContextX86 raw_context;
+ memset(&raw_context, 0, sizeof(raw_context));
+ raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
+ Context context(dump, raw_context);
+
+ Thread thread(dump, 0xa898f11b, stack, context,
+ 0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
+
+ dump.Add(&stack);
+ dump.Add(&context);
+ dump.Add(&thread);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ // This should succeed even though the thread has no stack memory.
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list != NULL);
+ ASSERT_EQ(1U, thread_list->thread_count());
+
+ MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(md_thread != NULL);
+
+ u_int32_t thread_id;
+ ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
+ ASSERT_EQ(0xa898f11bU, thread_id);
+
+ MinidumpContext* md_context = md_thread->GetContext();
+ ASSERT_NE(reinterpret_cast<MinidumpContext*>(NULL), md_context);
+
+ MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
+ ASSERT_EQ(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);
+}
+
+TEST(Dump, ThreadMissingContext) {
+ Dump dump(0, kLittleEndian);
+ Memory stack(dump, 0x2326a0fa);
+ stack.Append("stack for thread");
+
+ // Context is empty.
+ Context context(dump);
+
+ Thread thread(dump, 0xa898f11b, stack, context,
+ 0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
+
+ dump.Add(&stack);
+ dump.Add(&context);
+ dump.Add(&thread);
+ dump.Finish();
+
+ string contents;
+ ASSERT_TRUE(dump.GetContents(&contents));
+
+ istringstream minidump_stream(contents);
+ Minidump minidump(minidump_stream);
+ ASSERT_TRUE(minidump.Read());
+ ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
+
+ // This should succeed even though the thread has no stack memory.
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ ASSERT_TRUE(thread_list != NULL);
+ ASSERT_EQ(1U, thread_list->thread_count());
+
+ MinidumpThread* md_thread = thread_list->GetThreadAtIndex(0);
+ ASSERT_TRUE(md_thread != NULL);
+
+ u_int32_t thread_id;
+ ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
+ ASSERT_EQ(0xa898f11bU, thread_id);
+ MinidumpMemoryRegion* md_stack = md_thread->GetMemory();
+ ASSERT_NE(reinterpret_cast<MinidumpMemoryRegion*>(NULL), md_stack);
+
+ MinidumpContext* md_context = md_thread->GetContext();
+ ASSERT_EQ(reinterpret_cast<MinidumpContext*>(NULL), md_context);
+}
+
TEST(Dump, OneModule) {
static const MDVSFixedFileInfo fixed_file_info = {
0xb2fba33a, // signature
diff --git a/src/processor/stackwalker_amd64.cc b/src/processor/stackwalker_amd64.cc
index 12aa6333..b7d3274d 100644
--- a/src/processor/stackwalker_amd64.cc
+++ b/src/processor/stackwalker_amd64.cc
@@ -102,8 +102,8 @@ StackwalkerAMD64::StackwalkerAMD64(const SystemInfo* system_info,
StackFrame* StackwalkerAMD64::GetContextFrame() {
- if (!context_ || !memory_) {
- BPLOG(ERROR) << "Can't get context frame without context or memory";
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
return NULL;
}
diff --git a/src/processor/stackwalker_amd64_unittest.cc b/src/processor/stackwalker_amd64_unittest.cc
index 1721b8cb..2d679abb 100644
--- a/src/processor/stackwalker_amd64_unittest.cc
+++ b/src/processor/stackwalker_amd64_unittest.cc
@@ -172,6 +172,24 @@ TEST_F(GetContextFrame, Simple) {
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ raw_context.rip = 0x40000000c0000200ULL;
+ raw_context.rbp = 0x8000000080000000ULL;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerAMD64 walker(&system_info, &raw_context, NULL, &modules,
+ &frame_symbolizer);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+ ASSERT_GE(1U, frames->size());
+ StackFrameAMD64 *frame = static_cast<StackFrameAMD64 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
class GetCallerFrame: public StackwalkerAMD64Fixture, public Test { };
TEST_F(GetCallerFrame, ScanWithoutSymbols) {
diff --git a/src/processor/stackwalker_arm.cc b/src/processor/stackwalker_arm.cc
index 3beaa449..2535cd0f 100644
--- a/src/processor/stackwalker_arm.cc
+++ b/src/processor/stackwalker_arm.cc
@@ -59,8 +59,8 @@ StackwalkerARM::StackwalkerARM(const SystemInfo* system_info,
StackFrame* StackwalkerARM::GetContextFrame() {
- if (!context_ || !memory_) {
- BPLOG(ERROR) << "Can't get context frame without context or memory";
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
return NULL;
}
diff --git a/src/processor/stackwalker_arm_unittest.cc b/src/processor/stackwalker_arm_unittest.cc
index 88517b77..e5988f41 100644
--- a/src/processor/stackwalker_arm_unittest.cc
+++ b/src/processor/stackwalker_arm_unittest.cc
@@ -166,6 +166,21 @@ TEST_F(GetContextFrame, Simple) {
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerARM walker(&system_info, &raw_context, -1, NULL, &modules,
+ &frame_symbolizer);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+ StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
class GetCallerFrame: public StackwalkerARMFixture, public Test { };
TEST_F(GetCallerFrame, ScanWithoutSymbols) {
diff --git a/src/processor/stackwalker_ppc.cc b/src/processor/stackwalker_ppc.cc
index 6359e737..40bec8de 100644
--- a/src/processor/stackwalker_ppc.cc
+++ b/src/processor/stackwalker_ppc.cc
@@ -50,7 +50,7 @@ StackwalkerPPC::StackwalkerPPC(const SystemInfo* system_info,
StackFrameSymbolizer* resolver_helper)
: Stackwalker(system_info, memory, modules, resolver_helper),
context_(context) {
- if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
+ if (memory_ && memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
// This implementation only covers 32-bit ppc CPUs. The limits of the
// supplied stack are invalid. Mark memory_ = NULL, which will cause
// stackwalking to fail.
@@ -63,8 +63,8 @@ StackwalkerPPC::StackwalkerPPC(const SystemInfo* system_info,
StackFrame* StackwalkerPPC::GetContextFrame() {
- if (!context_ || !memory_) {
- BPLOG(ERROR) << "Can't get context frame without context or memory";
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
return NULL;
}
diff --git a/src/processor/stackwalker_sparc.cc b/src/processor/stackwalker_sparc.cc
index ac356baa..d1d5d236 100644
--- a/src/processor/stackwalker_sparc.cc
+++ b/src/processor/stackwalker_sparc.cc
@@ -54,8 +54,8 @@ StackwalkerSPARC::StackwalkerSPARC(const SystemInfo* system_info,
StackFrame* StackwalkerSPARC::GetContextFrame() {
- if (!context_ || !memory_) {
- BPLOG(ERROR) << "Can't get context frame without context or memory";
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
return NULL;
}
diff --git a/src/processor/stackwalker_x86.cc b/src/processor/stackwalker_x86.cc
index 6a4569a2..61d75ded 100644
--- a/src/processor/stackwalker_x86.cc
+++ b/src/processor/stackwalker_x86.cc
@@ -86,7 +86,7 @@ StackwalkerX86::StackwalkerX86(const SystemInfo* system_info,
context_(context),
cfi_walker_(cfi_register_map_,
(sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) {
- if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
+ if (memory_ && memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) {
// The x86 is a 32-bit CPU, the limits of the supplied stack are invalid.
// Mark memory_ = NULL, which will cause stackwalking to fail.
BPLOG(ERROR) << "Memory out of range for stackwalking: " <<
@@ -106,8 +106,8 @@ StackFrameX86::~StackFrameX86() {
}
StackFrame* StackwalkerX86::GetContextFrame() {
- if (!context_ || !memory_) {
- BPLOG(ERROR) << "Can't get context frame without context or memory";
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context";
return NULL;
}
diff --git a/src/processor/stackwalker_x86_unittest.cc b/src/processor/stackwalker_x86_unittest.cc
index bf76e881..25801a2a 100644
--- a/src/processor/stackwalker_x86_unittest.cc
+++ b/src/processor/stackwalker_x86_unittest.cc
@@ -181,6 +181,23 @@ TEST_F(GetContextFrame, Simple) {
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ raw_context.eip = 0x40000200;
+ raw_context.ebp = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerX86 walker(&system_info, &raw_context, NULL, &modules,
+ &frame_symbolizer);
+ ASSERT_TRUE(walker.Walk(&call_stack));
+ frames = call_stack.frames();
+ StackFrameX86 *frame = static_cast<StackFrameX86 *>(frames->at(0));
+ // Check that the values from the original raw context made it
+ // through to the context in the stack frame.
+ EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
+}
+
class GetCallerFrame: public StackwalkerX86Fixture, public Test { };
// Walk a traditional frame. A traditional frame saves the caller's
diff --git a/src/processor/synth_minidump.h b/src/processor/synth_minidump.h
index 531603ad..17dbc480 100644
--- a/src/processor/synth_minidump.h
+++ b/src/processor/synth_minidump.h
@@ -228,6 +228,8 @@ class Context: public Section {
// Create a context belonging to DUMP whose contents are a copy of CONTEXT.
Context(const Dump &dump, const MDRawContextX86 &context);
Context(const Dump &dump, const MDRawContextARM &context);
+ // Add an empty context to the dump.
+ Context(const Dump &dump) : Section(dump) {}
// Add constructors for other architectures here. Remember to byteswap.
};