Handle frame pointer omission, (#21), part 4 (final part!): FPO stackwalker.

r=bryner
 - This change allows Airbag to properly walk win32 stacks produced by code
   built with MSVC's frame pointer omission optimization (/Oy).  This
   optimization is enabled at /O1 and /O2.
 - There too many interface and file format changes to list here.

http://groups.google.com/group/airbag-dev/browse_thread/thread/85ce85bfa8457ece


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@42 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
mmentovai 2006-10-20 01:46:38 +00:00
parent 5afd60b067
commit 246f406828
33 changed files with 2643 additions and 1744 deletions

View file

@ -0,0 +1,50 @@
// Copyright (c) 2006, 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.
// call_stack.cc: A call stack comprised of stack frames.
//
// See call_stack.h for documentation.
//
// Author: Mark Mentovai
#include "google/call_stack.h"
#include "google/stack_frame.h"
#include "processor/linked_ptr.h"
namespace google_airbag {
CallStack::~CallStack() {
for (vector<StackFrame *>::const_iterator iterator = frames_.begin();
iterator != frames_.end();
++iterator) {
delete *iterator;
}
}
} // namespace google_airbag

View file

@ -53,6 +53,8 @@
#ifndef PROCESSOR_LINKED_PTR_H__
#define PROCESSOR_LINKED_PTR_H__
namespace google_airbag {
// This is used internally by all instances of linked_ptr<>. It needs to be
// a non-template class because different types of linked_ptr<> can refer to
// the same object (linked_ptr<Superclass>(obj) vs linked_ptr<Subclass>(obj)).
@ -186,4 +188,6 @@ linked_ptr<T> make_linked_ptr(T* ptr) {
return linked_ptr<T>(ptr);
}
#endif // PROCESSOR_LINKED_PTR_H__
} // namespace google_airbag
#endif // PROCESSOR_LINKED_PTR_H__

View file

@ -647,7 +647,7 @@ const u_int8_t* MinidumpMemoryRegion::GetMemory() {
return NULL;
// TODO(mmentovai): verify rational size!
auto_ptr<vector<u_int8_t> > memory(
auto_ptr< vector<u_int8_t> > memory(
new vector<u_int8_t>(descriptor_->memory.data_size));
if (!minidump_->ReadBytes(&(*memory)[0], descriptor_->memory.data_size))
@ -1086,7 +1086,7 @@ const u_int8_t* MinidumpModule::GetCVRecord() {
// variable-sized due to their pdb_file_name fields; these structures
// are not sizeof(MDCVInfoPDB70) or sizeof(MDCVInfoPDB20) and treating
// them as such would result in incomplete structures or overruns.
auto_ptr<vector<u_int8_t> > cv_record(
auto_ptr< vector<u_int8_t> > cv_record(
new vector<u_int8_t>(module_.cv_record.data_size));
if (!minidump_->ReadBytes(&(*cv_record)[0], module_.cv_record.data_size))
@ -1161,7 +1161,7 @@ const MDImageDebugMisc* MinidumpModule::GetMiscRecord() {
// because the MDImageDebugMisc is variable-sized due to its data field;
// this structure is not sizeof(MDImageDebugMisc) and treating it as such
// would result in an incomplete structure or an overrun.
auto_ptr<vector<u_int8_t> > misc_record_mem(
auto_ptr< vector<u_int8_t> > misc_record_mem(
new vector<u_int8_t>(module_.misc_record.data_size));
MDImageDebugMisc* misc_record =
reinterpret_cast<MDImageDebugMisc*>(&(*misc_record_mem)[0]);

View file

@ -45,7 +45,7 @@ MinidumpProcessor::~MinidumpProcessor() {
}
bool MinidumpProcessor::Process(const string &minidump_file,
StackFrames *stack_frames) {
CallStack *stack) {
Minidump dump(minidump_file);
if (!dump.Read()) {
return false;
@ -79,7 +79,7 @@ bool MinidumpProcessor::Process(const string &minidump_file,
return false;
}
walker->Walk(stack_frames);
walker->Walk(stack);
return true;
}

View file

@ -31,13 +31,15 @@
// corresponding symbol file, and checks the stack frames for correctness.
#include <string>
#include "google/call_stack.h"
#include "google/minidump_processor.h"
#include "google/stack_frame.h"
#include "google/symbol_supplier.h"
#include "processor/minidump.h"
using std::string;
using google_airbag::CallStack;
using google_airbag::MinidumpProcessor;
using google_airbag::StackFrames;
#define ASSERT_TRUE(cond) \
if (!(cond)) { \
@ -72,40 +74,40 @@ static bool RunTests() {
TestSymbolSupplier supplier;
MinidumpProcessor processor(&supplier);
StackFrames stack_frames;
CallStack stack;
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/minidump2.dmp";
ASSERT_TRUE(processor.Process(minidump_file, &stack_frames));
ASSERT_EQ(stack_frames.size(), 4);
ASSERT_TRUE(processor.Process(minidump_file, &stack));
ASSERT_EQ(stack.frames()->size(), 4);
ASSERT_EQ(stack_frames[0].module_base, 0x400000);
ASSERT_EQ(stack_frames[0].module_name, "c:\\test_app.exe");
ASSERT_EQ(stack_frames[0].function_name, "CrashFunction");
ASSERT_EQ(stack_frames[0].source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack_frames[0].source_line, 36);
ASSERT_EQ(stack.frames()->at(0)->module_base, 0x400000);
ASSERT_EQ(stack.frames()->at(0)->module_name, "c:\\test_app.exe");
ASSERT_EQ(stack.frames()->at(0)->function_name, "CrashFunction()");
ASSERT_EQ(stack.frames()->at(0)->source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack.frames()->at(0)->source_line, 65);
ASSERT_EQ(stack_frames[1].module_base, 0x400000);
ASSERT_EQ(stack_frames[1].module_name, "c:\\test_app.exe");
ASSERT_EQ(stack_frames[1].function_name, "main");
ASSERT_EQ(stack_frames[1].source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack_frames[1].source_line, 42);
ASSERT_EQ(stack.frames()->at(1)->module_base, 0x400000);
ASSERT_EQ(stack.frames()->at(1)->module_name, "c:\\test_app.exe");
ASSERT_EQ(stack.frames()->at(1)->function_name, "main");
ASSERT_EQ(stack.frames()->at(1)->source_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack.frames()->at(1)->source_line, 70);
// This comes from the CRT
ASSERT_EQ(stack_frames[2].module_base, 0x400000);
ASSERT_EQ(stack_frames[2].module_name, "c:\\test_app.exe");
ASSERT_EQ(stack_frames[2].function_name, "__tmainCRTStartup");
ASSERT_EQ(stack_frames[2].source_file_name,
ASSERT_EQ(stack.frames()->at(2)->module_base, 0x400000);
ASSERT_EQ(stack.frames()->at(2)->module_name, "c:\\test_app.exe");
ASSERT_EQ(stack.frames()->at(2)->function_name, "__tmainCRTStartup");
ASSERT_EQ(stack.frames()->at(2)->source_file_name,
"f:\\rtm\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c");
ASSERT_EQ(stack_frames[2].source_line, 318);
ASSERT_EQ(stack.frames()->at(2)->source_line, 318);
// No debug info available for kernel32.dll
ASSERT_EQ(stack_frames[3].module_base, 0x7c800000);
ASSERT_EQ(stack_frames[3].module_name,
ASSERT_EQ(stack.frames()->at(3)->module_base, 0x7c800000);
ASSERT_EQ(stack.frames()->at(3)->module_name,
"C:\\WINDOWS\\system32\\kernel32.dll");
ASSERT_TRUE(stack_frames[3].function_name.empty());
ASSERT_TRUE(stack_frames[3].source_file_name.empty());
ASSERT_EQ(stack_frames[3].source_line, 0);
ASSERT_TRUE(stack.frames()->at(3)->function_name.empty());
ASSERT_TRUE(stack.frames()->at(3)->source_file_name.empty());
ASSERT_EQ(stack.frames()->at(3)->source_line, 0);
return true;
}

View file

@ -38,12 +38,15 @@
#include <memory>
#include <string>
#include "google/call_stack.h"
#include "google/stack_frame.h"
#include "processor/minidump.h"
#include "processor/stackwalker_x86.h"
using std::auto_ptr;
using std::string;
using google_airbag::CallStack;
using google_airbag::MemoryRegion;
using google_airbag::Minidump;
using google_airbag::MinidumpContext;
@ -52,7 +55,6 @@ using google_airbag::MinidumpModuleList;
using google_airbag::MinidumpThread;
using google_airbag::MinidumpThreadList;
using google_airbag::StackFrame;
using google_airbag::StackFrames;
using google_airbag::Stackwalker;
@ -112,18 +114,17 @@ int main(int argc, char **argv) {
exit(1);
}
StackFrames stack;
CallStack stack;
stackwalker->Walk(&stack);
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",
for (index = 0; index < stack.frames()->size(); ++index) {
StackFrame *frame = stack.frames()->at(index);
printf("[%2d] instruction = 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);
frame->instruction,
frame->module_base ? frame->module_name.c_str() : "0x0",
frame->instruction - frame->module_base);
}
return 0;

View file

@ -47,7 +47,8 @@ class AutoStackClearer {
template<typename ValueType>
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression) {
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
DictionaryValidityType *assigned) {
// Ensure that the stack is cleared before returning.
AutoStackClearer clearer(&stack_);
@ -142,6 +143,8 @@ bool PostfixEvaluator<ValueType>::Evaluate(const string &expression) {
return false;
(*dictionary_)[identifier] = value;
if (assigned)
(*assigned)[identifier] = true;
} else {
// The token is not an operator, it's a literal value or an identifier.
// Push it onto the stack as-is. Use push_back instead of PushValue

View file

@ -69,6 +69,7 @@ template<typename ValueType>
class PostfixEvaluator {
public:
typedef map<string, ValueType> DictionaryType;
typedef map<string, bool> DictionaryValidityType;
// Create a PostfixEvaluator object that may be used (with Evaluate) on
// one or more expressions. PostfixEvaluator does not take ownership of
@ -82,8 +83,11 @@ class PostfixEvaluator {
// Evaluate the expression. The results of execution will be stored
// in one (or more) variables in the dictionary. Returns false if any
// failures occure during execution, leaving variables in the dictionary
// in an indeterminate state.
bool Evaluate(const string &expression);
// in an indeterminate state. If assigned is non-NULL, any keys set in
// the dictionary as a result of evaluation will also be set to true in
// assigned, providing a way to determine if an expression modifies any
// of its input variables.
bool Evaluate(const string &expression, DictionaryValidityType *assigned);
DictionaryType* dictionary() const { return dictionary_; }

View file

@ -61,7 +61,7 @@ struct EvaluateTest {
struct EvaluateTestSet {
// The dictionary used for all tests in the set.
map<string, unsigned int> *dictionary;
PostfixEvaluator<unsigned int>::DictionaryType *dictionary;
// The list of tests.
const EvaluateTest *evaluate_tests;
@ -77,7 +77,7 @@ struct EvaluateTestSet {
bool RunTests() {
// The first test set checks the basic operations and failure modes.
map<string, unsigned int> dictionary_0;
PostfixEvaluator<unsigned int>::DictionaryType dictionary_0;
const EvaluateTest evaluate_tests_0[] = {
{ "$rAdd 2 2 + =", true }, // $rAdd = 2 + 2 = 4
{ "$rAdd $rAdd 2 + =", true }, // $rAdd = $rAdd + 2 = 6
@ -122,7 +122,7 @@ bool RunTests() {
// The data is fudged a little bit because the tests use FakeMemoryRegion
// instead of a real stack snapshot, but the program strings are real and
// the implementation doesn't know or care that the data is not real.
map<string, unsigned int> dictionary_1;
PostfixEvaluator<unsigned int>::DictionaryType dictionary_1;
dictionary_1["$ebp"] = 0xbfff0010;
dictionary_1["$eip"] = 0x10000000;
dictionary_1["$esp"] = 0xbfff0000;
@ -186,13 +186,17 @@ bool RunTests() {
// tests can affect the state of the dictionary for later tests.
postfix_evaluator.set_dictionary(evaluate_test_set->dictionary);
// Use a new validity dictionary for each test set.
PostfixEvaluator<unsigned int>::DictionaryValidityType assigned;
for (unsigned int evaluate_test_index = 0;
evaluate_test_index < evaluate_test_count;
++evaluate_test_index) {
const EvaluateTest *evaluate_test = &evaluate_tests[evaluate_test_index];
// Do the test.
bool result = postfix_evaluator.Evaluate(evaluate_test->expression);
bool result = postfix_evaluator.Evaluate(evaluate_test->expression,
&assigned);
if (result != evaluate_test->evaluable) {
fprintf(stderr, "FAIL: evaluate set %d/%d, test %d/%d, "
"expression \"%s\", expected %s, observed %s\n",
@ -236,6 +240,24 @@ bool RunTests() {
identifier.c_str(), expected_value, observed_value);
return false;
}
// The value must be set in the "assigned" dictionary if it was a
// variable. It must not have been assigned if it was a constant.
bool expected_assigned = identifier[0] == '$';
bool observed_assigned = false;
PostfixEvaluator<unsigned int>::DictionaryValidityType::const_iterator
iterator_assigned = assigned.find(identifier);
if (iterator_assigned != assigned.end()) {
observed_assigned = iterator_assigned->second;
}
if (expected_assigned != observed_assigned) {
fprintf(stderr, "FAIL: evaluate test set %d/%d, "
"validate assignment of \"%s\", "
"expected %d, observed %d\n",
evaluate_test_set_index, evaluate_test_set_count,
identifier.c_str(), expected_assigned, observed_assigned);
return false;
}
}
}

View file

@ -62,13 +62,19 @@ struct SourceLineResolver::Line {
struct SourceLineResolver::Function {
Function(const string &function_name,
MemAddr function_address,
MemAddr code_size)
: name(function_name), address(function_address), size(code_size) { }
MemAddr code_size,
int set_parameter_size)
: name(function_name), address(function_address), size(code_size),
parameter_size(set_parameter_size) { }
string name;
MemAddr address;
MemAddr size;
RangeMap<MemAddr, linked_ptr<Line> > lines;
// The size of parameters passed to this function on the stack.
int parameter_size;
RangeMap< MemAddr, linked_ptr<Line> > lines;
};
class SourceLineResolver::Module {
@ -128,7 +134,7 @@ class SourceLineResolver::Module {
string name_;
FileMap files_;
RangeMap<MemAddr, linked_ptr<Function> > functions_;
RangeMap< MemAddr, linked_ptr<Function> > functions_;
// Each element in the array is a ContainedRangeMap for a type listed in
// StackInfoTypes. These are split by type because there may be overlaps
@ -184,7 +190,10 @@ bool SourceLineResolver::Module::LoadMap(const string &map_file) {
return false;
}
char buffer[1024];
// TODO(mmentovai): this might not be large enough to handle really long
// lines, which might be present for FUNC lines of highly-templatized
// code.
char buffer[8192];
Function *cur_func = NULL;
while (fgets(buffer, sizeof(buffer), f)) {
@ -201,6 +210,8 @@ bool SourceLineResolver::Module::LoadMap(const string &map_file) {
}
functions_.StoreRange(cur_func->address, cur_func->size,
linked_ptr<Function>(cur_func));
} else if (strncmp(buffer, "PUBLIC ", 7) == 0) {
// TODO(mmentovai): add a public map
} else {
if (!cur_func) {
return false;
@ -221,18 +232,19 @@ bool SourceLineResolver::Module::LoadMap(const string &map_file) {
void SourceLineResolver::Module::LookupAddress(
MemAddr address, StackFrame *frame, StackFrameInfo *frame_info) const {
if (frame_info) {
frame_info->valid = StackFrameInfo::VALID_NONE;
// Check for debugging info first, before any possible early returns.
// The caller will know that frame_info was filled in by checking its
// valid field.
//
// We only know about STACK_INFO_FRAME_DATA and STACK_INFO_FPO.
// STACK_INFO_STANDARD looks like it would do the right thing, too.
// Prefer them in this order.
// We only know about STACK_INFO_FRAME_DATA and STACK_INFO_FPO. Prefer
// them in this order. STACK_INFO_FRAME_DATA is the newer type that
// includes its own program string. STACK_INFO_FPO is the older type
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
if (!stack_info_[STACK_INFO_FRAME_DATA].RetrieveRange(address,
frame_info)) {
if (!stack_info_[STACK_INFO_FPO].RetrieveRange(address, frame_info)) {
stack_info_[STACK_INFO_STANDARD].RetrieveRange(address, frame_info);
}
stack_info_[STACK_INFO_FPO].RetrieveRange(address, frame_info);
}
}
@ -252,6 +264,16 @@ void SourceLineResolver::Module::LookupAddress(
frame->source_file_name = files_.find(line->source_file_id)->second;
}
frame->source_line = line->line;
if (frame_info &&
!(frame_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE)) {
// Even without a relevant STACK line, many functions contain information
// about how much space their parameters consume on the stack. Prefer
// the STACK stuff (above), but if it's not present, take the
// information from the FUNC line.
frame_info->parameter_size = func->parameter_size;
frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE;
}
}
// static
@ -303,19 +325,20 @@ void SourceLineResolver::Module::ParseFile(char *file_line) {
SourceLineResolver::Function* SourceLineResolver::Module::ParseFunction(
char *function_line) {
// FUNC <address> <name>
// FUNC <address> <stack_param_size> <name>
function_line += 5; // skip prefix
vector<char*> tokens;
if (!Tokenize(function_line, 3, &tokens)) {
if (!Tokenize(function_line, 4, &tokens)) {
return NULL;
}
u_int64_t address = strtoull(tokens[0], NULL, 16);
u_int64_t size = strtoull(tokens[1], NULL, 16);
char *name = tokens[2];
u_int64_t address = strtoull(tokens[0], NULL, 16);
u_int64_t size = strtoull(tokens[1], NULL, 16);
int stack_param_size = strtoull(tokens[2], NULL, 16);
char *name = tokens[3];
return new Function(name, address, size);
return new Function(name, address, size, stack_param_size);
}
SourceLineResolver::Line* SourceLineResolver::Module::ParseLine(
@ -340,17 +363,24 @@ SourceLineResolver::Line* SourceLineResolver::Module::ParseLine(
bool SourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
// STACK WIN <type> <rva> <code_size> <prolog_size> <epliog_size>
// <parameter_size> <saved_register_size> <local_size> <max_stack_size>
// <program_string>
// <has_program_string> <program_string_OR_allocates_base_pointer>
//
// If has_program_string is 1, the rest of the line is a program string.
// Otherwise, the final token tells whether the stack info indicates that
// a base pointer has been allocated.
//
// Expect has_program_string to be 1 when type is STACK_INFO_FRAME_DATA and
// 0 when type is STACK_INFO_FPO, but don't enforce this.
// Skip "STACK " prefix.
stack_info_line += 6;
vector<char*> tokens;
if (!Tokenize(stack_info_line, 11, &tokens))
if (!Tokenize(stack_info_line, 12, &tokens))
return false;
// Only MSVC stack frame info is understood for now.
char *platform = tokens[0];
const char *platform = tokens[0];
if (strcmp(platform, "WIN") != 0)
return false;
@ -358,15 +388,23 @@ bool SourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
if (type < 0 || type > STACK_INFO_LAST - 1)
return false;
u_int64_t rva = strtoull(tokens[2], NULL, 16);
u_int64_t code_size = strtoull(tokens[3], NULL, 16);
u_int32_t prolog_size = strtoul(tokens[4], NULL, 16);
u_int32_t epilog_size = strtoul(tokens[5], NULL, 16);
u_int32_t parameter_size = strtoul(tokens[6], NULL, 16);
u_int32_t saved_register_size = strtoul(tokens[7], NULL, 16);
u_int32_t local_size = strtoul(tokens[8], NULL, 16);
u_int32_t max_stack_size = strtoul(tokens[9], NULL, 16);
char *program_string = tokens[10];
u_int64_t rva = strtoull(tokens[2], NULL, 16);
u_int64_t code_size = strtoull(tokens[3], NULL, 16);
u_int32_t prolog_size = strtoul(tokens[4], NULL, 16);
u_int32_t epilog_size = strtoul(tokens[5], NULL, 16);
u_int32_t parameter_size = strtoul(tokens[6], NULL, 16);
u_int32_t saved_register_size = strtoul(tokens[7], NULL, 16);
u_int32_t local_size = strtoul(tokens[8], NULL, 16);
u_int32_t max_stack_size = strtoul(tokens[9], NULL, 16);
int has_program_string = strtoul(tokens[10], NULL, 16);
const char *program_string = "";
int allocates_base_pointer = 0;
if (has_program_string) {
program_string = tokens[11];
} else {
allocates_base_pointer = strtoul(tokens[11], NULL, 16);
}
// TODO(mmentovai): I wanted to use StoreRange's return value as this
// method's return value, but MSVC infrequently outputs stack info that
@ -395,6 +433,7 @@ bool SourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
saved_register_size,
local_size,
max_stack_size,
allocates_base_pointer,
program_string));
return true;

View file

@ -81,6 +81,7 @@ static bool RunTests() {
ASSERT_EQ(frame.function_name, "Function1_1");
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
ASSERT_EQ(frame.source_line, 44);
ASSERT_FALSE(frame_info.allocates_base_pointer);
ASSERT_EQ(frame_info.program_string,
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
@ -88,6 +89,7 @@ static bool RunTests() {
frame.instruction = 0x800;
resolver.FillSourceLineInfo(&frame, &frame_info);
ASSERT_TRUE(VerifyEmpty(frame));
ASSERT_FALSE(frame_info.allocates_base_pointer);
ASSERT_TRUE(frame_info.program_string.empty());
frame.instruction = 0x1280;
@ -95,6 +97,7 @@ static bool RunTests() {
ASSERT_EQ(frame.function_name, "Function1_3");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
ASSERT_FALSE(frame_info.allocates_base_pointer);
ASSERT_TRUE(frame_info.program_string.empty());
frame.instruction = 0x1380;
@ -102,6 +105,7 @@ static bool RunTests() {
ASSERT_EQ(frame.function_name, "Function1_4");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
ASSERT_FALSE(frame_info.allocates_base_pointer);
ASSERT_FALSE(frame_info.program_string.empty());
frame.instruction = 0x2180;

View file

@ -46,13 +46,20 @@ namespace google_airbag {
struct StackFrameInfo {
public:
StackFrameInfo() : valid(false),
enum Validity {
VALID_NONE = 0,
VALID_PARAMETER_SIZE = 1,
VALID_ALL = -1
};
StackFrameInfo() : valid(VALID_NONE),
prolog_size(0),
epilog_size(0),
parameter_size(0),
saved_register_size(0),
local_size(0),
max_stack_size(0),
allocates_base_pointer(0),
program_string() {}
StackFrameInfo(u_int32_t set_prolog_size,
@ -61,18 +68,27 @@ struct StackFrameInfo {
u_int32_t set_saved_register_size,
u_int32_t set_local_size,
u_int32_t set_max_stack_size,
int set_allocates_base_pointer,
const std::string set_program_string)
: valid(true),
: valid(VALID_ALL),
prolog_size(set_prolog_size),
epilog_size(set_epilog_size),
parameter_size(set_parameter_size),
saved_register_size(set_saved_register_size),
local_size(set_local_size),
max_stack_size(set_max_stack_size),
allocates_base_pointer(set_allocates_base_pointer),
program_string(set_program_string) {}
// True when the contents of the structure are valid.
bool valid;
// Clears the StackFrameInfo object so that users will see it as though
// it contains no information.
void Clear() { valid = VALID_NONE; program_string.erase(); }
// Identifies which fields in the structure are valid. This is of
// type Validity, but it is defined as an int because it's not
// possible to OR values into an enumerated type. Users must check
// this field before using any other.
int valid;
// These values come from IDiaFrameData.
u_int32_t prolog_size;
@ -81,6 +97,10 @@ struct StackFrameInfo {
u_int32_t saved_register_size;
u_int32_t local_size;
u_int32_t max_stack_size;
// Only one of allocates_base_pointer or program_string will be valid.
// If program_string is empty, use allocates_base_pointer.
bool allocates_base_pointer;
std::string program_string;
};

View file

@ -37,6 +37,8 @@
#include <memory>
#include "processor/stackwalker.h"
#include "google/call_stack.h"
#include "google/stack_frame.h"
#include "google/symbol_supplier.h"
#include "processor/minidump.h"
#include "processor/source_line_resolver.h"
@ -55,22 +57,23 @@ Stackwalker::Stackwalker(MemoryRegion *memory, MinidumpModuleList *modules,
}
void Stackwalker::Walk(StackFrames *frames) {
frames->clear();
void Stackwalker::Walk(CallStack *stack) {
stack_frame_info_.clear();
SourceLineResolver resolver;
// Begin with the context frame, and keep getting callers until there are
// no more.
auto_ptr<StackFrame> frame(new StackFrame());
auto_ptr<StackFrameInfo> frame_info(new StackFrameInfo());
bool valid = GetContextFrame(frame.get());
while (valid) {
// Take ownership of the pointer returned by GetContextFrame.
auto_ptr<StackFrame> frame(GetContextFrame());
while (frame.get()) {
// 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).
StackFrameInfo frame_info;
// Resolve the module information, if a module map was provided.
if (modules_) {
MinidumpModule *module =
@ -84,22 +87,20 @@ void Stackwalker::Walk(StackFrames *frames) {
resolver.LoadModule(frame->module_name, symbol_file);
}
}
resolver.FillSourceLineInfo(frame.get(), frame_info.get());
resolver.FillSourceLineInfo(frame.get(), &frame_info);
}
}
// Copy the frame into the frames vector.
frames->push_back(*frame);
stack_frame_info_.push_back(*frame_info);
// Add the frame to the call stack. Relinquish the ownership claim
// over the frame, because the stack now owns it.
stack->frames_.push_back(frame.release());
// 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());
frame_info.reset(new StackFrameInfo());
// Copy the frame info.
stack_frame_info_.push_back(frame_info);
frame_info.Clear();
// Get the next frame.
valid = GetCallerFrame(frame.get(), frames);
// Get the next frame and take ownership.
frame.reset(GetCallerFrame(stack));
}
}

View file

@ -33,8 +33,7 @@
// 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.
// Stackwalker assembles these StackFrame strucutres into a CallStack.
//
// Author: Mark Mentovai
@ -44,14 +43,15 @@
#include <vector>
#include "google/stack_frame.h"
#include "processor/memory_region.h"
#include "processor/stack_frame_info.h"
namespace google_airbag {
class CallStack;
class MemoryRegion;
class MinidumpContext;
class MinidumpModuleList;
struct StackFrame;
class SymbolSupplier;
@ -59,10 +59,9 @@ class Stackwalker {
public:
virtual ~Stackwalker() {}
// Fills the given vector of StackFrames by calling GetContextFrame and
// GetCallerFrame, and populating the returned frames with all available
// data.
void Walk(StackFrames *frames);
// Fills the given CallStack by calling GetContextFrame and GetCallerFrame,
// and populating the returned frames with all available data.
void Walk(CallStack* stack);
// Returns a new concrete subclass suitable for the CPU that a stack was
// generated on, according to the CPU type indicated by the context
@ -88,23 +87,26 @@ class Stackwalker {
MemoryRegion *memory_;
// Additional debugging information for each stack frame. This vector
// parallels the StackFrames vector. Subclasses may use this information
// to walk the stack.
// parallels the CallStack. Subclasses may use this information to help
// walk the stack.
std::vector<StackFrameInfo> stack_frame_info_;
private:
// Obtains the context frame, the innermost called procedure in a stack
// trace. Returns false on failure.
virtual bool GetContextFrame(StackFrame *frame) = 0;
// trace. Returns NULL on failure. GetContextFrame allocates a new
// StackFrame (or StackFrame subclass), ownership of which is taken by
// the caller.
virtual StackFrame* GetContextFrame() = 0;
// Obtains a caller frame. Each call to GetCallerFrame should return the
// frame that called the last frame returned by GetContextFrame or
// GetCallerFrame. To aid this purpose, walked_frames contains the
// StackFrames vector of frames that have already been walked.
// 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,
const StackFrames *walked_frames) = 0;
// GetCallerFrame. To aid this purpose, stack contains the CallStack
// made of frames that have already been walked. GetCallerFrame should
// return NULL on failure or when there are no more caller frames (when
// the end of the stack has been reached). GetCallerFrame allocates a new
// StackFrame (or StackFrame subclass), ownership of which is taken by
// the caller.
virtual StackFrame* GetCallerFrame(const CallStack *stack) = 0;
// A list of modules, for populating each StackFrame's module information.
// This field is optional and may be NULL.

View file

@ -35,6 +35,8 @@
#include "processor/stackwalker_ppc.h"
#include "google/call_stack.h"
#include "google/stack_frame_cpu.h"
#include "processor/minidump.h"
namespace google_airbag {
@ -55,65 +57,77 @@ StackwalkerPPC::StackwalkerPPC(const MDRawContextPPC *context,
}
bool StackwalkerPPC::GetContextFrame(StackFrame *frame) {
if (!context_ || !memory_ || !frame)
return false;
StackFrame* StackwalkerPPC::GetContextFrame() {
if (!context_ || !memory_)
return NULL;
// The stack frame and instruction pointers are stored directly in
// registers, so pull them straight out of the CPU context structure.
frame->frame_pointer = context_->gpr[1];
frame->instruction = context_->srr0;
StackFramePPC *frame = new StackFramePPC();
return true;
// The instruction pointer is stored directly in a register, so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = StackFramePPC::CONTEXT_VALID_ALL;
frame->instruction = frame->context.srr0;
return frame;
}
bool StackwalkerPPC::GetCallerFrame(StackFrame *frame,
const StackFrames *walked_frames) {
if (!memory_ || !frame || !walked_frames)
return false;
StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack)
return NULL;
// The stack frame and instruction pointers for previous frames are saved
// on the stack. The typical ppc calling convention is for the called
// procedure to store its return address in the calling procedure's stack
// frame at 8(%r1), and to allocate its own stack frame by decrementing %r1
// (the stack pointer) and saving the old value of %r1 at 0(%r1). Because
// the ppc has no hardware stack, there is no distinction between the
// stack pointer and frame pointer, and what is typically thought of as
// the frame pointer on an x86 is usually referred to as the stack pointer
// on a ppc.
// The instruction pointers for previous frames are saved on the stack.
// The typical ppc calling convention is for the called procedure to store
// its return address in the calling procedure's stack frame at 8(%r1),
// and to allocate its own stack frame by decrementing %r1 (the stack
// pointer) and saving the old value of %r1 at 0(%r1). Because the ppc has
// no hardware stack, there is no distinction between the stack pointer and
// frame pointer, and what is typically thought of as the frame pointer on
// an x86 is usually referred to as the stack pointer on a ppc.
u_int32_t last_stack_pointer = walked_frames->back().frame_pointer;
// 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 stack_pointer;
if (!memory_->GetMemoryAtAddress(last_stack_pointer, &stack_pointer))
return false;
StackFramePPC *last_frame = static_cast<StackFramePPC*>(
stack->frames()->back());
// 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 (stack_pointer <= last_stack_pointer)
return false;
u_int32_t instruction;
if (!memory_->GetMemoryAtAddress(stack_pointer + 8, &instruction))
return false;
u_int32_t stack_pointer;
if (!memory_->GetMemoryAtAddress(last_frame->context.gpr[1],
&stack_pointer) ||
stack_pointer <= last_frame->context.gpr[1]) {
return NULL;
}
// Mac OS X/Darwin gives 1 as the return address from the bottom-most
// frame in a stack (a thread's entry point). I haven't found any
// documentation on this, but 0 or 1 would be bogus return addresses,
// so check for them here and return false (end of stack) when they're
// hit to avoid having a phantom frame.
if (instruction <= 1)
return false;
u_int32_t instruction;
if (!memory_->GetMemoryAtAddress(stack_pointer + 8, &instruction) ||
instruction <= 1) {
return NULL;
}
frame->frame_pointer = stack_pointer;
frame->instruction = instruction;
StackFramePPC *frame = new StackFramePPC();
return true;
frame->context = last_frame->context;
frame->context.srr0 = instruction;
frame->context.gpr[1] = stack_pointer;
frame->context_validity = StackFramePPC::CONTEXT_VALID_SRR0 |
StackFramePPC::CONTEXT_VALID_GPR1;
// frame->context.srr0 is the return address, which is one instruction
// past the branch that caused us to arrive at the callee. Set
// frame_ppc->instruction to four less than that. Since all ppc
// instructions are 4 bytes wide, this is the address of the branch
// instruction. This allows 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.srr0 field of StackFramePPC.
frame->instruction = frame->context.srr0 - 4;
return frame;
}

View file

@ -64,9 +64,8 @@ class StackwalkerPPC : public Stackwalker {
// Implementation of Stackwalker, using ppc context (stack pointer in %r1,
// saved program counter in %srr0) and stack conventions (saved stack
// pointer at 0(%r1), return address at 8(0(%r1)).
virtual bool GetContextFrame(StackFrame *frame);
virtual bool GetCallerFrame(StackFrame *frame,
const StackFrames *walked_frames);
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.

View file

@ -40,13 +40,17 @@
#include <cstdio>
#include "google/airbag_types.h"
#include "google/call_stack.h"
#include "google/stack_frame.h"
#include "google/stack_frame_cpu.h"
#include "processor/memory_region.h"
#include "processor/minidump_format.h"
using google_airbag::CallStack;
using google_airbag::MemoryRegion;
using google_airbag::StackFrame;
using google_airbag::StackFrames;
using google_airbag::StackFramePPC;
using google_airbag::StackFrameX86;
#if defined(__i386__)
#include "processor/stackwalker_x86.h"
@ -78,6 +82,14 @@ class SelfMemoryRegion : public MemoryRegion {
private:
template<typename T> bool GetMemoryAtAddressInternal(u_int64_t address,
T* value) {
// Without knowing what addresses are actually mapped, just assume that
// everything low is not mapped. This helps the stackwalker catch the
// end of a stack when it tries to dereference a null or low pointer
// in an attempt to find the caller frame. Other unmapped accesses will
// cause the program to crash, but that would properly be a test failure.
if (address < 0x100)
return false;
u_int8_t* memory = 0;
*value = *reinterpret_cast<const T*>(&memory[address]);
return true;
@ -105,6 +117,22 @@ static u_int32_t GetEBP() {
}
// The caller's %esp is 8 higher than the value of %ebp in this function,
// assuming that it's not inlined and that the standard prolog is used.
// The CALL instruction places a 4-byte return address on the stack above
// the caller's %esp, and this function's prolog will save the caller's %ebp
// on the stack as well, for another 4 bytes, before storing %esp in %ebp.
static u_int32_t GetESP() __attribute__((noinline));
static u_int32_t GetESP() {
u_int32_t ebp;
__asm__ __volatile__(
"movl %%ebp, %0"
: "=a" (ebp)
);
return ebp + 8;
}
// GetEIP returns the instruction pointer identifying the next instruction
// to execute after GetEIP returns. It obtains this information from the
// stack, where it was placed by the call instruction that called GetEIP.
@ -177,6 +205,7 @@ static unsigned int CountCallerFrames() {
MDRawContextX86 context = MDRawContextX86();
context.eip = GetEIP();
context.ebp = GetEBP();
context.esp = GetESP();
StackwalkerX86 stackwalker = StackwalkerX86(&context, &memory, NULL, NULL);
#elif defined(__ppc__)
@ -187,24 +216,32 @@ static unsigned int CountCallerFrames() {
StackwalkerPPC stackwalker = StackwalkerPPC(&context, &memory, NULL, NULL);
#endif // __i386__ || __ppc__
StackFrames stack;
CallStack stack;
stackwalker.Walk(&stack);
#ifdef PRINT_STACKS
printf("\n");
for(unsigned int frame_index = 0;
frame_index < stack.size();
frame_index < stack.Count();
++frame_index) {
StackFrame *frame = &stack[frame_index];
printf("frame %-3d instruction = 0x%08llx frame_pointer = 0x%08llx\n",
frame_index, frame->instruction, frame->frame_pointer);
StackFrame *frame = stack.FrameAt(frame_index);
printf("frame %-3d instruction = 0x%08llx",
frame_index, frame->instruction);
#if defined(__i386__)
StackFrameX86 *frame_x86 = reinterpret_cast<StackFrameX86*>(frame.get());
printf(" esp = 0x%08x ebp = 0x%08x\n",
frame_x86->context.esp, frame_x86->context.ebp);
#elif defined(__ppc__)
StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame.get());
printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]);
#endif // __i386__ || __ppc__
}
#endif // PRINT_STACKS
// Subtract 1 because the caller wants the number of frames beneath
// itself. Because the caller called us, subract two for our frame and its
// frame, which are included in stack->size().
return stack.size() - 2;
return stack.frames()->size() - 2;
}

View file

@ -35,7 +35,10 @@
#include "processor/stackwalker_x86.h"
#include "google/call_stack.h"
#include "google/stack_frame_cpu.h"
#include "processor/minidump.h"
#include "processor/postfix_evaluator-inl.h"
namespace google_airbag {
@ -54,63 +57,249 @@ StackwalkerX86::StackwalkerX86(const MDRawContextX86 *context,
}
bool StackwalkerX86::GetContextFrame(StackFrame *frame) {
if (!context_ || !memory_ || !frame)
return false;
StackFrame* StackwalkerX86::GetContextFrame() {
if (!context_ || !memory_)
return NULL;
// The frame and instruction pointers are stored directly in registers,
// so pull them straight out of the CPU context structure.
frame->frame_pointer = context_->ebp;
frame->instruction = context_->eip;
StackFrameX86 *frame = new StackFrameX86();
return true;
// The instruction pointer is stored directly in a register, so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL;
frame->instruction = frame->context.eip;
return frame;
}
bool StackwalkerX86::GetCallerFrame(StackFrame *frame,
const StackFrames *walked_frames) {
if (!memory_ || !frame || !walked_frames)
return false;
StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack)
return NULL;
// 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.
StackFrameX86 *last_frame = static_cast<StackFrameX86*>(
stack->frames()->back());
StackFrameInfo *last_frame_info = &stack_frame_info_.back();
// 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).
// 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
// after the callee returns. Specifically, the value is correct for the
// cdecl calling convention, but not other conventions. The cdecl
// convention requires a caller to pop its callee's arguments from the
// stack after the callee returns. This is usually accomplished by adding
// the known size of the arguments to %esp. Other calling conventions,
// including stdcall, thiscall, and fastcall, require the callee to pop any
// parameters stored on the stack before returning. This is usually
// accomplished by using the RET n instruction, which pops n bytes off
// the stack after popping the return address.
//
// Because each frame's %esp will point to a location on the stack after
// callee arguments have been PUSHed, when locating things in a stack frame
// relative to %esp, the size of the arguments to the callee need to be
// taken into account. This seems a little bit unclean, but it's better
// than the alternative, which would need to take these same things into
// account, but only for cdecl functions. With this implementation, we get
// to be agnostic about each function's calling convention. Furthermore,
// this is how Windows debugging tools work, so it means that the %esp
// values produced by this stackwalker directly correspond to the %esp
// values you'll see there.
//
// If the last frame has no callee (because it's the context frame), just
// set the callee parameter size to 0: the stack pointer can't point to
// callee arguments because there's no callee. This is correct as long
// as the context wasn't captured while arguments were being pushed for
// a function call. Note that there may be functions whose parameter sizes
// 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.
u_int32_t last_frame_pointer = walked_frames->back().frame_pointer;
int frames_already_walked = stack_frame_info_.size();
u_int32_t last_frame_callee_parameter_size = 0;
if (frames_already_walked >= 2) {
StackFrameInfo *last_frame_callee_info =
&stack_frame_info_[frames_already_walked - 2];
if (last_frame_callee_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) {
last_frame_callee_parameter_size =
last_frame_callee_info->parameter_size;
}
}
// 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;
// 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 an Airbag 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.
PostfixEvaluator<u_int32_t>::DictionaryType dictionary;
dictionary["$ebp"] = last_frame->context.ebp;
dictionary["$esp"] = last_frame->context.esp;
dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
// 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;
if (last_frame_info->valid == StackFrameInfo::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->valid & StackFrameInfo::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;
}
u_int32_t instruction;
if (!memory_->GetMemoryAtAddress(last_frame_pointer + 4, &instruction))
return false;
// 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.
string program_string;
if (last_frame_info->valid == StackFrameInfo::VALID_ALL) {
// FPO data available.
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.
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.
//
// %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. %ebp is reset
// to itself only to cause it to appear to have been set in
// dictionary_validity.
//
// 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.
//
// %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 + = "
"$ebp $ebp =";
}
} else {
// No FPO information is available for the last frame. 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)
program_string = "$eip $ebp 4 + ^ = "
"$esp $ebp 8 + = "
"$ebp $ebp ^ =";
}
frame->frame_pointer = frame_pointer;
frame->instruction = instruction;
// Now crank it out, making sure that the program string set the three
// required variables.
PostfixEvaluator<u_int32_t> evaluator =
PostfixEvaluator<u_int32_t>(&dictionary, memory_);
PostfixEvaluator<u_int32_t>::DictionaryValidityType dictionary_validity;
if (!evaluator.Evaluate(program_string, &dictionary_validity) ||
dictionary_validity.find("$eip") == dictionary_validity.end() ||
dictionary_validity.find("$esp") == dictionary_validity.end() ||
dictionary_validity.find("$ebp") == dictionary_validity.end()) {
return NULL;
}
return true;
// 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();
frame->context = last_frame->context;
frame->context.eip = dictionary["$eip"];
frame->context.esp = dictionary["$esp"];
frame->context.ebp = dictionary["$ebp"];
frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP |
StackFrameX86::CONTEXT_VALID_ESP |
StackFrameX86::CONTEXT_VALID_EBP;
// These are nonvolatile (callee-save) registers, and the program string
// may have filled them in.
if (dictionary_validity.find("$ebx") == dictionary_validity.end()) {
frame->context.ebx = dictionary["$ebx"];
frame->context_validity |= StackFrameX86::CONTEXT_VALID_EBX;
}
if (dictionary_validity.find("$esi") == dictionary_validity.end()) {
frame->context.esi = dictionary["$esi"];
frame->context_validity |= StackFrameX86::CONTEXT_VALID_ESI;
}
if (dictionary_validity.find("$edi") == dictionary_validity.end()) {
frame->context.edi = dictionary["$edi"];
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;
}

View file

@ -61,11 +61,11 @@ class StackwalkerX86 : public Stackwalker {
SymbolSupplier *supplier);
private:
// Implementation of Stackwalker, using x86 context (%ebp, %eip) and
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp]).
virtual bool GetContextFrame(StackFrame *frame);
virtual bool GetCallerFrame(StackFrame *frame,
const StackFrames *walked_frames);
// Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
// alternate conventions as guided by stack_frame_info_).
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.

View file

@ -1,23 +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
[ 0] instruction = 0x020a1515 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00221515
[ 1] instruction = 0x020a03e2 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x002203e2
[ 2] instruction = 0x023c8a27 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00548a27
[ 3] instruction = 0x023ccfd8 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x0054cfd8
[ 4] instruction = 0x0222fd11 "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003afd11
[ 5] instruction = 0x022311dc "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003b11dc
[ 6] instruction = 0x034eb0f0 "c:\lizard\trunk\mozilla\dist\bin\components\xpc3250.dll" + 0x0005b0f0
[ 7] instruction = 0x0049bda3 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0007bda3
[ 8] instruction = 0x0047b92e "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0005b92e
[ 9] instruction = 0x0046c944 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0004c944
[10] instruction = 0x0046d344 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x0004d344
[11] instruction = 0x00430ec2 "c:\lizard\trunk\mozilla\dist\bin\js3250.dll" + 0x00010ec2
[12] instruction = 0x02213b7e "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x00393b7e
[13] instruction = 0x02249cec "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003c9cec
[14] instruction = 0x0224a80f "c:\lizard\trunk\mozilla\dist\bin\components\gklayout.dll" + 0x003ca80f
[15] instruction = 0x002ebff7 "c:\lizard\trunk\mozilla\dist\bin\xpcom_core.dll" + 0x0007bff7
[16] instruction = 0x002ec8eb "c:\lizard\trunk\mozilla\dist\bin\xpcom_core.dll" + 0x0007c8eb
[17] instruction = 0x029193b4 "c:\lizard\trunk\mozilla\dist\bin\components\gkwidget.dll" + 0x000293b4
[18] instruction = 0x03174b18 "c:\lizard\trunk\mozilla\dist\bin\components\tkitcmps.dll" + 0x00004b18
[19] instruction = 0x10008e5f "c:\lizard\trunk\mozilla\dist\bin\xul.dll" + 0x00008e5f
[20] instruction = 0x00401035 "c:\lizard\trunk\mozilla\dist\bin\firefox.exe" + 0x00001035
[21] instruction = 0x004011bb "c:\lizard\trunk\mozilla\dist\bin\firefox.exe" + 0x000011bb
[22] instruction = 0x7c816d4e "C:\WINDOWS\system32\kernel32.dll" + 0x00016d4e

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -1,16 +1,16 @@
FILE 1 file1_1.cc
FILE 2 file1_2.cc
FILE 3 file1_3.cc
FUNC 1000 c Function1_1
FUNC 1000 c 0 Function1_1
1000 4 44 1
1004 4 45 1
1008 4 46 1
FUNC 1100 8 Function1_2
FUNC 1100 8 4 Function1_2
1100 4 65 2
1104 4 66 2
FUNC 1200 100 Function1_3
FUNC 1300 100 Function1_4
STACK WIN 4 1000 c 1 0 0 0 0 0 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 1100 8 1 0 0 0 0 0 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 1100 100 1 0 0 0 0 0 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 1300 100 1 0 0 0 0 0 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
FUNC 1200 100 8 Function1_3
FUNC 1300 100 c Function1_4
STACK WIN 4 1000 c 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 1100 8 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 1100 100 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 1300 100 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =

View file

@ -1,14 +1,14 @@
FILE 1 file2_1.cc
FILE 2 file2_2.cc
FILE 3 file2_3.cc
FUNC 2000 c Function2_1
FUNC 2000 c 4 Function2_1
1000 4 54 1
1004 4 55 1
1008 4 56 1
FUNC 2170 14 Function2_2
FUNC 2170 14 4 Function2_2
2170 6 10 2
2176 4 12 2
217a 6 13 2
2180 4 21 2
STACK WIN 4 2000 c 1 0 0 0 0 0 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 2170 14 1 0 0 0 0 0 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 2000 c 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =
STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =