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

@ -51,9 +51,12 @@ lib_LTLIBRARIES = src/libairbag.la
src_libairbag_la_SOURCES = \
src/google/airbag_types.h \
src/google/call_stack.h \
src/google/minidump_processor.h \
src/google/stack_frame.h \
src/google/stack_frame_cpu.h \
src/google/symbol_supplier.h \
src/processor/call_stack.cc \
src/processor/contained_range_map.h \
src/processor/contained_range_map-inl.h \
src/processor/linked_ptr.h \
@ -109,6 +112,7 @@ src_processor_contained_range_map_unittest_SOURCES = \
src_processor_minidump_processor_unittest_SOURCES = \
src/processor/minidump_processor_unittest.cc
src_processor_minidump_processor_unittest_LDADD = \
src/processor/call_stack.lo \
src/processor/minidump_processor.lo \
src/processor/minidump.lo \
src/processor/stackwalker.lo \
@ -130,6 +134,7 @@ src_processor_source_line_resolver_unittest_LDADD = \
src_processor_stackwalker_selftest_SOURCES = \
src/processor/stackwalker_selftest.cc
src_processor_stackwalker_selftest_LDADD = \
src/processor/call_stack.lo \
src/processor/minidump.lo \
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo \
@ -148,6 +153,7 @@ src_processor_minidump_dump_LDADD = \
src_processor_minidump_stackwalk_SOURCES = \
src/processor/minidump_stackwalk.cc
src_processor_minidump_stackwalk_LDADD = \
src/processor/call_stack.lo \
src/processor/minidump.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_ppc.lo \

View file

