aboutsummaryrefslogtreecommitdiff
path: root/src/processor/basic_source_line_resolver_unittest.cc
diff options
context:
space:
mode:
authorjimblandy <jimblandy@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-03-16 16:28:32 +0000
committerjimblandy <jimblandy@4c0a9323-5329-0410-9bdc-e9ce6186880e>2010-03-16 16:28:32 +0000
commit6d3a825dbf5b924c2e754309b3008e462af1d8d2 (patch)
treecb3fe204a65015c308b7a6324b96e73e528e57c8 /src/processor/basic_source_line_resolver_unittest.cc
parentBreakpad processor: Unit tests for StackwalkerX86. (diff)
downloadbreakpad-6d3a825dbf5b924c2e754309b3008e462af1d8d2.tar.xz
Breakpad: Add minidump processor support for DWARF Call Frame Information.
Add a CFIFrameInfo class (named for symmetry with WindowsFrameInfo) to represent the set of STACK CFI rules in effect at a given instruction, and apply them to a set of register values. Provide a SimpleCFIWalker class template, to allow the essential CFI code to be shared amongst the different architectures. Teach BasicSourceLineResolver to partially parse 'STACK CFI' records, and produce the set of rules in effect at a given instruction on demand, by combining the initial rule set and the appropriate rule deltas in a CFIFrameInfo object. Adapt StackwalkerX86 and StackFrameX86 to retrieve, store, and apply CFI stack walking information. Add validity flags for all the general-purpose registers to StackFrameX86::ContextValidity. a=jimblandy, r=mmentovai git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@549 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src/processor/basic_source_line_resolver_unittest.cc')
-rw-r--r--src/processor/basic_source_line_resolver_unittest.cc166
1 files changed, 164 insertions, 2 deletions
diff --git a/src/processor/basic_source_line_resolver_unittest.cc b/src/processor/basic_source_line_resolver_unittest.cc
index c4cb6ea4..126ce6d6 100644
--- a/src/processor/basic_source_line_resolver_unittest.cc
+++ b/src/processor/basic_source_line_resolver_unittest.cc
@@ -32,10 +32,12 @@
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/stack_frame.h"
+#include "google_breakpad/processor/memory_region.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/windows_frame_info.h"
+#include "processor/cfi_frame_info.h"
#define ASSERT_TRUE(cond) \
if (!(cond)) { \
@@ -51,11 +53,13 @@ namespace {
using std::string;
using google_breakpad::BasicSourceLineResolver;
+using google_breakpad::CFIFrameInfo;
using google_breakpad::CodeModule;
-using google_breakpad::linked_ptr;
-using google_breakpad::scoped_ptr;
+using google_breakpad::MemoryRegion;
using google_breakpad::StackFrame;
using google_breakpad::WindowsFrameInfo;
+using google_breakpad::linked_ptr;
+using google_breakpad::scoped_ptr;
class TestCodeModule : public CodeModule {
public:
@@ -77,6 +81,70 @@ class TestCodeModule : public CodeModule {
string code_file_;
};
+// A mock memory region object, for use by the STACK CFI tests.
+class MockMemoryRegion: public MemoryRegion {
+ u_int64_t GetBase() const { return 0x10000; }
+ u_int32_t GetSize() const { return 0x01000; }
+ bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
+ *value = address & 0xff;
+ return true;
+ }
+ bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
+ *value = address & 0xffff;
+ return true;
+ }
+ bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
+ switch (address) {
+ case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
+ case 0x1000c: *value = 0x878f7524; break; // saved %esi
+ case 0x10010: *value = 0x6312f9a5; break; // saved %edi
+ case 0x10014: *value = 0x10038; break; // caller's %ebp
+ case 0x10018: *value = 0xf6438648; break; // return address
+ default: *value = 0xdeadbeef; break; // junk
+ }
+ return true;
+ }
+ bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
+ *value = address;
+ return true;
+ }
+};
+
+// Verify that, for every association in ACTUAL, EXPECTED has the same
+// association. (That is, ACTUAL's associations should be a subset of
+// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
+// ".cfa".
+static bool VerifyRegisters(
+ const char *file, int line,
+ const CFIFrameInfo::RegisterValueMap<u_int32_t> &expected,
+ const CFIFrameInfo::RegisterValueMap<u_int32_t> &actual) {
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator a;
+ a = actual.find(".cfa");
+ ASSERT_TRUE(a != actual.end());
+ a = actual.find(".ra");
+ ASSERT_TRUE(a != actual.end());
+ for (a = actual.begin(); a != actual.end(); a++) {
+ CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator e =
+ expected.find(a->first);
+ if (e == expected.end()) {
+ fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
+ file, line, a->first.c_str(), a->second);
+ return false;
+ }
+ if (e->second != a->second) {
+ fprintf(stderr,
+ "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
+ file, line, a->first.c_str(), a->second, e->second);
+ return false;
+ }
+ // Don't complain if this doesn't recover all registers. Although
+ // the DWARF spec says that unmentioned registers are undefined,
+ // GCC uses omission to mean that they are unchanged.
+ }
+ return true;
+}
+
+
static bool VerifyEmpty(const StackFrame &frame) {
ASSERT_TRUE(frame.function_name.empty());
ASSERT_TRUE(frame.source_file_name.empty());
@@ -105,6 +173,7 @@ static bool RunTests() {
StackFrame frame;
scoped_ptr<WindowsFrameInfo> windows_frame_info;
+ scoped_ptr<CFIFrameInfo> cfi_frame_info;
frame.instruction = 0x1000;
frame.module = NULL;
resolver.FillSourceLineInfo(&frame);
@@ -162,6 +231,99 @@ static bool RunTests() {
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_FALSE(windows_frame_info.get());
+ // module1 has STACK CFI records covering 3d40..3def;
+ // module2 has STACK CFI records covering 3df0..3e9f;
+ // check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
+ frame.instruction = 0x3d3f;
+ frame.module = &module1;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_FALSE(cfi_frame_info.get());
+
+ frame.instruction = 0x3e9f;
+ frame.module = &module1;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_FALSE(cfi_frame_info.get());
+
+ CFIFrameInfo::RegisterValueMap<u_int32_t> current_registers;
+ CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
+ CFIFrameInfo::RegisterValueMap<u_int32_t> expected_caller_registers;
+ MockMemoryRegion memory;
+
+ // Regardless of which instruction evaluation takes place at, it
+ // should produce the same values for the caller's registers.
+ expected_caller_registers[".cfa"] = 0x1001c;
+ expected_caller_registers[".ra"] = 0xf6438648;
+ expected_caller_registers["$ebp"] = 0x10038;
+ expected_caller_registers["$ebx"] = 0x98ecadc3;
+ expected_caller_registers["$esi"] = 0x878f7524;
+ expected_caller_registers["$edi"] = 0x6312f9a5;
+
+ frame.instruction = 0x3d40;
+ frame.module = &module1;
+ current_registers.clear();
+ current_registers["$esp"] = 0x10018;
+ current_registers["$ebp"] = 0x10038;
+ current_registers["$ebx"] = 0x98ecadc3;
+ current_registers["$esi"] = 0x878f7524;
+ current_registers["$edi"] = 0x6312f9a5;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_TRUE(cfi_frame_info.get());
+ ASSERT_TRUE(cfi_frame_info.get()
+ ->FindCallerRegs<u_int32_t>(current_registers, memory,
+ &caller_registers));
+ VerifyRegisters(__FILE__, __LINE__,
+ expected_caller_registers, caller_registers);
+
+ frame.instruction = 0x3d41;
+ current_registers["$esp"] = 0x10014;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_TRUE(cfi_frame_info.get());
+ ASSERT_TRUE(cfi_frame_info.get()
+ ->FindCallerRegs<u_int32_t>(current_registers, memory,
+ &caller_registers));
+ VerifyRegisters(__FILE__, __LINE__,
+ expected_caller_registers, caller_registers);
+
+ frame.instruction = 0x3d43;
+ current_registers["$ebp"] = 0x10014;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_TRUE(cfi_frame_info.get());
+ ASSERT_TRUE(cfi_frame_info.get()
+ ->FindCallerRegs<u_int32_t>(current_registers, memory,
+ &caller_registers));
+ VerifyRegisters(__FILE__, __LINE__,
+ expected_caller_registers, caller_registers);
+
+ frame.instruction = 0x3d54;
+ current_registers["$ebx"] = 0x6864f054U;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_TRUE(cfi_frame_info.get());
+ ASSERT_TRUE(cfi_frame_info.get()
+ ->FindCallerRegs<u_int32_t>(current_registers, memory,
+ &caller_registers));
+ VerifyRegisters(__FILE__, __LINE__,
+ expected_caller_registers, caller_registers);
+
+ frame.instruction = 0x3d5a;
+ current_registers["$esi"] = 0x6285f79aU;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_TRUE(cfi_frame_info.get());
+ ASSERT_TRUE(cfi_frame_info.get()
+ ->FindCallerRegs<u_int32_t>(current_registers, memory,
+ &caller_registers));
+ VerifyRegisters(__FILE__, __LINE__,
+ expected_caller_registers, caller_registers);
+
+ frame.instruction = 0x3d84;
+ current_registers["$edi"] = 0x64061449U;
+ cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
+ ASSERT_TRUE(cfi_frame_info.get());
+ ASSERT_TRUE(cfi_frame_info.get()
+ ->FindCallerRegs<u_int32_t>(current_registers, memory,
+ &caller_registers));
+ VerifyRegisters(__FILE__, __LINE__,
+ expected_caller_registers, caller_registers);
+
frame.instruction = 0x2900;
frame.module = &module1;
resolver.FillSourceLineInfo(&frame);