Make stack_frame_info vector hold linked_ptrs instead of objects;

make Stackwalker::Walk create and return a CallStack instead of filling a
caller-supplied one (#54).  r=bryner

Interface change: Stackwalker::Walk and MinidumpProcessor::Process now return
a new CallStack*.

d2bad5d7c1


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@45 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
mmentovai 2006-10-23 19:24:58 +00:00
parent 7772046297
commit d119a921ea
15 changed files with 192 additions and 142 deletions

View file

@ -46,9 +46,9 @@ class MinidumpProcessor {
MinidumpProcessor(SymbolSupplier *supplier); MinidumpProcessor(SymbolSupplier *supplier);
~MinidumpProcessor(); ~MinidumpProcessor();
// Fills in the given CallStack by processing the minidump file. Returns // Returns a new CallStack produced by processing the minidump file. The
// true on success. // caller takes ownership of the CallStack. Returns NULL on failure.
bool Process(const string &minidump_file, CallStack *stack); CallStack* Process(const string &minidump_file);
private: private:
SymbolSupplier *supplier_; SymbolSupplier *supplier_;

View file

@ -44,43 +44,41 @@ MinidumpProcessor::MinidumpProcessor(SymbolSupplier *supplier)
MinidumpProcessor::~MinidumpProcessor() { MinidumpProcessor::~MinidumpProcessor() {
} }
bool MinidumpProcessor::Process(const string &minidump_file, CallStack* MinidumpProcessor::Process(const string &minidump_file) {
CallStack *stack) {
Minidump dump(minidump_file); Minidump dump(minidump_file);
if (!dump.Read()) { if (!dump.Read()) {
return false; return NULL;
} }
MinidumpException *exception = dump.GetException(); MinidumpException *exception = dump.GetException();
if (!exception) { if (!exception) {
return false; return NULL;
} }
MinidumpThreadList *threads = dump.GetThreadList(); MinidumpThreadList *threads = dump.GetThreadList();
if (!threads) { if (!threads) {
return false; return NULL;
} }
// TODO(bryner): get all the threads // TODO(bryner): get all the threads
MinidumpThread *thread = threads->GetThreadByID(exception->GetThreadID()); MinidumpThread *thread = threads->GetThreadByID(exception->GetThreadID());
if (!thread) { if (!thread) {
return false; return NULL;
} }
MinidumpMemoryRegion *thread_memory = thread->GetMemory(); MinidumpMemoryRegion *thread_memory = thread->GetMemory();
if (!thread_memory) { if (!thread_memory) {
return false; return NULL;
} }
auto_ptr<Stackwalker> walker( auto_ptr<Stackwalker> walker(
Stackwalker::StackwalkerForCPU(exception->GetContext(), thread_memory, Stackwalker::StackwalkerForCPU(exception->GetContext(), thread_memory,
dump.GetModuleList(), supplier_)); dump.GetModuleList(), supplier_));
if (!walker.get()) { if (!walker.get()) {
return false; return NULL;
} }
walker->Walk(stack); return walker->Walk();
return true;
} }
} // namespace google_airbag } // namespace google_airbag

View file

@ -30,6 +30,7 @@
// Unit test for MinidumpProcessor. Uses a pre-generated minidump and // Unit test for MinidumpProcessor. Uses a pre-generated minidump and
// corresponding symbol file, and checks the stack frames for correctness. // corresponding symbol file, and checks the stack frames for correctness.
#include <memory>
#include <string> #include <string>
#include "google/call_stack.h" #include "google/call_stack.h"
#include "google/minidump_processor.h" #include "google/minidump_processor.h"
@ -37,6 +38,7 @@
#include "google/symbol_supplier.h" #include "google/symbol_supplier.h"
#include "processor/minidump.h" #include "processor/minidump.h"
using std::auto_ptr;
using std::string; using std::string;
using google_airbag::CallStack; using google_airbag::CallStack;
using google_airbag::MinidumpProcessor; using google_airbag::MinidumpProcessor;
@ -74,40 +76,40 @@ static bool RunTests() {
TestSymbolSupplier supplier; TestSymbolSupplier supplier;
MinidumpProcessor processor(&supplier); MinidumpProcessor processor(&supplier);
CallStack stack;
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/minidump2.dmp"; "/src/processor/testdata/minidump2.dmp";
ASSERT_TRUE(processor.Process(minidump_file, &stack)); auto_ptr<CallStack> stack(processor.Process(minidump_file));
ASSERT_EQ(stack.frames()->size(), 4); ASSERT_TRUE(stack.get());
ASSERT_EQ(stack->frames()->size(), 4);
ASSERT_EQ(stack.frames()->at(0)->module_base, 0x400000); 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)->module_name, "c:\\test_app.exe");
ASSERT_EQ(stack.frames()->at(0)->function_name, "CrashFunction()"); 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_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack.frames()->at(0)->source_line, 65); ASSERT_EQ(stack->frames()->at(0)->source_line, 65);
ASSERT_EQ(stack.frames()->at(1)->module_base, 0x400000); 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)->module_name, "c:\\test_app.exe");
ASSERT_EQ(stack.frames()->at(1)->function_name, "main"); 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_file_name, "c:\\test_app.cc");
ASSERT_EQ(stack.frames()->at(1)->source_line, 70); ASSERT_EQ(stack->frames()->at(1)->source_line, 70);
// This comes from the CRT // This comes from the CRT
ASSERT_EQ(stack.frames()->at(2)->module_base, 0x400000); 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)->module_name, "c:\\test_app.exe");
ASSERT_EQ(stack.frames()->at(2)->function_name, "__tmainCRTStartup"); ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup");
ASSERT_EQ(stack.frames()->at(2)->source_file_name, ASSERT_EQ(stack->frames()->at(2)->source_file_name,
"f:\\rtm\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c"); "f:\\rtm\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c");
ASSERT_EQ(stack.frames()->at(2)->source_line, 318); ASSERT_EQ(stack->frames()->at(2)->source_line, 318);
// No debug info available for kernel32.dll // No debug info available for kernel32.dll
ASSERT_EQ(stack.frames()->at(3)->module_base, 0x7c800000); ASSERT_EQ(stack->frames()->at(3)->module_base, 0x7c800000);
ASSERT_EQ(stack.frames()->at(3)->module_name, ASSERT_EQ(stack->frames()->at(3)->module_name,
"C:\\WINDOWS\\system32\\kernel32.dll"); "C:\\WINDOWS\\system32\\kernel32.dll");
ASSERT_TRUE(stack.frames()->at(3)->function_name.empty()); ASSERT_TRUE(stack->frames()->at(3)->function_name.empty());
ASSERT_TRUE(stack.frames()->at(3)->source_file_name.empty()); ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
ASSERT_EQ(stack.frames()->at(3)->source_line, 0); ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
return true; return true;
} }

View file

@ -114,12 +114,11 @@ int main(int argc, char **argv) {
exit(1); exit(1);
} }
CallStack stack; auto_ptr<CallStack> stack(stackwalker->Walk());
stackwalker->Walk(&stack);
unsigned int index; unsigned int index;
for (index = 0; index < stack.frames()->size(); ++index) { for (index = 0; index < stack->frames()->size(); ++index) {
StackFrame *frame = stack.frames()->at(index); StackFrame *frame = stack->frames()->at(index);
printf("[%2d] instruction = 0x%08llx \"%s\" + 0x%08llx\n", printf("[%2d] instruction = 0x%08llx \"%s\" + 0x%08llx\n",
index, index,
frame->instruction, frame->instruction,

View file

@ -28,18 +28,20 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h> #include <stdio.h>
#include <map>
#include <string.h> #include <string.h>
#include <map>
#include <memory>
#include <vector> #include <vector>
#include <utility> #include <utility>
#include "processor/source_line_resolver.h" #include "processor/source_line_resolver.h"
#include "google/stack_frame.h" #include "google/stack_frame.h"
#include "processor/linked_ptr.h"
#include "processor/address_map-inl.h" #include "processor/address_map-inl.h"
#include "processor/contained_range_map-inl.h" #include "processor/contained_range_map-inl.h"
#include "processor/linked_ptr.h"
#include "processor/range_map-inl.h" #include "processor/range_map-inl.h"
#include "processor/stack_frame_info.h" #include "processor/stack_frame_info.h"
using std::auto_ptr;
using std::map; using std::map;
using std::vector; using std::vector;
using std::make_pair; using std::make_pair;
@ -104,9 +106,10 @@ class SourceLineResolver::Module {
// Looks up the given relative address, and fills the StackFrame struct // Looks up the given relative address, and fills the StackFrame struct
// with the result. Additional debugging information, if available, is // with the result. Additional debugging information, if available, is
// placed in frame_info. // returned. If no additional information is available, returns NULL.
void LookupAddress(MemAddr address, // A NULL return value is not an error. The caller takes ownership of
StackFrame *frame, StackFrameInfo *frame_info) const; // any returned StackFrameInfo object.
StackFrameInfo* LookupAddress(MemAddr address, StackFrame *frame) const;
private: private:
friend class SourceLineResolver; friend class SourceLineResolver;
@ -163,7 +166,8 @@ class SourceLineResolver::Module {
// StackInfoTypes. These are split by type because there may be overlaps // StackInfoTypes. These are split by type because there may be overlaps
// between maps of different types, but some information is only available // between maps of different types, but some information is only available
// as certain types. // as certain types.
ContainedRangeMap<MemAddr, StackFrameInfo> stack_info_[STACK_INFO_LAST]; ContainedRangeMap< MemAddr, linked_ptr<StackFrameInfo> >
stack_info_[STACK_INFO_LAST];
}; };
SourceLineResolver::SourceLineResolver() : modules_(new ModuleMap) { SourceLineResolver::SourceLineResolver() : modules_(new ModuleMap) {
@ -198,13 +202,14 @@ bool SourceLineResolver::HasModule(const string &module_name) const {
return modules_->find(module_name) != modules_->end(); return modules_->find(module_name) != modules_->end();
} }
void SourceLineResolver::FillSourceLineInfo(StackFrame *frame, StackFrameInfo* SourceLineResolver::FillSourceLineInfo(
StackFrameInfo *frame_info) const { StackFrame *frame) const {
ModuleMap::const_iterator it = modules_->find(frame->module_name); ModuleMap::const_iterator it = modules_->find(frame->module_name);
if (it != modules_->end()) { if (it != modules_->end()) {
it->second->LookupAddress(frame->instruction - frame->module_base, return it->second->LookupAddress(frame->instruction - frame->module_base,
frame, frame_info); frame);
} }
return NULL;
} }
bool SourceLineResolver::Module::LoadMap(const string &map_file) { bool SourceLineResolver::Module::LoadMap(const string &map_file) {
@ -259,23 +264,24 @@ bool SourceLineResolver::Module::LoadMap(const string &map_file) {
return true; return true;
} }
void SourceLineResolver::Module::LookupAddress( StackFrameInfo* SourceLineResolver::Module::LookupAddress(
MemAddr address, StackFrame *frame, StackFrameInfo *frame_info) const { MemAddr address, StackFrame *frame) const {
if (frame_info) { linked_ptr<StackFrameInfo> retrieved_info;
frame_info->valid = StackFrameInfo::VALID_NONE; // Check for debugging info first, before any possible early returns.
//
// 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,
&retrieved_info)) {
stack_info_[STACK_INFO_FPO].RetrieveRange(address, &retrieved_info);
}
// Check for debugging info first, before any possible early returns. auto_ptr<StackFrameInfo> frame_info;
// The caller will know that frame_info was filled in by checking its if (retrieved_info.get()) {
// valid field. frame_info.reset(new StackFrameInfo());
// frame_info->CopyFrom(*retrieved_info.get());
// 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)) {
stack_info_[STACK_INFO_FPO].RetrieveRange(address, frame_info);
}
} }
// First, look for a matching FUNC range. Use RetrieveNearestRange instead // First, look for a matching FUNC range. Use RetrieveNearestRange instead
@ -324,18 +330,20 @@ void SourceLineResolver::Module::LookupAddress(
frame->function_base = frame->module_base + public_address; frame->function_base = frame->module_base + public_address;
} else { } else {
// No FUNC or PUBLIC data available. // No FUNC or PUBLIC data available.
return; return frame_info.release();
} }
if (frame_info && if (!frame_info.get()) {
!(frame_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE)) {
// Even without a relevant STACK line, many functions contain information // Even without a relevant STACK line, many functions contain information
// about how much space their parameters consume on the stack. Prefer // about how much space their parameters consume on the stack. Prefer
// the STACK stuff (above), but if it's not present, take the // the STACK stuff (above), but if it's not present, take the
// information from the FUNC or PUBLIC line. // information from the FUNC or PUBLIC line.
frame_info.reset(new StackFrameInfo());
frame_info->parameter_size = parameter_size; frame_info->parameter_size = parameter_size;
frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE; frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE;
} }
return frame_info.release();
} }
// static // static
@ -518,15 +526,16 @@ bool SourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
// if ContainedRangeMap were modified to allow replacement of // if ContainedRangeMap were modified to allow replacement of
// already-stored values. // already-stored values.
stack_info_[type].StoreRange(rva, code_size, linked_ptr<StackFrameInfo> stack_frame_info(
StackFrameInfo(prolog_size, new StackFrameInfo(prolog_size,
epilog_size, epilog_size,
parameter_size, parameter_size,
saved_register_size, saved_register_size,
local_size, local_size,
max_stack_size, max_stack_size,
allocates_base_pointer, allocates_base_pointer,
program_string)); program_string));
stack_info_[type].StoreRange(rva, code_size, stack_frame_info);
return true; return true;
} }