@ -108,8 +108,8 @@ libLTLIBRARIES_INSTALL = $(INSTALL)
LTLIBRARIES = $(lib_LTLIBRARIES)
src_libairbag_la_LIBADD =
am__dirstamp = $(am__leading_dot)dirstamp
am_src_libairbag_la_OBJECTS = src/processor/minidump.lo \
src/processor/minidump_processor.lo \
am_src_libairbag_la_OBJECTS = src/processor/call_stack.lo \
src/processor/minidump.lo src/processor/minidump_processor.lo \
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_x86.lo
@ -133,6 +133,7 @@ am_src_processor_minidump_processor_unittest_OBJECTS = \
src_processor_minidump_processor_unittest_OBJECTS = \
$(am_src_processor_minidump_processor_unittest_OBJECTS)
src_processor_minidump_processor_unittest_DEPENDENCIES = \
src/processor/call_stack.lo \
src/processor/minidump_processor.lo src/processor/minidump.lo \
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_x86.lo \
@ -142,8 +143,8 @@ am_src_processor_minidump_stackwalk_OBJECTS = \
src_processor_minidump_stackwalk_OBJECTS = \
$(am_src_processor_minidump_stackwalk_OBJECTS)
src_processor_minidump_stackwalk_DEPENDENCIES = \
src/processor/minidump.lo src/processor/stackwalker.lo \
src/processor/stackwalker_ppc.lo \
src/processor/call_stack.lo src/processor/minidump.lo \
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_x86.lo \
src/processor/source_line_resolver.lo
am_src_processor_postfix_evaluator_unittest_OBJECTS = \
@ -167,7 +168,7 @@ am_src_processor_stackwalker_selftest_OBJECTS = \
src_processor_stackwalker_selftest_OBJECTS = \
$(am_src_processor_stackwalker_selftest_OBJECTS)
src_processor_stackwalker_selftest_DEPENDENCIES = \
src/processor/minidump.lo \
src/processor/call_stack.lo src/processor/minidump.lo \
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_x86.lo
@ -342,9 +343,12 @@ dist_doc_DATA = \
lib_LTLIBRARIES = src/libairbag.la
src_libairbag_la_SOURCES = \
src/google/airbag_types.h \
src/google/call_stack.h \
src/google/minidump_processor.h \
src/google/stack_frame.h \
src/google/stack_frame_cpu.h \
src/google/symbol_supplier.h \
src/processor/call_stack.cc \
src/processor/contained_range_map.h \
src/processor/contained_range_map-inl.h \
src/processor/linked_ptr.h \
@ -380,6 +384,7 @@ src_processor_minidump_processor_unittest_SOURCES = \
src/processor/minidump_processor_unittest.cc
src_processor_minidump_processor_unittest_LDADD = \
src/processor/call_stack.lo \
src/processor/minidump_processor.lo \
src/processor/minidump.lo \
src/processor/stackwalker.lo \
@ -403,6 +408,7 @@ src_processor_stackwalker_selftest_SOURCES = \
src/processor/stackwalker_selftest.cc
src_processor_stackwalker_selftest_LDADD = \
src/processor/call_stack.lo \
src/processor/minidump.lo \
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo \
@ -420,6 +426,7 @@ src_processor_minidump_stackwalk_SOURCES = \
src/processor/minidump_stackwalk.cc
src_processor_minidump_stackwalk_LDADD = \
src/processor/call_stack.lo \
src/processor/minidump.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_ppc.lo \
@ -524,6 +531,8 @@ src/processor/$(am__dirstamp):
src/processor/$(DEPDIR)/$(am__dirstamp):
@$(mkdir_p) src/processor/$(DEPDIR)
@: > src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/call_stack.lo: src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/minidump.lo: src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/minidump_processor.lo: src/processor/$(am__dirstamp) \
@ -633,6 +642,8 @@ src/processor/stackwalker_selftest$(EXEEXT): $(src_processor_stackwalker_selftes
mostlyclean-compile:
-rm -f *.$(OBJEXT)
-rm -f src/processor/call_stack.$(OBJEXT)
-rm -f src/processor/call_stack.lo
-rm -f src/processor/contained_range_map_unittest.$(OBJEXT)
-rm -f src/processor/minidump.$(OBJEXT)
-rm -f src/processor/minidump.lo
@ -657,6 +668,7 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/call_stack.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/contained_range_map_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_dump.Po@am__quote@

View file

@ -30,8 +30,15 @@
#include <stdio.h>
#include <atlbase.h>
#include <dia2.h>
#include <DbgHelp.h>
#include "common/windows/pdb_source_line_writer.h"
// This constant may be missing from DbgHelp.h. See the documentation for
// IDiaSymbol::get_undecoratedNameEx.
#ifndef UNDNAME_NO_ECSU
#define UNDNAME_NO_ECSU 0x8000 // Suppresses enum/class/struct/union.
#endif // UNDNAME_NO_ECSU
namespace google_airbag {
PDBSourceLineWriter::PDBSourceLineWriter() : output_(NULL) {
@ -119,14 +126,10 @@ bool PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *lines) {
bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) {
// The function format is:
// FUNC <address> <function>
CComBSTR name;
if (FAILED(function->get_name(&name))) {
fprintf(stderr, "failed to get function name\n");
return false;
}
if (name.Length() == 0) {
fprintf(stderr, "empty function name\n");
// FUNC <address> <length> <param_stack_size> <function>
DWORD rva;
if (FAILED(function->get_relativeVirtualAddress(&rva))) {
fprintf(stderr, "couldn't get rva\n");
return false;
}
@ -136,18 +139,26 @@ bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) {
return false;
}
DWORD rva;
if (FAILED(function->get_relativeVirtualAddress(&rva))) {
fprintf(stderr, "couldn't get rva\n");
CComBSTR name;
int stack_param_size;
if (!GetSymbolFunctionName(function, &name, &stack_param_size)) {
return false;
}
// If the decorated name didn't give the parameter size, try to
// calculate it.
if (stack_param_size < 0) {
stack_param_size = GetFunctionStackParamSize(function);
}
fprintf(output_, "FUNC %x %llx %x %ws\n",
rva, length, stack_param_size, name);
CComPtr<IDiaEnumLineNumbers> lines;
if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) {
return false;
}
fwprintf(output_, L"FUNC %x %llx %s\n", rva, length, name);
if (!PrintLines(lines)) {
return false;
}
@ -211,7 +222,7 @@ bool PDBSourceLineWriter::PrintFunctions() {
DWORD rva_last = 0;
if (FAILED(symbol->get_relativeVirtualAddress(&rva_last))) {
fprintf(stderr, "failed to get symbol rva\n");
return false;
return false;
}
ULONG count;
@ -221,10 +232,19 @@ bool PDBSourceLineWriter::PrintFunctions() {
fprintf(stderr, "failed to get symbol tag\n");
return false;
}
// For a given function, DIA seems to give either a symbol with
// SymTagFunction or SymTagPublicSymbol, but not both. This means
// that PDBSourceLineWriter will output either a FUNC or PUBLIC line,
// but not both.
if (tag == SymTagFunction) {
if (!PrintFunction(symbol)) {
return false;
}
} else if (tag == SymTagPublicSymbol) {
if (!PrintCodePublicSymbol(symbol)) {
return false;
}
}
symbol.Release();
} while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1);
@ -247,7 +267,7 @@ bool PDBSourceLineWriter::PrintFrameData() {
ULONG count;
while (!frame_data_enum &&
SUCCEEDED(tables->Next(1, &table, &count)) &&
count == 1) {
count == 1) {
table->QueryInterface(_uuidof(IDiaEnumFrameData),
reinterpret_cast<void**>(&frame_data_enum));
table.Release();
@ -277,6 +297,9 @@ bool PDBSourceLineWriter::PrintFrameData() {
// epliog_size is always 0.
DWORD epilog_size = 0;
// parameter_size is the size of parameters passed on the stack. If any
// parameters are not passed on the stack (such as in registers), their
// sizes will not be included in parameter_size.
DWORD parameter_size;
if (FAILED(frame_data->get_lengthParams(&parameter_size)))
return false;
@ -289,18 +312,39 @@ bool PDBSourceLineWriter::PrintFrameData() {
if (FAILED(frame_data->get_lengthLocals(&local_size)))
return false;
DWORD max_stack_size;
// get_maxStack can return S_FALSE, just use 0 in that case.
DWORD max_stack_size = 0;
if (FAILED(frame_data->get_maxStack(&max_stack_size)))
return false;
BSTR program_string;
if (FAILED(frame_data->get_program(&program_string)))
// get_programString can return S_FALSE, indicating that there is no
// program string. In that case, check whether %ebp is used.
HRESULT program_string_result;
CComBSTR program_string;
if (FAILED(program_string_result = frame_data->get_program(
&program_string))) {
return false;
}
fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %ws\n",
// get_allocatesBasePointer can return S_FALSE, treat that as though
// %ebp is not used.
BOOL allocates_base_pointer = FALSE;
if (program_string_result != S_OK) {
if (FAILED(frame_data->get_allocatesBasePointer(
&allocates_base_pointer))) {
return false;
}
}
fprintf(output_, "STACK WIN %x %x %x %x %x %x %x %x %x %d ",
type, rva, code_size, prolog_size, epilog_size,
parameter_size, saved_register_size, local_size, max_stack_size,
program_string);
program_string_result == S_OK);
if (program_string_result == S_OK) {
fprintf(output_, "%ws\n", program_string);
} else {
fprintf(output_, "%d\n", allocates_base_pointer);
}
frame_data.Release();
}
@ -308,6 +352,259 @@ bool PDBSourceLineWriter::PrintFrameData() {
return true;
}
bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
BOOL is_code;
if (FAILED(symbol->get_code(&is_code))) {
return false;
}
if (!is_code) {
return true;
}
DWORD rva;
if (FAILED(symbol->get_relativeVirtualAddress(&rva))) {
return false;
}
CComBSTR name;
int stack_param_size;
if (!GetSymbolFunctionName(symbol, &name, &stack_param_size)) {
return false;
}
fprintf(output_, "PUBLIC %x %x %ws\n", rva,
stack_param_size > 0 ? stack_param_size : 0, name);
return true;
}
// wcstol_positive_strict is sort of like wcstol, but much stricter. string
// should be a buffer pointing to a null-terminated string containing only
// decimal digits. If the entire string can be converted to an integer
// without overflowing, and there are no non-digit characters before the
// result is set to the value and this function returns true. Otherwise,
// this function returns false. This is an alternative to the strtol, atoi,
// and scanf families, which are not as strict about input and in some cases
// don't provide a good way for the caller to determine if a conversion was
// successful.
static bool wcstol_positive_strict(wchar_t *string, int *result) {
int value = 0;
for (wchar_t *c = string; *c != '\0'; ++c) {
int last_value = value;
value *= 10;
// Detect overflow.
if (value / 10 != last_value || value < 0) {
return false;
}
if (*c < '0' || *c > '9') {
return false;
}
unsigned int c_value = *c - '0';
last_value = value;
value += c_value;
// Detect overflow.
if (value < last_value) {
return false;
}
// Forbid leading zeroes unless the string is just "0".
if (value == 0 && *(c+1) != '\0') {
return false;
}
}
*result = value;
return true;
}
// static
bool PDBSourceLineWriter::GetSymbolFunctionName(IDiaSymbol *function,
BSTR *name,
int *stack_param_size) {
*stack_param_size = -1;
const DWORD undecorate_options = UNDNAME_NO_MS_KEYWORDS |
UNDNAME_NO_FUNCTION_RETURNS |
UNDNAME_NO_ALLOCATION_MODEL |
UNDNAME_NO_ALLOCATION_LANGUAGE |
UNDNAME_NO_THISTYPE |
UNDNAME_NO_ACCESS_SPECIFIERS |
UNDNAME_NO_THROW_SIGNATURES |
UNDNAME_NO_MEMBER_TYPE |
UNDNAME_NO_RETURN_UDT_MODEL |
UNDNAME_NO_ECSU;
// Use get_undecoratedNameEx to get readable C++ names with arguments.
if (function->get_undecoratedNameEx(undecorate_options, name) != S_OK) {
if (function->get_name(name) != S_OK) {
fprintf(stderr, "failed to get function name\n");
return false;
}
// If a name comes from get_name because no undecorated form existed,
// it's already formatted properly to be used as output. Don't do any
// additional processing.
} else {
// C++ uses a bogus "void" argument for functions and methods that don't
// take any parameters. Take it out of the undecorated name because it's
// ugly and unnecessary.
const wchar_t *replace_string = L"(void)";
const size_t replace_length = wcslen(replace_string);
const wchar_t *replacement_string = L"()";
size_t length = wcslen(*name);
if (length >= replace_length) {
wchar_t *name_end = *name + length - replace_length;
if (wcscmp(name_end, replace_string) == 0) {
wcscpy_s(name_end, replace_length, replacement_string);
length = wcslen(*name);
}
}
// Undecorate names used for stdcall and fastcall. These names prefix
// the identifier with '_' (stdcall) or '@' (fastcall) and suffix it
// with '@' followed by the number of bytes of parameters, in decimal.
// If such a name is found, take note of the size and undecorate it.
// Only do this for names that aren't C++, which is determined based on
// whether the undecorated name contains any ':' or '(' characters.
if (!wcschr(*name, ':') && !wcschr(*name, '(') &&
(*name[0] == '_' || *name[0] == '@')) {
wchar_t *last_at = wcsrchr(*name + 1, '@');
if (last_at && wcstol_positive_strict(last_at + 1, stack_param_size)) {
// If this function adheres to the fastcall convention, it accepts up
// to the first 8 bytes of parameters in registers (%ecx and %edx).
// We're only interested in the stack space used for parameters, so
// so subtract 8 and don't let the size go below 0.
if (*name[0] == '@') {
if (*stack_param_size > 8) {
*stack_param_size -= 8;
} else {
*stack_param_size = 0;
}
}
// Undecorate the name by moving it one character to the left in its
// buffer, and terminating it where the last '@' had been.
wcsncpy_s(*name, length, *name + 1, last_at - *name - 1);
} else if (*name[0] == '_') {
// This symbol's name is encoded according to the cdecl rules. The
// name doesn't end in a '@' character followed by a decimal positive
// nteger, so it's not a stdcall name. Strip off the leading
// underscore.
wcsncpy_s(*name, length, *name + 1, length - 1);
}
}
}
return true;
}
// static
int PDBSourceLineWriter::GetFunctionStackParamSize(IDiaSymbol *function) {
// This implementation is highly x86-specific.
// Gather the symbols corresponding to data.
CComPtr<IDiaEnumSymbols> data_children;
if (FAILED(function->findChildren(SymTagData, NULL, nsNone,
&data_children))) {
return 0;
}
// lowest_base is the lowest %ebp-relative byte offset used for a parameter.
// highest_end is one greater than the highest offset (i.e. base + length).
// Stack parameters are assumed to be contiguous, because in reality, they
// are.
int lowest_base = INT_MAX;
int highest_end = INT_MIN;
CComPtr<IDiaSymbol> child;
DWORD count;
while (SUCCEEDED(data_children->Next(1, &child, &count)) && count == 1) {
// If any operation fails at this point, just proceed to the next child.
// Use the next_child label instead of continue because child needs to
// be released before it's reused. Declare constructable/destructable
// types early to avoid gotos that cross initializations.
CComPtr<IDiaSymbol> child_type;
// DataIsObjectPtr is only used for |this|. Because |this| can be passed
// as a stack parameter, look for it in addition to traditional
// parameters.
DWORD child_kind;
if (FAILED(child->get_dataKind(&child_kind)) ||
(child_kind != DataIsParam && child_kind != DataIsObjectPtr)) {
goto next_child;
}
// Only concentrate on register-relative parameters. Parameters may also
// be enregistered (passed directly in a register), but those don't
// consume any stack space, so they're not of interest.
DWORD child_location_type;
if (FAILED(child->get_locationType(&child_location_type)) ||
child_location_type != LocIsRegRel) {
goto next_child;
}
// Of register-relative parameters, the only ones that make any sense are
// %ebp- or %esp-relative. Note that MSVC's debugging information always
// gives parameters as %ebp-relative even when a function doesn't use a
// traditional frame pointer and stack parameters are accessed relative to
// %esp, so just look for %ebp-relative parameters. If you wanted to
// access parameters, you'd probably want to treat these %ebp-relative
// offsets as if they were relative to %esp before a function's prolog
// executed.
DWORD child_register;
if (FAILED(child->get_registerId(&child_register)) ||
child_register != CV_REG_EBP) {
goto next_child;
}
LONG child_register_offset;
if (FAILED(child->get_offset(&child_register_offset))) {
goto next_child;
}
if (FAILED(child->get_type(&child_type))) {
goto next_child;
}
ULONGLONG child_length;
if (FAILED(child_type->get_length(&child_length))) {
goto next_child;
}
int child_end = child_register_offset + static_cast<ULONG>(child_length);
if (child_register_offset < lowest_base) {
lowest_base = child_register_offset;
}
if (child_end > highest_end) {
highest_end = child_end;
}
next_child:
child.Release();
}
int param_size = 0;
// Make sure lowest_base isn't less than 4, because [%esp+4] is the lowest
// possible address to find a stack parameter before executing a function's
// prolog (see above). Some optimizations cause parameter offsets to be
// lower than 4, but we're not concerned with those because we're only
// looking for parameters contained in addresses higher than where the
// return address is stored.
if (lowest_base < 4) {
lowest_base = 4;
}
if (highest_end > lowest_base) {
// All stack parameters are pushed as at least 4-byte quantities. If the
// last type was narrower than 4 bytes, promote it. This assumes that all
// parameters' offsets are 4-byte-aligned, which is always the case. Only
// worry about the last type, because we're not summing the type sizes,
// just looking at the lowest and highest offsets.
int remainder = highest_end % 4;
if (remainder) {
highest_end += 4 - remainder;
}
param_size = highest_end - lowest_base;
}
return param_size;
}
bool PDBSourceLineWriter::WriteMap(FILE *map_file) {
bool ret = false;
output_ = map_file;

View file

@ -96,6 +96,24 @@ class PDBSourceLineWriter {
// backtraces in the absence of frame pointers. Returns true on success.
bool PrintFrameData();
// Outputs a single public symbol address and name, if the symbol corresponds
// to a code address. Returns true on success. If symbol is does not
// correspond to code, returns true without outputting anything.
bool PrintCodePublicSymbol(IDiaSymbol *symbol);
// Returns the function name for a symbol. If possible, the name is
// undecorated. If the symbol's decorated form indicates the size of
// parameters on the stack, this information is returned in stack_param_size.
// Returns true on success. If the symbol doesn't encode parameter size
// information, stack_param_size is set to -1.
static bool GetSymbolFunctionName(IDiaSymbol *function, BSTR *name,
int *stack_param_size);
// Returns the number of bytes of stack space used for a function's
// parameters. function must have the tag SymTagFunction. In the event of
// a failure, returns 0, which is also a valid number of bytes.
static int GetFunctionStackParamSize(IDiaSymbol *function);
// The session for the currently-open pdb file.
CComPtr<IDiaSession> session_;

73
src/google/call_stack.h Normal file
View file

@ -0,0 +1,73 @@
// 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.h: A call stack comprised of stack frames.
//
// This class manages a vector of stack frames. It is used instead of
// exposing the vector directly to allow the CallStack to own StackFrame
// pointers without having to publicly export the linked_ptr class. A
// CallStack must be composed of pointers instead of objects to allow for
// CPU-specific StackFrame subclasses.
//
// By convention, the stack frame at index 0 is the innermost callee frame,
// and the frame at the highest index in a call stack is the outermost
// caller. CallStack only allows stacks to be built by pushing frames,
// beginning with the innermost callee frame.
//
// Author: Mark Mentovai
#ifndef GOOGLE_CALL_STACK_H__
#define GOOGLE_CALL_STACK_H__
#include <vector>
namespace google_airbag {
using std::vector;
struct StackFrame;
template<typename T> class linked_ptr;
class CallStack {
public:
~CallStack();
const vector<StackFrame*>* frames() const { return &frames_; }
private:
// Stackwalker is responsible for building the frames_ vector.
friend class Stackwalker;
// Storage for pushed frames.
vector<StackFrame*> frames_;
};
} // namespace google_airbag
#endif // GOOGLE_CALL_STACK_H__

View file

@ -31,12 +31,12 @@
#define GOOGLE_MINIDUMP_PROCESSOR_H__
#include <string>
#include "google/stack_frame.h"
namespace google_airbag {
using std::string;
class CallStack;
class SymbolSupplier;
class MinidumpProcessor {
@ -46,9 +46,9 @@ class MinidumpProcessor {
MinidumpProcessor(SymbolSupplier *supplier);
~MinidumpProcessor();
// Fills in the given StackFrames vector by processing the minidump file.
// Returns true on success.
bool Process(const string &minidump_file, StackFrames *stack_frames);
// Fills in the given CallStack by processing the minidump file. Returns
// true on success.
bool Process(const string &minidump_file, CallStack *stack);
private:
SymbolSupplier *supplier_;

View file

@ -31,58 +31,51 @@
#define GOOGLE_STACK_FRAME_H__
#include <string>
#include <vector>
#include "google/airbag_types.h"
namespace google_airbag {
using std::string;
using std::vector;
struct StackFrame {
// Initialize sensible defaults, or this will be instantiated with
// primitive members in an undetermined state.
StackFrame()
: instruction()
, frame_pointer()
, module_base()
, module_name()
, function_base()
, function_name()
, source_file_name()
, source_line() {}
: instruction(),
module_base(),
module_name(),
function_base(),
function_name(),
source_file_name(),
source_line() {}
virtual ~StackFrame() {}
// The program counter location relative to the module base
// The program counter location as an absolute virtual address. For the
// innermost called frame in a stack, this will be an exact program counter
// or instruction pointer value. For all other frames, this will be within
// the instruction that caused execution to branch to a called function,
// but may not necessarily point to the exact beginning of that instruction.
u_int64_t instruction;
// The frame pointer to this stack frame
u_int64_t frame_pointer;
// The base address of the module
// The base address of the module.
u_int64_t module_base;
// The module in which the pc resides
// The module in which the instruction resides.
string module_name;
// The start address of the function, may be omitted if debug symbols
// are not available.
u_int64_t function_base;
// The function name, may be omitted if debug symbols are not available
// The function name, may be omitted if debug symbols are not available.
string function_name;
// The source file name, may be omitted if debug symbols are not available
// The source file name, may be omitted if debug symbols are not available.
string source_file_name;
// The (1-based) source line number,
// may be omitted if debug symbols are not available
// The (1-based) source line number, may be omitted if debug symbols are
// not available.
int source_line;
// TODO(bryner): saved registers
};
typedef vector<StackFrame> StackFrames;
} // namespace google_airbag
#endif // GOOGLE_STACK_FRAME_H__

View file

@ -0,0 +1,103 @@
// 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.
// stack_frame_cpu.h: CPU-specific StackFrame extensions.
//
// These types extend the StackFrame structure to carry CPU-specific register
// state. They are defined in this header instead of stack_frame.h to
// avoid the need to include minidump_format.h when only the generic
// StackFrame type is needed.
//
// Author: Mark Mentovai
#ifndef GOOGLE_STACK_FRAME_CPU_H__
#define GOOGLE_STACK_FRAME_CPU_H__
#include "google/stack_frame.h"
#include "processor/minidump_format.h"
namespace google_airbag {
struct StackFrameX86 : public StackFrame {
// ContextValidity has one entry for each relevant hardware pointer register
// (%eip and %esp) and one entry for each nonvolatile (callee-save) register.
enum ContextValidity {
CONTEXT_VALID_NONE = 0,
CONTEXT_VALID_EIP = 1 << 0,
CONTEXT_VALID_ESP = 1 << 1,
CONTEXT_VALID_EBP = 1 << 2,
CONTEXT_VALID_EBX = 1 << 3,
CONTEXT_VALID_ESI = 1 << 4,
CONTEXT_VALID_EDI = 1 << 5,
CONTEXT_VALID_ALL = -1
};
StackFrameX86() : context(), context_validity(CONTEXT_VALID_NONE) {}
// Register state. This is only fully valid for the topmost frame in a
// stack. In other frames, the values of nonvolatile registers may be
// present, given sufficient debugging information. Refer to
// context_validity.
MDRawContextX86 context;
// context_validity is actually ContextValidity, but int is used because
// the OR operator doesn't work well with enumerated types. This indicates
// which fields in context are valid.
int context_validity;
};
struct StackFramePPC : public StackFrame {
// ContextValidity should eventually contain entries for the validity of
// other nonvolatile (callee-save) registers as in
// StackFrameX86::ContextValidity, but the ppc stackwalker doesn't currently
// locate registers other than the ones listed here.
enum ContextValidity {
CONTEXT_VALID_NONE = 0,
CONTEXT_VALID_SRR0 = 1 << 0,
CONTEXT_VALID_GPR1 = 1 << 1,
CONTEXT_VALID_ALL = -1
};
StackFramePPC() : context(), context_validity(CONTEXT_VALID_NONE) {}
// Register state. This is only fully valid for the topmost frame in a
// stack. In other frames, the values of nonvolatile registers may be
// present, given sufficient debugging information. Refer to
// context_validity.
MDRawContextPPC context;
// context_validity is actually ContextValidity, but int is used because
// the OR operator doesn't work well with enumerated types. This indicates
// which fields in context are valid.
int context_validity;
};
} // namespace google_airbag
#endif // GOOGLE_STACK_FRAME_CPU_H__

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 ^ =

View file

@ -1179,7 +1179,7 @@ FILE 1178 f:\rtm\public\sdk\inc\ddbanned.h
FILE 1179 f:\rtm\vctools\crt_bld\self_x86\crt\prebuild\h\vadefs.h
FILE 1180 f:\rtm\vctools\crt_bld\self_x86\crt\prebuild\h\cruntime.h
FILE 1181 f:\rtm\public\sdk\inc\tvout.h
FUNC 1000 187 main
FUNC 1000 187 8 main
1000 39 24 172
1039 11 25 172
104a 1b 26 172
@ -1197,21 +1197,21 @@ FUNC 1000 187 main
1142 9 47 172
114b 16 48 172
1161 26 49 172
FUNC 1190 a ATL::CComBSTR::~CComBSTR
FUNC 1190 a 0 ATL::CComBSTR::~CComBSTR()
1190 0 1351 28
1190 9 1352 28
1199 1 1353 28
FUNC 11a0 f ATL::CComPtr<IDiaEnumSymbolsByAddr>::~CComPtr<IDiaEnumSymbolsByAddr>
FUNC 11a0 f 0 ATL::CComPtr<IDiaEnumSymbols>::~CComPtr<IDiaEnumSymbols>()
11a0 0 25 26
11a0 f 26 26
FUNC 11b0 15 airbag::PDBSourceLineWriter::Close
FUNC 11b0 15 0 airbag::PDBSourceLineWriter::Close()
11b0 0 212 26
11b0 14 213 26
11c4 1 214 26
FUNC 11d0 10 airbag::PDBSourceLineWriter::PDBSourceLineWriter
FUNC 11d0 10 0 airbag::PDBSourceLineWriter::PDBSourceLineWriter()
11d0 f 22 26
11df 1 23 26
FUNC 11e0 163 airbag::PDBSourceLineWriter::Open
FUNC 11e0 163 4 airbag::PDBSourceLineWriter::Open(std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > const &)
11e0 24 28 26
1204 14 29 26
1218 c 31 26
@ -1230,7 +1230,7 @@ FUNC 11e0 163 airbag::PDBSourceLineWriter::Open
12fe 18 49 26
1316 1a 52 26
1330 13 53 26
FUNC 1350 19b airbag::PDBSourceLineWriter::PrintLines
FUNC 1350 19b 4 airbag::PDBSourceLineWriter::PrintLines(IDiaEnumLineNumbers *)
1350 29 55 26
1379 6 58 26
137f 3c 61 26
@ -1247,7 +1247,7 @@ FUNC 1350 19b airbag::PDBSourceLineWriter::PrintLines
14c5 1f 71 26
14e4 5 76 26
14e9 2 77 26
FUNC 14f0 1d3 airbag::PDBSourceLineWriter::PrintFunction
FUNC 14f0 1d3 4 airbag::PDBSourceLineWriter::PrintFunction(IDiaSymbol *)
14f0 28 86 26
1518 6 89 26
151e 19 90 26
@ -1273,7 +1273,7 @@ FUNC 14f0 1d3 airbag::PDBSourceLineWriter::PrintFunction
1675 20 118 26
1695 19 120 26
16ae 15 121 26
FUNC 16d0 326 airbag::PDBSourceLineWriter::PrintSourceFiles
FUNC 16d0 326 0 airbag::PDBSourceLineWriter::PrintSourceFiles()
16d0 29 123 26
16f9 6 124 26
16ff 17 125 26
@ -1304,7 +1304,7 @@ FUNC 16d0 326 airbag::PDBSourceLineWriter::PrintSourceFiles
19a3 33 142 26
19d6 17 148 26
19ed 9 153 26
FUNC 1a00 1b1 airbag::PDBSourceLineWriter::PrintFunctions
FUNC 1a00 1b1 0 airbag::PDBSourceLineWriter::PrintFunctions()
1a00 27 164 26
1a27 6 165 26
1a2d 17 166 26
@ -1328,7 +1328,7 @@ FUNC 1a00 1b1 airbag::PDBSourceLineWriter::PrintFunctions
1b95 12 199 26
1ba7 5 187 26
1bac 5 188 26
FUNC 1bc0 35 airbag::PDBSourceLineWriter::WriteMap
FUNC 1bc0 35 4 airbag::PDBSourceLineWriter::WriteMap(_iobuf *)
1bc0 0 201 26
1bc0 d 203 26
1bcd 12 204 26
@ -1337,13 +1337,13 @@ FUNC 1bc0 35 airbag::PDBSourceLineWriter::WriteMap
1beb 4 208 26
1bef 3 209 26
1bf2 3 210 26
FUNC 1c02 f __security_check_cookie
FUNC 1c02 f 0 __security_check_cookie
1c02 0 52 1111
1c02 6 55 1111
1c08 2 56 1111
1c0a 2 57 1111
1c0c 5 59 1111
FUNC 1c11 4b pre_cpp_init
FUNC 1c11 4b 0 pre_cpp_init
1c11 0 310 576
1c11 a 312 576
1c1b 5 322 576
@ -1351,7 +1351,7 @@ FUNC 1c11 4b pre_cpp_init
1c4a 9 334 576
1c53 8 335 576
1c5b 1 337 576
FUNC 1c5c 176 __tmainCRTStartup
FUNC 1c5c 176 0 __tmainCRTStartup
1c5c c 410 576
1c68 5 433 576
1c6d 9 458 576
@ -1389,7 +1389,7 @@ FUNC 1c5c 176 __tmainCRTStartup
1dc0 7 621 576
1dc7 5 623 576
1dcc 6 624 576
FUNC 1dd2 e2 pre_c_init
FUNC 1dd2 e2 0 pre_c_init
1dd2 0 221 576
1dd2 60 225 576
1e32 d 233 576
@ -1406,11 +1406,11 @@ FUNC 1dd2 e2 pre_c_init
1ea8 9 289 576
1eb1 2 292 576
1eb3 1 293 576
FUNC 1eb4 a mainCRTStartup
FUNC 1eb4 a 0 mainCRTStartup
1eb4 0 393 576
1eb4 5 400 576
1eb9 5 402 576
FUNC 1ebe 104 __report_gsfailure
FUNC 1ebe 104 0 __report_gsfailure
1ebe 9 140 730
1ec7 5 170 730
1ecc 6 171 730
@ -1447,7 +1447,7 @@ FUNC 1ebe 104 __report_gsfailure
1fa6 8 315 730
1fae 12 319 730
1fc0 2 320 730
FUNC 1fc8 9f _onexit
FUNC 1fc8 9f 4 _onexit
1fc8 c 79 481
1fd4 12 84 481
1fe6 5 86 481
@ -1463,13 +1463,13 @@ FUNC 1fc8 9f _onexit
2055 3 120 481
2058 6 121 481
205e 9 117 481
FUNC 2067 12 atexit
FUNC 2067 12 4 atexit
2067 0 126 481
2067 11 127 481
2078 1 128 481
FUNC 2079 24 _RTC_Initialize
FUNC 209d 24 _RTC_Terminate
FUNC 20d0 29 _ValidateImageBase
FUNC 2079 24 0 _RTC_Initialize
FUNC 209d 24 0 _RTC_Terminate
FUNC 20d0 29 4 _ValidateImageBase
20d0 0 44 893
20d0 b 50 893
20db 2 52 893
@ -1479,7 +1479,7 @@ FUNC 20d0 29 _ValidateImageBase
20e9 2 58 893
20eb d 62 893
20f8 1 68 893
FUNC 2100 42 _FindPESection
FUNC 2100 42 8 _FindPESection
2100 0 92 893
2100 9 99 893
2109 19 108 893
@ -1487,7 +1487,7 @@ FUNC 2100 42 _FindPESection
2132 a 108 893
213c 5 123 893
2141 1 124 893
FUNC 2142 6c _IsNonwritableInCurrentImage
FUNC 2142 6c 4 _IsNonwritableInCurrentImage
2142 c 152 893
214e 4 159 893
2152 e 167 893
@ -1500,18 +1500,18 @@ FUNC 2142 6c _IsNonwritableInCurrentImage
2188 17 190 893
219f 9 196 893
21a8 6 198 893
FUNC 21bc 45 __SEH_prolog4
FUNC 2201 14 __SEH_epilog4
FUNC 2215 23 _except_handler4
FUNC 2238 29 _setdefaultprecision
FUNC 21bc 45 0 _SEH_prolog4
FUNC 2201 14 0 _SEH_epilog4
FUNC 2215 23 10 _except_handler4
FUNC 2238 29 0 _setdefaultprecision
2238 1 30 1040
2239 27 31 1040
2260 1 32 1040
FUNC 2261 3 _setargv
FUNC 2261 3 0 _setargv
2261 0 56 616
2261 2 57 616
2263 1 58 616
FUNC 2264 94 __security_init_cookie
FUNC 2264 94 0 __security_init_cookie
2264 6 97 770
226a 21 117 770
228b 7 119 770
@ -1530,66 +1530,66 @@ FUNC 2264 94 __security_init_cookie
22e5 6 215 770
22eb b 216 770
22f6 2 218 770
STACK WIN 4 1000 187 39 0 8 8 23c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1023 164 16 0 8 c 23c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 576 - ^ =
STACK WIN 4 1190 a 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11b0 15 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11d0 10 2 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11e0 163 24 0 4 8 10 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1350 19b 29 0 4 c 1c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1362 189 17 0 4 10 1c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 32 - ^ =
STACK WIN 4 1363 188 16 0 4 14 1c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 36 - ^ = $ebx $T0 32 - ^ =
STACK WIN 4 14f0 1d3 28 0 4 c 1c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1502 1c1 16 0 4 10 1c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 32 - ^ =
STACK WIN 4 16d0 326 29 0 0 c 2c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 16e2 314 17 0 0 10 2c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 48 - ^ =
STACK WIN 4 16e3 313 16 0 0 14 2c 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 52 - ^ = $ebx $T0 48 - ^ =
STACK WIN 4 1a00 1b1 27 0 0 8 20 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1a12 19f 15 0 0 c 20 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 36 - ^ =
STACK WIN 4 1bc0 35 8 0 4 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1bc5 2d 3 0 4 4 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 1bc6 29 2 0 4 8 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 1c02 f 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1c11 4b 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1c5c 176 c 0 0 c 20 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 1d82 14 0 0 0 c 20 0 $T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + =
STACK WIN 4 1dd2 e2 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1eb4 a 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1ebe 104 9 0 0 0 328 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 1fc8 9f c 0 4 c 24 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 205e 8 0 0 4 c 24 0 $T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + =
STACK WIN 4 2067 12 0 0 4 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2079 24 2 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 207a 22 1 0 0 4 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 207b 20 0 0 0 8 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 209d 24 2 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 209e 22 1 0 0 4 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 209f 20 0 0 0 8 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 20d0 29 0 0 4 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2100 42 18 0 8 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 210e 33 a 0 8 4 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 210f 31 9 0 8 8 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 2118 27 0 0 8 c 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 2142 6c c 0 4 c 18 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 2188 14 0 0 4 c 18 0 $T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + =
STACK WIN 4 2215 23 0 0 10 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2238 29 1 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2239 27 0 0 0 4 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2261 3 0 0 0 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2261 3 0 0 4 0 0 0 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2264 94 15 0 0 0 10 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 2278 7e 1 0 0 4 10 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 4 2279 7c 0 0 0 8 10 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 4 2295 5f 0 0 0 c 10 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 0 1d82 14 0 0 0 0 0 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 0 205e 9 0 0 0 0 0 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 0 2188 14 0 0 0 0 0 0 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 4 1000 187 39 0 8 8 23c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1023 164 16 0 8 c 23c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 576 - ^ =
STACK WIN 4 1190 a 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11a0 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11b0 15 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11d0 10 2 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 11e0 163 24 0 4 8 10 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1350 19b 29 0 4 c 1c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1362 189 17 0 4 10 1c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 32 - ^ =
STACK WIN 4 1363 188 16 0 4 14 1c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 36 - ^ = $ebx $T0 32 - ^ =
STACK WIN 4 14f0 1d3 28 0 4 c 1c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1502 1c1 16 0 4 10 1c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 32 - ^ =
STACK WIN 4 16d0 326 29 0 0 c 2c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 16e2 314 17 0 0 10 2c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 48 - ^ =
STACK WIN 4 16e3 313 16 0 0 14 2c 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebp $T0 52 - ^ = $ebx $T0 48 - ^ =
STACK WIN 4 1a00 1b1 27 0 0 8 20 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1a12 19f 15 0 0 c 20 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 36 - ^ =
STACK WIN 4 1bc0 35 8 0 4 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1bc5 2d 3 0 4 4 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 1bc6 29 2 0 4 8 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 1c02 f 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1c11 4b 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1c5c 176 c 0 0 c 20 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 1d82 14 0 0 0 c 20 0 1 $T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + =
STACK WIN 4 1dd2 e2 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1eb4 a 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 1ebe 104 9 0 0 0 328 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 1fc8 9f c 0 4 c 24 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 205e 8 0 0 4 c 24 0 1 $T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + =
STACK WIN 4 2067 12 0 0 4 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2079 24 2 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 207a 22 1 0 0 4 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 207b 20 0 0 0 8 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 209d 24 2 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 209e 22 1 0 0 4 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 209f 20 0 0 0 8 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 20d0 29 0 0 4 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2100 42 18 0 8 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 210e 33 a 0 8 4 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 210f 31 9 0 8 8 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 2118 27 0 0 8 c 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + = $ebx $T0 4 - ^ =
STACK WIN 4 2142 6c c 0 4 c 18 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 2188 14 0 0 4 c 18 0 1 $T0 $ebp = $T2 $esp = $T1 .raSearchStart = $eip $T1 ^ = $ebp $T0 = $esp $T1 4 + = $L $T0 .cbSavedRegs - = $P $T1 4 + .cbParams + =
STACK WIN 4 2215 23 0 0 10 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2238 29 1 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2239 27 0 0 0 4 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2261 3 0 0 0 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2261 3 0 0 4 0 0 0 1 $T2 $esp .cbLocals + .cbSavedRegs + = $T0 .raSearchStart = $eip $T0 ^ = $esp $T0 4 + =
STACK WIN 4 2264 94 15 0 0 0 10 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + =
STACK WIN 4 2278 7e 1 0 0 4 10 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 4 2279 7c 0 0 0 8 10 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 4 2295 5f 0 0 0 c 10 0 1 $T0 $ebp = $eip $T0 4 + ^ = $ebp $T0 ^ = $esp $T0 8 + = $L $T0 .cbSavedRegs - = $P $T0 8 + .cbParams + = $ebx $T0 20 - ^ =
STACK WIN 0 1d82 14 0 0 0 0 0 0 0 0
STACK WIN 0 205e 9 0 0 0 0 0 0 0 0
STACK WIN 0 2188 14 0 0 0 0 0 0 0 0