aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-09-06 19:28:46 +0000
committermmentovai <mmentovai@4c0a9323-5329-0410-9bdc-e9ce6186880e>2006-09-06 19:28:46 +0000
commit213800d30c11612cb0457c94d7233813a22d83d5 (patch)
treea143c65491b29e712788c295ef9969828cebb4e0 /src
parentInitial implementation of minidump reader (#6). r=bryner (diff)
downloadbreakpad-213800d30c11612cb0457c94d7233813a22d83d5.tar.xz
Initial implementation of x86 stackwalker (#9). r=bryner
git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@12 4c0a9323-5329-0410-9bdc-e9ce6186880e
Diffstat (limited to 'src')
-rw-r--r--src/processor/minidump_stackwalk.cc118
-rwxr-xr-xsrc/processor/minidump_stackwalk_test6
-rw-r--r--src/processor/stackwalker.cc78
-rw-r--r--src/processor/stackwalker.h83
-rw-r--r--src/processor/stackwalker_x86.cc103
-rw-r--r--src/processor/stackwalker_x86.h68
-rw-r--r--src/processor/testdata/minidump1.stack.out23
7 files changed, 479 insertions, 0 deletions
diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc
new file mode 100644
index 00000000..71948a3b
--- /dev/null
+++ b/src/processor/minidump_stackwalk.cc
@@ -0,0 +1,118 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// minidump_stackwalk.cc: Print the stack of the exception thread from a
+// minidump.
+//
+// Author: Mark Mentovai
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#define O_BINARY 0
+#else // !_WIN32
+#include <io.h>
+#define open _open
+#endif // !_WIN32
+
+#include <memory>
+
+#include "processor/minidump.h"
+#include "processor/stackwalker_x86.h"
+
+
+using std::auto_ptr;
+using namespace google_airbag;
+
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s <file>\n", argv[0]);
+ exit(1);
+ }
+
+ int fd = open(argv[1], O_RDONLY | O_BINARY);
+ if (fd == -1) {
+ fprintf(stderr, "open failed\n");
+ exit(1);
+ }
+
+ Minidump minidump(fd);
+ if (!minidump.Read()) {
+ fprintf(stderr, "minidump.Read() failed\n");
+ exit(1);
+ }
+
+ MinidumpException* exception = minidump.GetException();
+ if (!exception) {
+ fprintf(stderr, "minidump.GetException() failed\n");
+ exit(1);
+ }
+
+ MinidumpThreadList* thread_list = minidump.GetThreadList();
+ if (!thread_list) {
+ fprintf(stderr, "minidump.GetThreadList() failed\n");
+ exit(1);
+ }
+
+ MinidumpThread* exception_thread =
+ thread_list->GetThreadByID(exception->GetThreadID());
+ if (!exception_thread) {
+ fprintf(stderr, "thread_list->GetThreadByID() failed\n");
+ exit(1);
+ }
+
+ MemoryRegion* stack_memory = exception_thread->GetMemory();
+ if (!stack_memory) {
+ fprintf(stderr, "exception_thread->GetStackMemory() failed\n");
+ exit(1);
+ }
+
+ MinidumpContext* context = exception->GetContext();
+ if (!context) {
+ fprintf(stderr, "exception->GetContext() failed\n");
+ exit(1);
+ }
+
+ MinidumpModuleList* modules = minidump.GetModuleList();
+ if (!modules) {
+ fprintf(stderr, "minidump.GetModuleList() failed\n");
+ exit(1);
+ }
+
+ StackwalkerX86 stackwalker = StackwalkerX86(context, stack_memory, modules);
+
+ auto_ptr<StackFrames> stack(stackwalker.Walk());
+ if (!stack.get()) {
+ fprintf(stderr, "stackwalker->Walk() failed\n");
+ exit(1);
+ }
+
+ unsigned int index;
+ for (index = 0 ; index < stack->size() ; index++) {
+ StackFrame frame = stack->at(index);
+ printf("[%2d] ebp = 0x%08llx eip = 0x%08llx \"%s\" + 0x%08llx\n",
+ index,
+ frame.frame_pointer,
+ frame.instruction,
+ frame.module_base ? frame.module_name.c_str() : "0x0",
+ frame.instruction - frame.module_base);
+ }
+
+ return 0;
+}
diff --git a/src/processor/minidump_stackwalk_test b/src/processor/minidump_stackwalk_test
new file mode 100755
index 00000000..a83ea3b3
--- /dev/null
+++ b/src/processor/minidump_stackwalk_test
@@ -0,0 +1,6 @@
+#!/bin/sh
+testdata_dir=$srcdir/src/processor/testdata
+./src/processor/minidump_stackwalk $testdata_dir/minidump1.dmp | \
+ tr -s '\015' '\012' | \
+ diff -u $testdata_dir/minidump1.stack.out -
+exit $?
diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc
new file mode 100644
index 00000000..646ba955
--- /dev/null
+++ b/src/processor/stackwalker.cc
@@ -0,0 +1,78 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// stackwalker.cc: Generic stackwalker.
+//
+// See stackwalker.h for documentation.
+//
+// Author: Mark Mentovai
+
+
+#include <memory>
+
+#include "processor/stackwalker.h"
+#include "processor/minidump.h"
+
+
+namespace google_airbag {
+
+
+using std::auto_ptr;
+
+
+Stackwalker::Stackwalker(MemoryRegion* memory, MinidumpModuleList* modules)
+ : memory_(memory), modules_(modules) {
+}
+
+
+StackFrames* Stackwalker::Walk() {
+ auto_ptr<StackFrames> frames(new StackFrames());
+
+ // Begin with the context frame, and keep getting callers until there are
+ // no more.
+
+ auto_ptr<StackFrame> frame(new StackFrame());
+ bool valid = GetContextFrame(frame.get());
+ while (valid) {
+ // frame already contains a good frame with properly set instruction and
+ // frame_pointer fields. The frame structure comes from either the
+ // context frame (above) or a caller frame (below).
+
+ // Resolve the module information, if a module map was provided.
+ if (modules_) {
+ MinidumpModule* module =
+ modules_->GetModuleForAddress(frame->instruction);
+ if (module) {
+ frame->module_name = *(module->GetName());
+ frame->module_base = module->base_address();
+ }
+ }
+
+ // Copy the frame into the frames vector.
+ frames->push_back(*frame);
+
+ // Use a new object for the next frame, even though the old object was
+ // copied. If StackFrame provided some sort of Clear() method, then
+ // the same frame could be reused.
+ frame.reset(new StackFrame());
+
+ // Get the next frame.
+ valid = GetCallerFrame(frame.get());
+ }
+
+ return frames.release();
+}
+
+
+} // namespace google_airbag
diff --git a/src/processor/stackwalker.h b/src/processor/stackwalker.h
new file mode 100644
index 00000000..1ae9a85d
--- /dev/null
+++ b/src/processor/stackwalker.h
@@ -0,0 +1,83 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// stackwalker.cc: Generic stackwalker.
+//
+// The Stackwalker class is an abstract base class providing common generic
+// methods that apply to stacks from all systems. Specific implementations
+// will extend this class by providing GetContextFrame and GetCallerFrame
+// methods to fill in system-specific data in a StackFrame structure.
+// Stackwalker assembles these StackFrame strucutres into a vector of
+// StackFrames.
+//
+// Author: Mark Mentovai
+
+
+#ifndef PROCESSOR_STACKWALKER_H__
+#define PROCESSOR_STACKWALKER_H__
+
+
+#include "google/stack_frame.h"
+#include "processor/memory_region.h"
+
+
+namespace google_airbag {
+
+
+class MinidumpModuleList;
+
+
+class Stackwalker {
+ public:
+ virtual ~Stackwalker() {}
+
+ // Produces a vector of StackFrames by calling GetContextFrame and
+ // GetCallerFrame, and populating the returned frames with module
+ // offset and name information if possible. The caller takes ownership
+ // of the StackFrames object and is responsible for freeing it.
+ StackFrames* Walk();
+
+ protected:
+ // memory identifies a MemoryRegion that provides the stack memory
+ // for the stack to walk. modules, if non-NULL, is a MinidumpModuleList
+ // that is used to look up which code module each stack frame is
+ // associated with.
+ Stackwalker(MemoryRegion* memory, MinidumpModuleList* modules);
+
+ // The stack memory to walk. Subclasses will require this region to
+ // get information from the stack.
+ MemoryRegion* memory_;
+
+ private:
+ // Obtains the context frame, the innermost called procedure in a stack
+ // trace. Returns false on failure.
+ virtual bool GetContextFrame(StackFrame* frame) = 0;
+
+ // Obtains a caller frame. Each call to GetCallerFrame should return the
+ // frame that called the last frame returned by GetContextFrame or
+ // GetCallerFrame. GetCallerFrame should return false on failure or
+ // when there are no more caller frames (when the end of the stack has
+ // been reached).
+ virtual bool GetCallerFrame(StackFrame* frame) = 0;
+
+ // A list of modules, for populating each StackFrame's module information.
+ // This field is optional and may be NULL.
+ MinidumpModuleList* modules_;
+};
+
+
+} // namespace google_airbag
+
+
+#endif // PROCESSOR_STACKWALKER_H__
diff --git a/src/processor/stackwalker_x86.cc b/src/processor/stackwalker_x86.cc
new file mode 100644
index 00000000..d3360caf
--- /dev/null
+++ b/src/processor/stackwalker_x86.cc
@@ -0,0 +1,103 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// stackwalker_x86.cc: x86-specific stackwalker.
+//
+// See stackwalker_x86.h for documentation.
+//
+// Author: Mark Mentovai
+
+
+#include "processor/stackwalker_x86.h"
+#include "processor/minidump.h"
+
+
+namespace google_airbag {
+
+
+StackwalkerX86::StackwalkerX86(MinidumpContext* context,
+ MemoryRegion* memory,
+ MinidumpModuleList* modules)
+ : Stackwalker(memory, modules)
+ , last_frame_pointer_(0) {
+ if (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.
+ memory_ = NULL;
+ }
+
+ // TODO(mmentovai): verify that |context| is x86 when Minidump supports
+ // other CPU types.
+ context_ = context->context();
+}
+
+
+bool StackwalkerX86::GetContextFrame(StackFrame* frame) {
+ if (!context_ || !memory_ || !frame)
+ return false;
+
+ // The frame and instruction pointers are stored directly in registers,
+ // so pull them straight out of the CPU context structure.
+ frame->frame_pointer = last_frame_pointer_ = context_->ebp;
+ frame->instruction = context_->eip;
+
+ return true;
+}
+
+
+bool StackwalkerX86::GetCallerFrame(StackFrame* frame) {
+ if (!memory_ || !frame)
+ return false;
+
+ // The frame and instruction pointers for previous frames are saved on the
+ // stack. The typical x86 calling convention, when frame pointers are
+ // present, is for the calling procedure to use CALL, which pushes the
+ // return address onto the stack and sets the instruction pointer (%eip)
+ // to the entry point of the called routine. The called routine's then
+ // PUSHes the calling routine's frame pointer (%ebp) onto the stack before
+ // copying the stack pointer (%esp) to the frame pointer (%ebp). Therefore,
+ // the calling procedure's frame pointer is always available by
+ // dereferencing the called procedure's frame pointer, and the return
+ // address is always available at the memory location immediately above
+ // the address pointed to by the called procedure's frame pointer.
+
+ // If there is no frame pointer, determining the layout of the stack is
+ // considerably more difficult, requiring debugging information. This
+ // stackwalker doesn't attempt to solve that problem (at this point).
+
+ // Don't pass frame.frame_pointer or frame.instruction directly
+ // ReadMemory, because their types are too wide (64-bit), and we
+ // specifically want to read 32-bit quantities for both.
+ u_int32_t frame_pointer;
+ if (!memory_->GetMemoryAtAddress(last_frame_pointer_, &frame_pointer))
+ return false;
+
+ // A caller frame must reside higher in memory than its callee frames.
+ // Anything else is an error, or an indication that we've reached the
+ // end of the stack.
+ if (frame_pointer <= last_frame_pointer_)
+ return false;
+
+ u_int32_t instruction;
+ if (!memory_->GetMemoryAtAddress(last_frame_pointer_ + 4, &instruction))
+ return false;
+
+ frame->frame_pointer = last_frame_pointer_ = frame_pointer;
+ frame->instruction = instruction;
+
+ return true;
+}
+
+
+} // namespace google_airbag
diff --git a/src/processor/stackwalker_x86.h b/src/processor/stackwalker_x86.h
new file mode 100644
index 00000000..74d893a2
--- /dev/null
+++ b/src/processor/stackwalker_x86.h
@@ -0,0 +1,68 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// stackwalker_x86.h: x86-specific stackwalker.
+//
+// Provides stack frames given x86 register context and a memory region
+// corresponding to an x86 stack.
+//
+// Author: Mark Mentovai
+
+
+#ifndef PROCESSOR_STACKWALKER_X86_H__
+#define PROCESSOR_STACKWALKER_X86_H__
+
+
+#include "google/airbag_types.h"
+#include "processor/stackwalker.h"
+#include "processor/minidump_format.h"
+
+
+namespace google_airbag {
+
+
+class MinidumpContext;
+class MinidumpModuleList;
+
+
+class StackwalkerX86 : public Stackwalker {
+ public:
+ // context is a MinidumpContext object that gives access to x86-specific
+ // register state corresponding to the innermost called frame to be
+ // included in the stack. memory and modules are passed directly through
+ // to the base Stackwalker constructor.
+ StackwalkerX86(MinidumpContext* context,
+ MemoryRegion* memory,
+ MinidumpModuleList* modules);
+
+ private:
+ // Implementation of Stackwalker, using x86 context (%ebp, %eip) and
+ // stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp]).
+ bool GetContextFrame(StackFrame* frame);
+ bool GetCallerFrame(StackFrame* frame);
+
+ // Stores the CPU context corresponding to the innermost stack frame to
+ // be returned by GetContextFrame.
+ const MDRawContextX86* context_;
+
+ // Stores the frame pointer returned in the last stack frame returned by
+ // GetContextFrame or GetCallerFrame.
+ u_int32_t last_frame_pointer_;
+};
+
+
+} // namespace google_airbag
+
+
+#endif // PROCESSOR_STACKWALKER_X86_H__
diff --git a/src/processor/testdata/minidump1.stack.out b/src/processor/testdata/minidump1.stack.out
new file mode 100644
index 00000000..d2e254d8
--- /dev/null
+++ b/src/processor/testdata/minidump1.stack.out
@@ -0,0 +1,23 @@
+[ 0] ebp = 0x0012ecb8 eip = 0x020a1515 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00221515
+[ 1] ebp = 0x0012ecd8 eip = 0x020a03e3 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x002203e3
+[ 2] ebp = 0x0012ecf0 eip = 0x023c8a28 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00548a28
+[ 3] ebp = 0x0012ed30 eip = 0x023ccfd9 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x0054cfd9
+[ 4] ebp = 0x0012ed64 eip = 0x0222fd12 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003afd12
+[ 5] ebp = 0x0012ed94 eip = 0x022311dd "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003b11dd
+[ 6] ebp = 0x0012edc8 eip = 0x034eb0f1 "c:\lizard\trunk\mozilla\dist\bin\components\xpc3250.dll" + 0x0005b0f1
+[ 7] ebp = 0x0012eeb0 eip = 0x0049bda4 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0007bda4
+[ 8] ebp = 0x0012f834 eip = 0x0047b92f "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0005b92f
+[ 9] ebp = 0x0012f93c eip = 0x0046c945 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0004c945
+[10] ebp = 0x0012f9c8 eip = 0x0046d345 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0004d345
+[11] ebp = 0x0012f9f0 eip = 0x00430ec3 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x00010ec3
+[12] ebp = 0x0012fa4c eip = 0x02213b7f "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00393b7f
+[13] ebp = 0x0012fb60 eip = 0x02249ced "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003c9ced
+[14] ebp = 0x0012fb70 eip = 0x0224a810 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003ca810
+[15] ebp = 0x0012fbbc eip = 0x002ebff8 "c:\lizard\trunk\mozilla\dist\bin\xpcom_core.dll" + 0x0007bff8
+[16] ebp = 0x0012fbe4 eip = 0x002ec8ec "c:\lizard\trunk\mozilla\dist\bin\xpcom_core.dll" + 0x0007c8ec
+[17] ebp = 0x0012fc48 eip = 0x029193b5 "c:\lizard\trunk\mozilla\dist\bin\components\gkwidget.dll" + 0x000293b5
+[18] ebp = 0x0012fc5c eip = 0x03174b19 "c:\lizard\trunk\mozilla\dist\bin\components\tkitcmps.dll" + 0x00004b19
+[19] ebp = 0x0012ff54 eip = 0x10008e60 "c:\lizard\trunk\mozilla\dist\bin\xul.dll" + 0x00008e60
+[20] ebp = 0x0012ff68 eip = 0x00401036 "c:\lizard\trunk\mozilla\dist\bin\firefox.exe" + 0x00001036
+[21] ebp = 0x0012ffc0 eip = 0x004011bc "c:\lizard\trunk\mozilla\dist\bin\firefox.exe" + 0x000011bc
+[22] ebp = 0x0012fff0 eip = 0x7c816d4f "C:\WINDOWS\system32\kernel32.dll" + 0x00016d4f