aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/processor/stackwalker_x86.cc395
-rw-r--r--src/processor/stackwalker_x86.h21
2 files changed, 246 insertions, 170 deletions
diff --git a/src/processor/stackwalker_x86.cc b/src/processor/stackwalker_x86.cc
index f24667c9..6ea18319 100644
--- a/src/processor/stackwalker_x86.cc
+++ b/src/processor/stackwalker_x86.cc
@@ -36,13 +36,14 @@
#include "processor/postfix_evaluator-inl.h"
-#include "processor/stackwalker_x86.h"
#include "google_breakpad/processor/call_stack.h"
#include "google_breakpad/processor/code_modules.h"
#include "google_breakpad/processor/memory_region.h"
-#include "google_breakpad/processor/stack_frame_cpu.h"
#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/logging.h"
+#include "processor/scoped_ptr.h"
+#include "processor/stackwalker_x86.h"
#include "processor/windows_frame_info.h"
namespace google_breakpad {
@@ -72,7 +73,7 @@ StackFrameX86::~StackFrameX86() {
windows_frame_info = NULL;
}
-StackFrame* StackwalkerX86::GetContextFrame() {
+StackFrame *StackwalkerX86::GetContextFrame() {
if (!context_ || !memory_) {
BPLOG(ERROR) << "Can't get context frame without context or memory";
return NULL;
@@ -90,23 +91,23 @@ StackFrame* StackwalkerX86::GetContextFrame() {
return frame;
}
-
-StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
- if (!memory_ || !stack) {
- BPLOG(ERROR) << "Can't get caller frame without memory or stack";
- return NULL;
- }
+StackFrameX86 *StackwalkerX86::GetCallerByWindowsFrameInfo(
+ const vector<StackFrame *> &frames,
+ WindowsFrameInfo *last_frame_info) {
StackFrameX86::FrameTrust trust = StackFrameX86::FRAME_TRUST_NONE;
- StackFrameX86 *last_frame = static_cast<StackFrameX86*>(
- stack->frames()->back());
- WindowsFrameInfo *last_frame_info
- = resolver_->FindWindowsFrameInfo(last_frame);
+ StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
// Save the stack walking info we found, in case we need it later to
// find the callee of the frame we're constructing now.
last_frame->windows_frame_info = last_frame_info;
+ // This function only covers the full STACK WIN case. If
+ // last_frame_info is VALID_PARAMETER_SIZE-only, then we should
+ // assume the traditional frame format or use some other strategy.
+ if (last_frame_info->valid != WindowsFrameInfo::VALID_ALL)
+ return NULL;
+
// This stackwalker sets each frame's %esp to its value immediately prior
// to the CALL into the callee. This means that %esp points to the last
// callee argument pushed onto the stack, which may not be where %esp points
@@ -139,12 +140,11 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
// are unknown, 0 is also used in that case. When that happens, it should
// be possible to walk to the next frame without reference to %esp.
- int frames_already_walked = stack->frames()->size();
u_int32_t last_frame_callee_parameter_size = 0;
+ int frames_already_walked = frames.size();
if (frames_already_walked >= 2) {
- StackFrameX86 *last_frame_callee
- = static_cast<StackFrameX86 *>((*stack->frames())
- [frames_already_walked - 2]);
+ const StackFrameX86 *last_frame_callee
+ = static_cast<StackFrameX86 *>(frames[frames_already_walked - 2]);
WindowsFrameInfo *last_frame_callee_info
= last_frame_callee->windows_frame_info;
if (last_frame_callee_info &&
@@ -157,148 +157,105 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
// Set up the dictionary for the PostfixEvaluator. %ebp and %esp are used
// in each program string, and their previous values are known, so set them
- // here. .cbCalleeParams is a Breakpad extension that allows us to use
- // the PostfixEvaluator engine when certain types of debugging information
- // are present without having to write the constants into the program string
- // as literals.
+ // here.
PostfixEvaluator<u_int32_t>::DictionaryType dictionary;
+ // Provide the current register values.
dictionary["$ebp"] = last_frame->context.ebp;
dictionary["$esp"] = last_frame->context.esp;
+ // Provide constants from the debug info for last_frame and its callee.
+ // .cbCalleeParams is a Breakpad extension that allows us to use the
+ // PostfixEvaluator engine when certain types of debugging information
+ // are present without having to write the constants into the program
+ // string as literals.
dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
-
- if (last_frame_info && last_frame_info->valid == WindowsFrameInfo::VALID_ALL) {
- // FPO debugging data is available. Initialize constants.
- dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
- dictionary[".cbLocals"] = last_frame_info->local_size;
- dictionary[".raSearchStart"] = last_frame->context.esp +
- last_frame_callee_parameter_size +
- last_frame_info->local_size +
- last_frame_info->saved_register_size;
- }
- if (last_frame_info &&
- last_frame_info->valid & WindowsFrameInfo::VALID_PARAMETER_SIZE) {
- // This is treated separately because it can either come from FPO data or
- // from other debugging data.
- dictionary[".cbParams"] = last_frame_info->parameter_size;
- }
-
- // Decide what type of program string to use. The program string is in
+ dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
+ dictionary[".cbLocals"] = last_frame_info->local_size;
+ dictionary[".raSearchStart"] = last_frame->context.esp +
+ last_frame_callee_parameter_size +
+ last_frame_info->local_size +
+ last_frame_info->saved_register_size;
+ dictionary[".cbParams"] = last_frame_info->parameter_size;
+
+ // Decide what type of program string to use. The program string is in
// postfix notation and will be passed to PostfixEvaluator::Evaluate.
// Given the dictionary and the program string, it is possible to compute
// the return address and the values of other registers in the calling
- // function. When encountering a nontraditional frame (one which takes
- // advantage of FPO), the stack may need to be scanned for these values.
- // For traditional frames, simple deterministic dereferencing suffices
- // without any need for scanning. The results of program string evaluation
+ // function. Because of bugs described below, the stack may need to be
+ // scanned for these values. The results of program string evaluation
// will be used to determine whether to scan for better values.
string program_string;
- bool traditional_frame = true;
bool recover_ebp = true;
- if (last_frame_info && last_frame_info->valid == WindowsFrameInfo::VALID_ALL) {
- // FPO data available.
- traditional_frame = false;
- trust = StackFrameX86::FRAME_TRUST_CFI;
- if (!last_frame_info->program_string.empty()) {
- // The FPO data has its own program string, which will tell us how to
- // get to the caller frame, and may even fill in the values of
- // nonvolatile registers and provide pointers to local variables and
- // parameters. In some cases, particularly with program strings that use
- // .raSearchStart, the stack may need to be scanned afterward.
- program_string = last_frame_info->program_string;
- } else if (last_frame_info->allocates_base_pointer) {
- // The function corresponding to the last frame doesn't use the frame
- // pointer for conventional purposes, but it does allocate a new
- // frame pointer and use it for its own purposes. Its callee's
- // information is still accessed relative to %esp, and the previous
- // value of %ebp can be recovered from a location in its stack frame,
- // within the saved-register area.
- //
- // Functions that fall into this category use the %ebp register for
- // a purpose other than the frame pointer. They restore the caller's
- // %ebp before returning. These functions create their stack frame
- // after a CALL by decrementing the stack pointer in an amount
- // sufficient to store local variables, and then PUSHing saved
- // registers onto the stack. Arguments to a callee function, if any,
- // are PUSHed after that. Walking up to the caller, therefore,
- // can be done solely with calculations relative to the stack pointer
- // (%esp). The return address is recovered from the memory location
- // above the known sizes of the callee's parameters, saved registers,
- // and locals. The caller's stack pointer (the value of %esp when
- // the caller executed CALL) is the location immediately above the
- // saved return address. The saved value of %ebp to be restored for
- // the caller is at a known location in the saved-register area of
- // the stack frame.
- //
- // For this type of frame, MSVC 14 (from Visual Studio 8/2005) in
- // link-time code generation mode (/LTCG and /GL) can generate erroneous
- // debugging data. The reported size of saved registers can be 0,
- // which is clearly an error because these frames must, at the very
- // least, save %ebp. For this reason, in addition to those given above
- // about the use of .raSearchStart, the stack may need to be scanned
- // for a better return address and a better frame pointer after the
- // program string is evaluated.
- //
- // %eip_new = *(%esp_old + callee_params + saved_regs + locals)
- // %ebp_new = *(%esp_old + callee_params + saved_regs - 8)
- // %esp_new = %esp_old + callee_params + saved_regs + locals + 4
- program_string = "$eip .raSearchStart ^ = "
- "$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = "
- "$esp .raSearchStart 4 + =";
- } else {
- // The function corresponding to the last frame doesn't use %ebp at
- // all. The callee frame is located relative to %esp.
- //
- // The called procedure's instruction pointer and stack pointer are
- // recovered in the same way as the case above, except that no
- // frame pointer (%ebp) is used at all, so it is not saved anywhere
- // in the callee's stack frame and does not need to be recovered.
- // Because %ebp wasn't used in the callee, whatever value it has
- // is the value that it had in the caller, so it can be carried
- // straight through without bringing its validity into question.
- //
- // Because of the use of .raSearchStart, the stack will possibly be
- // examined to locate a better return address after program string
- // evaluation. The stack will not be examined to locate a saved
- // %ebp value, because these frames do not save (or use) %ebp.
- //
- // %eip_new = *(%esp_old + callee_params + saved_regs + locals)
- // %esp_new = %esp_old + callee_params + saved_regs + locals + 4
- // %ebp_new = %ebp_old
- program_string = "$eip .raSearchStart ^ = "
- "$esp .raSearchStart 4 + =";
- recover_ebp = false;
- }
+
+ trust = StackFrameX86::FRAME_TRUST_CFI;
+ if (!last_frame_info->program_string.empty()) {
+ // The FPO data has its own program string, which will tell us how to
+ // get to the caller frame, and may even fill in the values of
+ // nonvolatile registers and provide pointers to local variables and
+ // parameters. In some cases, particularly with program strings that use
+ // .raSearchStart, the stack may need to be scanned afterward.
+ program_string = last_frame_info->program_string;
+ } else if (last_frame_info->allocates_base_pointer) {
+ // The function corresponding to the last frame doesn't use the frame
+ // pointer for conventional purposes, but it does allocate a new
+ // frame pointer and use it for its own purposes. Its callee's
+ // information is still accessed relative to %esp, and the previous
+ // value of %ebp can be recovered from a location in its stack frame,
+ // within the saved-register area.
+ //
+ // Functions that fall into this category use the %ebp register for
+ // a purpose other than the frame pointer. They restore the caller's
+ // %ebp before returning. These functions create their stack frame
+ // after a CALL by decrementing the stack pointer in an amount
+ // sufficient to store local variables, and then PUSHing saved
+ // registers onto the stack. Arguments to a callee function, if any,
+ // are PUSHed after that. Walking up to the caller, therefore,
+ // can be done solely with calculations relative to the stack pointer
+ // (%esp). The return address is recovered from the memory location
+ // above the known sizes of the callee's parameters, saved registers,
+ // and locals. The caller's stack pointer (the value of %esp when
+ // the caller executed CALL) is the location immediately above the
+ // saved return address. The saved value of %ebp to be restored for
+ // the caller is at a known location in the saved-register area of
+ // the stack frame.
+ //
+ // For this type of frame, MSVC 14 (from Visual Studio 8/2005) in
+ // link-time code generation mode (/LTCG and /GL) can generate erroneous
+ // debugging data. The reported size of saved registers can be 0,
+ // which is clearly an error because these frames must, at the very
+ // least, save %ebp. For this reason, in addition to those given above
+ // about the use of .raSearchStart, the stack may need to be scanned
+ // for a better return address and a better frame pointer after the
+ // program string is evaluated.
+ //
+ // %eip_new = *(%esp_old + callee_params + saved_regs + locals)
+ // %ebp_new = *(%esp_old + callee_params + saved_regs - 8)
+ // %esp_new = %esp_old + callee_params + saved_regs + locals + 4
+ program_string = "$eip .raSearchStart ^ = "
+ "$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = "
+ "$esp .raSearchStart 4 + =";
} else {
- // No FPO information is available for the last frame. Assume that the
- // standard %ebp-using x86 calling convention is in use.
+ // The function corresponding to the last frame doesn't use %ebp at
+ // all. The callee frame is located relative to %esp.
//
- // 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 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. The
- // calling procedure's stack pointer (%esp) is 8 higher than the value
- // of the called procedure's frame pointer at the time the calling
- // procedure made the CALL: 4 bytes for the return address pushed by the
- // CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
- // pointer.
+ // The called procedure's instruction pointer and stack pointer are
+ // recovered in the same way as the case above, except that no
+ // frame pointer (%ebp) is used at all, so it is not saved anywhere
+ // in the callee's stack frame and does not need to be recovered.
+ // Because %ebp wasn't used in the callee, whatever value it has
+ // is the value that it had in the caller, so it can be carried
+ // straight through without bringing its validity into question.
//
- // Instruction and frame pointer recovery for these traditional frames is
- // entirely deterministic, and the stack will not be scanned after
- // recovering these values.
+ // Because of the use of .raSearchStart, the stack will possibly be
+ // examined to locate a better return address after program string
+ // evaluation. The stack will not be examined to locate a saved
+ // %ebp value, because these frames do not save (or use) %ebp.
//
- // %eip_new = *(%ebp_old + 4)
- // %esp_new = %ebp_old + 8
- // %ebp_new = *(%ebp_old)
- trust = StackFrameX86::FRAME_TRUST_FP;
- program_string = "$eip $ebp 4 + ^ = "
- "$esp $ebp 8 + = "
- "$ebp $ebp ^ =";
+ // %eip_new = *(%esp_old + callee_params + saved_regs + locals)
+ // %esp_new = %esp_old + callee_params + saved_regs + locals + 4
+ // %ebp_new = %ebp_old
+ program_string = "$eip .raSearchStart ^ = "
+ "$esp .raSearchStart 4 + =";
+ recover_ebp = false;
}
// Now crank it out, making sure that the program string set at least the
@@ -331,15 +288,14 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
trust = StackFrameX86::FRAME_TRUST_SCAN;
}
- // If this stack frame did not use %ebp in a traditional way, locating the
- // return address isn't entirely deterministic. In that case, the stack
- // can be scanned to locate the return address.
+ // Since this stack frame did not use %ebp in a traditional way,
+ // locating the return address isn't entirely deterministic. In that
+ // case, the stack can be scanned to locate the return address.
//
- // Even in nontraditional frames, if program string evaluation resulted in
- // both %eip and %ebp values of 0, trust that the end of the stack has been
+ // However, if program string evaluation resulted in both %eip and
+ // %ebp values of 0, trust that the end of the stack has been
// reached and don't scan for anything else.
- if (!traditional_frame &&
- (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0)) {
+ if (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0) {
int offset = 0;
// This scan can only be done if a CodeModules object is available, to
@@ -401,13 +357,6 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
}
}
- // Treat an instruction address of 0 as end-of-stack. Treat incorrect stack
- // direction as end-of-stack to enforce progress and avoid infinite loops.
- if (dictionary["$eip"] == 0 ||
- dictionary["$esp"] <= last_frame->context.esp) {
- return NULL;
- }
-
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameX86 *frame = new StackFrameX86();
@@ -436,19 +385,129 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI;
}
- // frame->context.eip is the return address, which is one instruction
- // past the CALL that caused us to arrive at the callee. Set
- // frame->instruction to one less than that. This won't reference the
- // beginning of the CALL instruction, but it's guaranteed to be within the
- // CALL, which is sufficient to get the source line information to match up
- // with the line that contains a function call. Callers that require the
- // exact return address value may access the context.eip field of
- // StackFrameX86.
- frame->instruction = frame->context.eip - 1;
+ return frame;
+}
+
+StackFrameX86 *StackwalkerX86::GetCallerByEBPAtBase(
+ const vector<StackFrame *> &frames) {
+ StackFrameX86::FrameTrust trust;
+ StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
+ u_int32_t last_esp = last_frame->context.esp;
+ u_int32_t last_ebp = last_frame->context.ebp;
+
+ // Assume that the standard %ebp-using x86 calling convention is in
+ // use.
+ //
+ // 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 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. The
+ // calling procedure's stack pointer (%esp) is 8 higher than the value
+ // of the called procedure's frame pointer at the time the calling
+ // procedure made the CALL: 4 bytes for the return address pushed by the
+ // CALL itself, and 4 bytes for the callee's PUSH of the caller's frame
+ // pointer.
+ //
+ // %eip_new = *(%ebp_old + 4)
+ // %esp_new = %ebp_old + 8
+ // %ebp_new = *(%ebp_old)
+
+ u_int32_t caller_eip, caller_esp, caller_ebp;
+
+ if (memory_->GetMemoryAtAddress(last_ebp + 4, &caller_eip) &&
+ memory_->GetMemoryAtAddress(last_ebp, &caller_ebp)) {
+ caller_esp = last_ebp + 8;
+ trust = StackFrameX86::FRAME_TRUST_FP;
+ } else {
+ // We couldn't read the memory %ebp refers to. It may be that %ebp
+ // is pointing to non-stack memory. We'll scan the stack for a
+ // return address. This can happen if last_frame is executing code
+ // for a module for which we don't have symbols, and that module
+ // is compiled without a frame pointer.
+ if (!ScanForReturnAddress(last_esp, &caller_esp, &caller_eip)) {
+ // if we can't find an instruction pointer even with stack scanning,
+ // give up.
+ return false;
+ }
+
+ // ScanForReturnAddress found a reasonable return address. Advance
+ // %esp to the location above the one where the return address was
+ // found. Assume that %ebp is unchanged.
+ caller_esp += 4;
+ caller_ebp = last_ebp;
+
+ trust = StackFrameX86::FRAME_TRUST_SCAN;
+ }
+
+ // Create a new stack frame (ownership will be transferred to the caller)
+ // and fill it in.
+ StackFrameX86 *frame = new StackFrameX86();
+
+ frame->trust = trust;
+ frame->context = last_frame->context;
+ frame->context.eip = caller_eip;
+ frame->context.esp = caller_esp;
+ frame->context.ebp = caller_ebp;
+ frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
+ StackFrameX86::CONTEXT_VALID_ESP |
+ StackFrameX86::CONTEXT_VALID_EBP;
return frame;
}
+StackFrame *StackwalkerX86::GetCallerFrame(const CallStack *stack) {
+ if (!memory_ || !stack) {
+ BPLOG(ERROR) << "Can't get caller frame without memory or stack";
+ return NULL;
+ }
+
+ const vector<StackFrame *> &frames = *stack->frames();
+ StackFrameX86 *last_frame = static_cast<StackFrameX86 *>(frames.back());
+ scoped_ptr<StackFrameX86> new_frame;
+
+ // If we have Windows stack walking information, use that.
+ WindowsFrameInfo *windows_frame_info
+ = resolver_->FindWindowsFrameInfo(last_frame);
+ if (windows_frame_info)
+ new_frame.reset(GetCallerByWindowsFrameInfo(frames, windows_frame_info));
+
+ // Otherwise, hope that we're using a traditional frame structure.
+ if (!new_frame.get())
+ new_frame.reset(GetCallerByEBPAtBase(frames));
+
+ // If nothing worked, tell the caller.
+ if (!new_frame.get())
+ return NULL;
+
+ // Treat an instruction address of 0 as end-of-stack.
+ if (new_frame->context.eip == 0)
+ return NULL;
+
+ // If the new stack pointer is at a lower address than the old, then
+ // that's clearly incorrect. Treat this as end-of-stack to enforce
+ // progress and avoid infinite loops.
+ if (new_frame->context.esp <= last_frame->context.esp)
+ return NULL;
+
+ // new_frame->context.eip is the return address, which is one instruction
+ // past the CALL that caused us to arrive at the callee. Set
+ // new_frame->instruction to one less than that. This won't reference the
+ // beginning of the CALL instruction, but it's guaranteed to be within
+ // the CALL, which is sufficient to get the source line information to
+ // match up with the line that contains a function call. Callers that
+ // require the exact return address value may access the context.eip
+ // field of StackFrameX86.
+ new_frame->instruction = new_frame->context.eip - 1;
+
+ return new_frame.release();
+}
+
bool StackwalkerX86::ScanForReturnAddress(u_int32_t location_start,
u_int32_t *location_found,
u_int32_t *eip_found) {
diff --git a/src/processor/stackwalker_x86.h b/src/processor/stackwalker_x86.h
index 255c16b3..b4d134ea 100644
--- a/src/processor/stackwalker_x86.h
+++ b/src/processor/stackwalker_x86.h
@@ -1,3 +1,5 @@
+// -*- mode: c++ -*-
+
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
@@ -42,6 +44,7 @@
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
namespace google_breakpad {
@@ -66,8 +69,22 @@ class StackwalkerX86 : public Stackwalker {
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
// alternate conventions as guided by any WindowsFrameInfo available for the
// code in question.).
- virtual StackFrame* GetContextFrame();
- virtual StackFrame* GetCallerFrame(const CallStack *stack);
+ virtual StackFrame *GetContextFrame();
+ virtual StackFrame *GetCallerFrame(const CallStack *stack);
+
+ // Use windows_frame_info (derived from STACK WIN and FUNC records)
+ // to construct the frame that called frames.back(). The caller
+ // takes ownership of the returned frame. Return NULL on failure.
+ StackFrameX86 *GetCallerByWindowsFrameInfo(
+ const vector<StackFrame*> &frames,
+ WindowsFrameInfo *windows_frame_info);
+
+ // Assuming a traditional frame layout --- where the caller's %ebp
+ // has been pushed just after the return address and the callee's
+ // %ebp points to the saved %ebp --- construct the frame that called
+ // frames.back(). The caller takes ownership of the returned frame.
+ // Return NULL on failure.
+ StackFrameX86 *GetCallerByEBPAtBase(const vector<StackFrame*> &frames);
// Scan the stack starting at location_start, looking for an address
// that looks like a valid instruction pointer. Addresses must