View file

@ -66,8 +66,11 @@ class SourceLineResolver {
// Fills in the function_base, function_name, source_file_name, // Fills in the function_base, function_name, source_file_name,
// and source_line fields of the StackFrame. The instruction and // and source_line fields of the StackFrame. The instruction and
// module_name fields must already be filled in. Additional debugging // module_name fields must already be filled in. Additional debugging
// information, if available, is placed in frame_info. // information, if available, is returned. If the information is not
void FillSourceLineInfo(StackFrame *frame, StackFrameInfo *frame_info) const; // available, returns NULL. A NULL return value does not indicate an
// error. The caller takes ownership of any returned StackFrameInfo
// object.
StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const;
private: private:
template<class T> class MemAddrMap; template<class T> class MemAddrMap;

View file

@ -28,12 +28,16 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h> #include <stdio.h>
#include <memory>
#include <string> #include <string>
#include "processor/source_line_resolver.h" #include "processor/source_line_resolver.h"
#include "google/stack_frame.h" #include "google/stack_frame.h"
#include "processor/linked_ptr.h"
#include "processor/stack_frame_info.h" #include "processor/stack_frame_info.h"
using std::auto_ptr;
using std::string; using std::string;
using google_airbag::linked_ptr;
using google_airbag::SourceLineResolver; using google_airbag::SourceLineResolver;
using google_airbag::StackFrame; using google_airbag::StackFrame;
using google_airbag::StackFrameInfo; using google_airbag::StackFrameInfo;
@ -55,12 +59,10 @@ static bool VerifyEmpty(const StackFrame &frame) {
return true; return true;
} }
static void ClearSourceLineInfo(StackFrame *frame, static void ClearSourceLineInfo(StackFrame *frame) {
StackFrameInfo *frame_info) {
frame->function_name.clear(); frame->function_name.clear();
frame->source_file_name.clear(); frame->source_file_name.clear();
frame->source_line = 0; frame->source_line = 0;
frame_info->program_string.clear();
} }
static bool RunTests() { static bool RunTests() {
@ -74,62 +76,64 @@ static bool RunTests() {
ASSERT_TRUE(resolver.HasModule("module2")); ASSERT_TRUE(resolver.HasModule("module2"));
StackFrame frame; StackFrame frame;
StackFrameInfo frame_info;
frame.instruction = 0x1000; frame.instruction = 0x1000;
frame.module_name = "module1"; frame.module_name = "module1";
resolver.FillSourceLineInfo(&frame, &frame_info); auto_ptr<StackFrameInfo> frame_info(resolver.FillSourceLineInfo(&frame));
ASSERT_EQ(frame.function_name, "Function1_1"); ASSERT_EQ(frame.function_name, "Function1_1");
ASSERT_EQ(frame.source_file_name, "file1_1.cc"); ASSERT_EQ(frame.source_file_name, "file1_1.cc");
ASSERT_EQ(frame.source_line, 44); ASSERT_EQ(frame.source_line, 44);
ASSERT_FALSE(frame_info.allocates_base_pointer); ASSERT_TRUE(frame_info.get());
ASSERT_EQ(frame_info.program_string, ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_EQ(frame_info->program_string,
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ ="); "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
ClearSourceLineInfo(&frame, &frame_info); ClearSourceLineInfo(&frame);
frame.instruction = 0x800; frame.instruction = 0x800;
resolver.FillSourceLineInfo(&frame, &frame_info); frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_TRUE(VerifyEmpty(frame)); ASSERT_TRUE(VerifyEmpty(frame));
ASSERT_FALSE(frame_info.allocates_base_pointer); ASSERT_FALSE(frame_info.get());
ASSERT_TRUE(frame_info.program_string.empty());
frame.instruction = 0x1280; frame.instruction = 0x1280;
resolver.FillSourceLineInfo(&frame, &frame_info); frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_EQ(frame.function_name, "Function1_3"); ASSERT_EQ(frame.function_name, "Function1_3");
ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0); ASSERT_EQ(frame.source_line, 0);
ASSERT_FALSE(frame_info.allocates_base_pointer); ASSERT_TRUE(frame_info.get());
ASSERT_TRUE(frame_info.program_string.empty()); ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_TRUE(frame_info->program_string.empty());
frame.instruction = 0x1380; frame.instruction = 0x1380;
resolver.FillSourceLineInfo(&frame, &frame_info); frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_EQ(frame.function_name, "Function1_4"); ASSERT_EQ(frame.function_name, "Function1_4");
ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0); ASSERT_EQ(frame.source_line, 0);
ASSERT_FALSE(frame_info.allocates_base_pointer); ASSERT_TRUE(frame_info.get());
ASSERT_FALSE(frame_info.program_string.empty()); ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_FALSE(frame_info->program_string.empty());
frame.instruction = 0x2180; frame.instruction = 0x2180;
frame.module_name = "module2"; frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame, &frame_info); frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_EQ(frame.function_name, "Function2_2"); ASSERT_EQ(frame.function_name, "Function2_2");
ASSERT_EQ(frame.source_file_name, "file2_2.cc"); ASSERT_EQ(frame.source_file_name, "file2_2.cc");
ASSERT_EQ(frame.source_line, 21); ASSERT_EQ(frame.source_line, 21);
ASSERT_EQ(frame_info.prolog_size, 1); ASSERT_TRUE(frame_info.get());
ASSERT_EQ(frame_info->prolog_size, 1);
frame.instruction = 0x216f; frame.instruction = 0x216f;
frame.module_name = "module2"; frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame, &frame_info); resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_1"); ASSERT_EQ(frame.function_name, "Public2_1");
ClearSourceLineInfo(&frame, &frame_info); ClearSourceLineInfo(&frame);
frame.instruction = 0x219f; frame.instruction = 0x219f;
frame.module_name = "module2"; frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame, &frame_info); resolver.FillSourceLineInfo(&frame);
ASSERT_TRUE(frame.function_name.empty()); ASSERT_TRUE(frame.function_name.empty());
frame.instruction = 0x21a0; frame.instruction = 0x21a0;
frame.module_name = "module2"; frame.module_name = "module2";
resolver.FillSourceLineInfo(&frame, &frame_info); resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_2"); ASSERT_EQ(frame.function_name, "Public2_2");
ASSERT_FALSE(resolver.LoadModule("module3", ASSERT_FALSE(resolver.LoadModule("module3",

View file

@ -80,6 +80,19 @@ struct StackFrameInfo {
allocates_base_pointer(set_allocates_base_pointer), allocates_base_pointer(set_allocates_base_pointer),
program_string(set_program_string) {} program_string(set_program_string) {}
// CopyFrom makes "this" StackFrameInfo object identical to "that".
void CopyFrom(const StackFrameInfo &that) {
valid = that.valid;
prolog_size = that.prolog_size;
epilog_size = that.epilog_size;
parameter_size = that.parameter_size;
saved_register_size = that.saved_register_size;
local_size = that.local_size;
max_stack_size = that.max_stack_size;
allocates_base_pointer = that.allocates_base_pointer;
program_string = that.program_string;
}
// Clears the StackFrameInfo object so that users will see it as though // Clears the StackFrameInfo object so that users will see it as though
// it contains no information. // it contains no information.
void Clear() { valid = VALID_NONE; program_string.erase(); } void Clear() { valid = VALID_NONE; program_string.erase(); }

View file

@ -40,8 +40,10 @@
#include "google/call_stack.h" #include "google/call_stack.h"
#include "google/stack_frame.h" #include "google/stack_frame.h"
#include "google/symbol_supplier.h" #include "google/symbol_supplier.h"
#include "processor/linked_ptr.h"
#include "processor/minidump.h" #include "processor/minidump.h"
#include "processor/source_line_resolver.h" #include "processor/source_line_resolver.h"
#include "processor/stack_frame_info.h"
#include "processor/stackwalker_ppc.h" #include "processor/stackwalker_ppc.h"
#include "processor/stackwalker_x86.h" #include "processor/stackwalker_x86.h"
@ -52,15 +54,20 @@ using std::auto_ptr;
Stackwalker::Stackwalker(MemoryRegion *memory, MinidumpModuleList *modules, Stackwalker::Stackwalker(MemoryRegion *memory, MinidumpModuleList *modules,
SymbolSupplier *supplier) SymbolSupplier *supplier)
: memory_(memory), stack_frame_info_(), modules_(modules), : memory_(memory), modules_(modules), supplier_(supplier) {
supplier_(supplier) {
} }
void Stackwalker::Walk(CallStack *stack) { CallStack* Stackwalker::Walk() {
stack_frame_info_.clear();
SourceLineResolver resolver; SourceLineResolver resolver;
auto_ptr<CallStack> stack(new CallStack());
// stack_frame_info parallels the CallStack. The vector is passed to the
// GetCallerFrame function. It contains information that may be helpful
// for stackwalking.
vector< linked_ptr<StackFrameInfo> > stack_frame_info;
// Begin with the context frame, and keep getting callers until there are // Begin with the context frame, and keep getting callers until there are
// no more. // no more.
@ -72,7 +79,7 @@ void Stackwalker::Walk(CallStack *stack) {
// frame_pointer fields. The frame structure comes from either the // frame_pointer fields. The frame structure comes from either the
// context frame (above) or a caller frame (below). // context frame (above) or a caller frame (below).
StackFrameInfo frame_info; linked_ptr<StackFrameInfo> frame_info;
// Resolve the module information, if a module map was provided. // Resolve the module information, if a module map was provided.
if (modules_) { if (modules_) {
@ -87,7 +94,7 @@ void Stackwalker::Walk(CallStack *stack) {
resolver.LoadModule(frame->module_name, symbol_file); resolver.LoadModule(frame->module_name, symbol_file);
} }
} }
resolver.FillSourceLineInfo(frame.get(), &frame_info); frame_info.reset(resolver.FillSourceLineInfo(frame.get()));
} }
} }
@ -95,13 +102,15 @@ void Stackwalker::Walk(CallStack *stack) {
// over the frame, because the stack now owns it. // over the frame, because the stack now owns it.
stack->frames_.push_back(frame.release()); stack->frames_.push_back(frame.release());
// Copy the frame info. // Add the frame info to the parallel stack.
stack_frame_info_.push_back(frame_info); stack_frame_info.push_back(frame_info);
frame_info.Clear(); frame_info.reset(NULL);
// Get the next frame and take ownership. // Get the next frame and take ownership.
frame.reset(GetCallerFrame(stack)); frame.reset(GetCallerFrame(stack.get(), stack_frame_info));
} }
return stack.release();
} }

View file

@ -43,25 +43,28 @@
#include <vector> #include <vector>
#include "processor/stack_frame_info.h"
namespace google_airbag { namespace google_airbag {
class CallStack; class CallStack;
template<typename T> class linked_ptr;
class MemoryRegion; class MemoryRegion;
class MinidumpContext; class MinidumpContext;
class MinidumpModuleList; class MinidumpModuleList;
struct StackFrame; struct StackFrame;
struct StackFrameInfo;
class SymbolSupplier; class SymbolSupplier;
using std::vector;
class Stackwalker { class Stackwalker {
public: public:
virtual ~Stackwalker() {} virtual ~Stackwalker() {}
// Fills the given CallStack by calling GetContextFrame and GetCallerFrame, // Creates a new CallStack and populates it by calling GetContextFrame and
// and populating the returned frames with all available data. // GetCallerFrame. The frames are further processed to fill all available
void Walk(CallStack* stack); // data. The caller takes ownership of the CallStack returned by Walk.
CallStack* Walk();
// Returns a new concrete subclass suitable for the CPU that a stack was // Returns a new concrete subclass suitable for the CPU that a stack was
// generated on, according to the CPU type indicated by the context // generated on, according to the CPU type indicated by the context
@ -86,11 +89,6 @@ class Stackwalker {
// get information from the stack. // get information from the stack.
MemoryRegion *memory_; MemoryRegion *memory_;
// Additional debugging information for each stack frame. This vector
// parallels the CallStack. Subclasses may use this information to help
// walk the stack.
std::vector<StackFrameInfo> stack_frame_info_;
private: private:
// Obtains the context frame, the innermost called procedure in a stack // Obtains the context frame, the innermost called procedure in a stack
// trace. Returns NULL on failure. GetContextFrame allocates a new // trace. Returns NULL on failure. GetContextFrame allocates a new
@ -106,7 +104,9 @@ class Stackwalker {
// the end of the stack has been reached). GetCallerFrame allocates a new // the end of the stack has been reached). GetCallerFrame allocates a new
// StackFrame (or StackFrame subclass), ownership of which is taken by // StackFrame (or StackFrame subclass), ownership of which is taken by
// the caller. // the caller.
virtual StackFrame* GetCallerFrame(const CallStack *stack) = 0; virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) = 0;
// A list of modules, for populating each StackFrame's module information. // A list of modules, for populating each StackFrame's module information.
// This field is optional and may be NULL. // This field is optional and may be NULL.

View file

@ -73,7 +73,9 @@ StackFrame* StackwalkerPPC::GetContextFrame() {
} }
StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack *stack) { StackFrame* StackwalkerPPC::GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
if (!memory_ || !stack) if (!memory_ || !stack)
return NULL; return NULL;

View file

@ -65,7 +65,9 @@ class StackwalkerPPC : public Stackwalker {
// saved program counter in %srr0) and stack conventions (saved stack // saved program counter in %srr0) and stack conventions (saved stack
// pointer at 0(%r1), return address at 8(0(%r1)). // pointer at 0(%r1), return address at 8(0(%r1)).
virtual StackFrame* GetContextFrame(); virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack); virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
// Stores the CPU context corresponding to the innermost stack frame to // Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame. // be returned by GetContextFrame.

View file

@ -38,6 +38,7 @@
#include <cstdio> #include <cstdio>
#include <memory>
#include "google/airbag_types.h" #include "google/airbag_types.h"
#include "google/call_stack.h" #include "google/call_stack.h"
@ -46,6 +47,7 @@
#include "processor/memory_region.h" #include "processor/memory_region.h"
#include "processor/minidump_format.h" #include "processor/minidump_format.h"
using std::auto_ptr;
using google_airbag::CallStack; using google_airbag::CallStack;
using google_airbag::MemoryRegion; using google_airbag::MemoryRegion;
using google_airbag::StackFrame; using google_airbag::StackFrame;
@ -216,23 +218,22 @@ static unsigned int CountCallerFrames() {
StackwalkerPPC stackwalker = StackwalkerPPC(&context, &memory, NULL, NULL); StackwalkerPPC stackwalker = StackwalkerPPC(&context, &memory, NULL, NULL);
#endif // __i386__ || __ppc__ #endif // __i386__ || __ppc__
CallStack stack; auto_ptr<CallStack> stack(stackwalker.Walk());
stackwalker.Walk(&stack);
#ifdef PRINT_STACKS #ifdef PRINT_STACKS
printf("\n"); printf("\n");
for(unsigned int frame_index = 0; for(unsigned int frame_index = 0;
frame_index < stack.Count(); frame_index < stack->frames()->size();
++frame_index) { ++frame_index) {
StackFrame *frame = stack.FrameAt(frame_index); StackFrame *frame = stack->frames()->at(frame_index);
printf("frame %-3d instruction = 0x%08llx", printf("frame %-3d instruction = 0x%08llx",
frame_index, frame->instruction); frame_index, frame->instruction);
#if defined(__i386__) #if defined(__i386__)
StackFrameX86 *frame_x86 = reinterpret_cast<StackFrameX86*>(frame.get()); StackFrameX86 *frame_x86 = reinterpret_cast<StackFrameX86*>(frame);
printf(" esp = 0x%08x ebp = 0x%08x\n", printf(" esp = 0x%08x ebp = 0x%08x\n",
frame_x86->context.esp, frame_x86->context.ebp); frame_x86->context.esp, frame_x86->context.ebp);
#elif defined(__ppc__) #elif defined(__ppc__)
StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame.get()); StackFramePPC *frame_ppc = reinterpret_cast<StackFramePPC*>(frame);
printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]); printf(" gpr[1] = 0x%08x\n", frame_ppc->context.gpr[1]);
#endif // __i386__ || __ppc__ #endif // __i386__ || __ppc__
} }
@ -241,7 +242,7 @@ static unsigned int CountCallerFrames() {
// Subtract 1 because the caller wants the number of frames beneath // Subtract 1 because the caller wants the number of frames beneath
// itself. Because the caller called us, subract two for our frame and its // itself. Because the caller called us, subract two for our frame and its
// frame, which are included in stack->size(). // frame, which are included in stack->size().
return stack.frames()->size() - 2; return stack->frames()->size() - 2;
} }

View file

@ -37,8 +37,10 @@
#include "processor/stackwalker_x86.h" #include "processor/stackwalker_x86.h"
#include "google/call_stack.h" #include "google/call_stack.h"
#include "google/stack_frame_cpu.h" #include "google/stack_frame_cpu.h"
#include "processor/linked_ptr.h"
#include "processor/minidump.h" #include "processor/minidump.h"
#include "processor/postfix_evaluator-inl.h" #include "processor/postfix_evaluator-inl.h"
#include "processor/stack_frame_info.h"
namespace google_airbag { namespace google_airbag {
@ -73,13 +75,15 @@ StackFrame* StackwalkerX86::GetContextFrame() {
} }
StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) { StackFrame* StackwalkerX86::GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
if (!memory_ || !stack) if (!memory_ || !stack)
return NULL; return NULL;
StackFrameX86 *last_frame = static_cast<StackFrameX86*>( StackFrameX86 *last_frame = static_cast<StackFrameX86*>(
stack->frames()->back()); stack->frames()->back());
StackFrameInfo *last_frame_info = &stack_frame_info_.back(); StackFrameInfo *last_frame_info = stack_frame_info.back().get();
// This stackwalker sets each frame's %esp to its value immediately prior // 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 // to the CALL into the callee. This means that %esp points to the last
@ -113,12 +117,13 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
// are unknown, 0 is also used in that case. When that happens, it should // 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. // be possible to walk to the next frame without reference to %esp.
int frames_already_walked = stack_frame_info_.size(); int frames_already_walked = stack_frame_info.size();
u_int32_t last_frame_callee_parameter_size = 0; u_int32_t last_frame_callee_parameter_size = 0;
if (frames_already_walked >= 2) { if (frames_already_walked >= 2) {
StackFrameInfo *last_frame_callee_info = StackFrameInfo *last_frame_callee_info =
&stack_frame_info_[frames_already_walked - 2]; stack_frame_info[frames_already_walked - 2].get();
if (last_frame_callee_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) { if (last_frame_callee_info &&
last_frame_callee_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) {
last_frame_callee_parameter_size = last_frame_callee_parameter_size =
last_frame_callee_info->parameter_size; last_frame_callee_info->parameter_size;
} }
@ -135,7 +140,7 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
dictionary["$esp"] = last_frame->context.esp; dictionary["$esp"] = last_frame->context.esp;
dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size; dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size;
if (last_frame_info->valid == StackFrameInfo::VALID_ALL) { if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) {
// FPO debugging data is available. Initialize constants. // FPO debugging data is available. Initialize constants.
dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size; dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size;
dictionary[".cbLocals"] = last_frame_info->local_size; dictionary[".cbLocals"] = last_frame_info->local_size;
@ -144,7 +149,8 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
last_frame_info->local_size + last_frame_info->local_size +
last_frame_info->saved_register_size; last_frame_info->saved_register_size;
} }
if (last_frame_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) { if (last_frame_info &&
last_frame_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) {
// This is treated separately because it can either come from FPO data or // This is treated separately because it can either come from FPO data or
// from other debugging data. // from other debugging data.
dictionary[".cbParams"] = last_frame_info->parameter_size; dictionary[".cbParams"] = last_frame_info->parameter_size;
@ -156,7 +162,7 @@ StackFrame* StackwalkerX86::GetCallerFrame(const CallStack *stack) {
// the return address and the values of other registers in the calling // the return address and the values of other registers in the calling
// function. // function.
string program_string; string program_string;
if (last_frame_info->valid == StackFrameInfo::VALID_ALL) { if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) {
// FPO data available. // FPO data available.
if (!last_frame_info->program_string.empty()) { if (!last_frame_info->program_string.empty()) {
// The FPO data has its own program string, which will tell us how to // The FPO data has its own program string, which will tell us how to

View file

@ -65,7 +65,9 @@ class StackwalkerX86 : public Stackwalker {
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or // stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
// alternate conventions as guided by stack_frame_info_). // alternate conventions as guided by stack_frame_info_).
virtual StackFrame* GetContextFrame(); virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack *stack); virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
// Stores the CPU context corresponding to the innermost stack frame to // Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame. // be returned by GetContextFrame.