mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-22 18:56:31 +00:00
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:
parent
5afd60b067
commit
246f406828
|
@ -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 \
|
||||
|
|
22
Makefile.in
22
Makefile.in
|
@ -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@
|
||||
|
|
|
@ -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(¶meter_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;
|
||||
|
|
|
@ -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
73
src/google/call_stack.h
Normal 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__
|
|
@ -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_;
|
||||
|
|
|
@ -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__
|
||||
|
|
103
src/google/stack_frame_cpu.h
Normal file
103
src/google/stack_frame_cpu.h
Normal 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__
|
50
src/processor/call_stack.cc
Normal file
50
src/processor/call_stack.cc
Normal 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
|
|
@ -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__
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
46
src/processor/testdata/minidump1.stack.out
vendored
46
src/processor/testdata/minidump1.stack.out
vendored
|
@ -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
|
||||
|
|
BIN
src/processor/testdata/minidump2.dmp
vendored
BIN
src/processor/testdata/minidump2.dmp
vendored
Binary file not shown.
2704
src/processor/testdata/minidump2.sym
vendored
2704
src/processor/testdata/minidump2.sym
vendored
File diff suppressed because it is too large
Load diff
16
src/processor/testdata/module1.out
vendored
16
src/processor/testdata/module1.out
vendored
|
@ -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 ^ =
|
||||
|
|
8
src/processor/testdata/module2.out
vendored
8
src/processor/testdata/module2.out
vendored
|
@ -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 ^ =
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue