From db3342a10ec30902aa9018b80e1d9a40bd01c487 Mon Sep 17 00:00:00 2001 From: mmentovai Date: Tue, 5 Dec 2006 22:52:28 +0000 Subject: [PATCH] Module API (#32). r=waylonis, bryner - Introduces a standard API for dealing with modules. MinidumpModule is now a concrete implementation of this API. Code may interact with single modules using the CodeModule interface, and collections of modules using its container, the CodeModules interface. - CodeModule is used directly by SymbolSupplier implementations and SourceLineResolver. Reliance on the specific implementation in MinidumpModule has been eliminated. - Module lists are now added to ProcessState objects. Module references in each stack frame are now pointers to objects in these module lists. - The sample minidump_stackwalk tool prints the module list after printing all threads' stacks. http://groups.google.com/group/airbag-dev/browse_frm/thread/a9c0550edde54cf8 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@74 4c0a9323-5329-0410-9bdc-e9ce6186880e --- Makefile.am | 47 ++- Makefile.in | 62 ++- aclocal.m4 | 17 + configure | 129 ++++-- configure.ac | 2 +- src/google_airbag/processor/code_module.h | 93 ++++ src/google_airbag/processor/code_modules.h | 98 +++++ src/google_airbag/processor/minidump.h | 84 ++-- src/google_airbag/processor/process_state.h | 8 +- src/google_airbag/processor/stack_frame.h | 18 +- src/google_airbag/processor/stackwalker.h | 12 +- src/google_airbag/processor/symbol_supplier.h | 4 +- src/processor/basic_code_module.h | 95 +++++ src/processor/basic_code_modules.cc | 112 +++++ src/processor/basic_code_modules.h | 85 ++++ src/processor/minidump.cc | 398 +++++++++++++----- src/processor/minidump_processor.cc | 18 +- src/processor/minidump_processor_unittest.cc | 45 +- src/processor/minidump_stackwalk.cc | 35 +- src/processor/postfix_evaluator_unittest.cc | 35 +- src/processor/process_state.cc | 3 + src/processor/range_map-inl.h | 29 ++ src/processor/range_map.h | 14 + src/processor/range_map_unittest.cc | 77 ++++ src/processor/simple_symbol_supplier.cc | 56 +-- src/processor/simple_symbol_supplier.h | 31 +- src/processor/source_line_resolver.cc | 24 +- .../source_line_resolver_unittest.cc | 56 ++- src/processor/stackwalker.cc | 15 +- src/processor/stackwalker_ppc.cc | 4 +- src/processor/stackwalker_ppc.h | 8 +- src/processor/stackwalker_selftest.cc | 35 +- src/processor/stackwalker_x86.cc | 4 +- src/processor/stackwalker_x86.h | 7 +- src/processor/testdata/minidump2.dump.out | 91 ++-- .../testdata/minidump2.stackwalk.out | 15 + 36 files changed, 1515 insertions(+), 351 deletions(-) create mode 100644 src/google_airbag/processor/code_module.h create mode 100644 src/google_airbag/processor/code_modules.h create mode 100644 src/processor/basic_code_module.h create mode 100644 src/processor/basic_code_modules.cc create mode 100644 src/processor/basic_code_modules.h diff --git a/Makefile.am b/Makefile.am index 7a85e1e3..6f1ca84e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -53,6 +53,8 @@ src_libairbag_la_SOURCES = \ src/google_airbag/common/airbag_types.h \ src/google_airbag/common/minidump_format.h \ src/google_airbag/processor/call_stack.h \ + src/google_airbag/processor/code_module.h \ + src/google_airbag/processor/code_modules.h \ src/google_airbag/processor/memory_region.h \ src/google_airbag/processor/minidump.h \ src/google_airbag/processor/minidump_processor.h \ @@ -63,6 +65,9 @@ src_libairbag_la_SOURCES = \ src/google_airbag/processor/symbol_supplier.h \ src/processor/address_map.h \ src/processor/address_map-inl.h \ + src/processor/basic_code_module.h \ + src/processor/basic_code_modules.cc \ + src/processor/basic_code_modules.h \ src/processor/call_stack.cc \ src/processor/contained_range_map.h \ src/processor/contained_range_map-inl.h \ @@ -126,6 +131,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/basic_code_modules.lo \ src/processor/call_stack.lo \ src/processor/minidump_processor.lo \ src/processor/minidump.lo \ @@ -154,6 +160,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/basic_code_modules.lo \ src/processor/call_stack.lo \ src/processor/minidump.lo \ src/processor/source_line_resolver.lo \ @@ -168,11 +175,13 @@ noinst_SCRIPTS = $(check_SCRIPTS) src_processor_minidump_dump_SOURCES = \ src/processor/minidump_dump.cc src_processor_minidump_dump_LDADD = \ + src/processor/basic_code_modules.lo \ src/processor/minidump.lo src_processor_minidump_stackwalk_SOURCES = \ src/processor/minidump_stackwalk.cc src_processor_minidump_stackwalk_LDADD = \ + src/processor/basic_code_modules.lo \ src/processor/call_stack.lo \ src/processor/minidump.lo \ src/processor/minidump_processor.lo \ @@ -186,16 +195,44 @@ src_processor_minidump_stackwalk_LDADD = \ ## Additional files to be included in a source distribution +## +## find src/client src/common src/processor/testdata src/tools \ +## -type f \! -wholename '*/.svn*' -print | sort EXTRA_DIST = \ $(SCRIPTS) \ - src/processor/testdata/minidump1.dmp \ - src/processor/testdata/minidump1.out \ - src/processor/testdata/minidump1.stack.out \ + src/client/minidump_file_writer.cc \ + src/client/minidump_file_writer.h \ + src/client/minidump_file_writer-inl.h \ + src/client/windows/airbag_client.sln \ + src/client/windows/handler/exception_handler.cc \ + src/client/windows/handler/exception_handler.h \ + src/client/windows/handler/exception_handler.vcproj \ + src/client/windows/sender/crash_report_sender.cc \ + src/client/windows/sender/crash_report_sender.h \ + src/client/windows/sender/crash_report_sender.vcproj \ + src/common/windows/guid_string.cc \ + src/common/windows/guid_string.h \ + src/common/windows/http_upload.cc \ + src/common/windows/http_upload.h \ + src/common/windows/pdb_source_line_writer.cc \ + src/common/windows/pdb_source_line_writer.h \ + src/common/windows/string_utils-inl.h \ src/processor/testdata/minidump2.dmp \ - src/processor/testdata/minidump2.sym \ + src/processor/testdata/minidump2.dump.out \ + src/processor/testdata/minidump2.stackwalk.out \ src/processor/testdata/module1.out \ src/processor/testdata/module2.out \ - src/processor/testdata/module3_bad.out + src/processor/testdata/module3_bad.out \ + src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym \ + src/processor/testdata/symbols/test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1/test_app.sym \ + src/processor/testdata/test_app.cc \ + src/tools/windows/dump_syms/dump_syms.cc \ + src/tools/windows/dump_syms/dump_syms.vcproj \ + src/tools/windows/dump_syms/run_regtest.sh \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.out \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb \ + src/tools/windows/symupload/symupload.cc \ + src/tools/windows/symupload/symupload.vcproj ## Additional rules diff --git a/Makefile.in b/Makefile.in index e1437da4..10d7108a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -110,8 +110,9 @@ libLTLIBRARIES_INSTALL = $(INSTALL) LTLIBRARIES = $(lib_LTLIBRARIES) src_libairbag_la_LIBADD = am__dirstamp = $(am__leading_dot)dirstamp -am_src_libairbag_la_OBJECTS = src/processor/call_stack.lo \ - src/processor/minidump.lo src/processor/minidump_processor.lo \ +am_src_libairbag_la_OBJECTS = src/processor/basic_code_modules.lo \ + src/processor/call_stack.lo src/processor/minidump.lo \ + src/processor/minidump_processor.lo \ src/processor/pathname_stripper.lo \ src/processor/process_state.lo \ src/processor/simple_symbol_supplier.lo \ @@ -137,12 +138,14 @@ am_src_processor_minidump_dump_OBJECTS = \ src/processor/minidump_dump.$(OBJEXT) src_processor_minidump_dump_OBJECTS = \ $(am_src_processor_minidump_dump_OBJECTS) -src_processor_minidump_dump_DEPENDENCIES = src/processor/minidump.lo +src_processor_minidump_dump_DEPENDENCIES = \ + src/processor/basic_code_modules.lo src/processor/minidump.lo am_src_processor_minidump_processor_unittest_OBJECTS = \ src/processor/minidump_processor_unittest.$(OBJEXT) src_processor_minidump_processor_unittest_OBJECTS = \ $(am_src_processor_minidump_processor_unittest_OBJECTS) src_processor_minidump_processor_unittest_DEPENDENCIES = \ + src/processor/basic_code_modules.lo \ src/processor/call_stack.lo \ src/processor/minidump_processor.lo src/processor/minidump.lo \ src/processor/process_state.lo src/processor/stackwalker.lo \ @@ -154,6 +157,7 @@ am_src_processor_minidump_stackwalk_OBJECTS = \ src_processor_minidump_stackwalk_OBJECTS = \ $(am_src_processor_minidump_stackwalk_OBJECTS) src_processor_minidump_stackwalk_DEPENDENCIES = \ + src/processor/basic_code_modules.lo \ src/processor/call_stack.lo src/processor/minidump.lo \ src/processor/minidump_processor.lo \ src/processor/pathname_stripper.lo \ @@ -189,6 +193,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/basic_code_modules.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 \ @@ -370,6 +375,8 @@ src_libairbag_la_SOURCES = \ src/google_airbag/common/airbag_types.h \ src/google_airbag/common/minidump_format.h \ src/google_airbag/processor/call_stack.h \ + src/google_airbag/processor/code_module.h \ + src/google_airbag/processor/code_modules.h \ src/google_airbag/processor/memory_region.h \ src/google_airbag/processor/minidump.h \ src/google_airbag/processor/minidump_processor.h \ @@ -380,6 +387,9 @@ src_libairbag_la_SOURCES = \ src/google_airbag/processor/symbol_supplier.h \ src/processor/address_map.h \ src/processor/address_map-inl.h \ + src/processor/basic_code_module.h \ + src/processor/basic_code_modules.cc \ + src/processor/basic_code_modules.h \ src/processor/call_stack.cc \ src/processor/contained_range_map.h \ src/processor/contained_range_map-inl.h \ @@ -421,6 +431,7 @@ src_processor_minidump_processor_unittest_SOURCES = \ src/processor/minidump_processor_unittest.cc src_processor_minidump_processor_unittest_LDADD = \ + src/processor/basic_code_modules.lo \ src/processor/call_stack.lo \ src/processor/minidump_processor.lo \ src/processor/minidump.lo \ @@ -452,6 +463,7 @@ src_processor_stackwalker_selftest_SOURCES = \ src/processor/stackwalker_selftest.cc src_processor_stackwalker_selftest_LDADD = \ + src/processor/basic_code_modules.lo \ src/processor/call_stack.lo \ src/processor/minidump.lo \ src/processor/source_line_resolver.lo \ @@ -464,12 +476,14 @@ src_processor_minidump_dump_SOURCES = \ src/processor/minidump_dump.cc src_processor_minidump_dump_LDADD = \ + src/processor/basic_code_modules.lo \ src/processor/minidump.lo src_processor_minidump_stackwalk_SOURCES = \ src/processor/minidump_stackwalk.cc src_processor_minidump_stackwalk_LDADD = \ + src/processor/basic_code_modules.lo \ src/processor/call_stack.lo \ src/processor/minidump.lo \ src/processor/minidump_processor.lo \ @@ -483,14 +497,39 @@ src_processor_minidump_stackwalk_LDADD = \ EXTRA_DIST = \ $(SCRIPTS) \ - src/processor/testdata/minidump1.dmp \ - src/processor/testdata/minidump1.out \ - src/processor/testdata/minidump1.stack.out \ + src/client/minidump_file_writer.cc \ + src/client/minidump_file_writer.h \ + src/client/minidump_file_writer-inl.h \ + src/client/windows/airbag_client.sln \ + src/client/windows/handler/exception_handler.cc \ + src/client/windows/handler/exception_handler.h \ + src/client/windows/handler/exception_handler.vcproj \ + src/client/windows/sender/crash_report_sender.cc \ + src/client/windows/sender/crash_report_sender.h \ + src/client/windows/sender/crash_report_sender.vcproj \ + src/common/windows/guid_string.cc \ + src/common/windows/guid_string.h \ + src/common/windows/http_upload.cc \ + src/common/windows/http_upload.h \ + src/common/windows/pdb_source_line_writer.cc \ + src/common/windows/pdb_source_line_writer.h \ + src/common/windows/string_utils-inl.h \ src/processor/testdata/minidump2.dmp \ - src/processor/testdata/minidump2.sym \ + src/processor/testdata/minidump2.dump.out \ + src/processor/testdata/minidump2.stackwalk.out \ src/processor/testdata/module1.out \ src/processor/testdata/module2.out \ - src/processor/testdata/module3_bad.out + src/processor/testdata/module3_bad.out \ + src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym \ + src/processor/testdata/symbols/test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1/test_app.sym \ + src/processor/testdata/test_app.cc \ + src/tools/windows/dump_syms/dump_syms.cc \ + src/tools/windows/dump_syms/dump_syms.vcproj \ + src/tools/windows/dump_syms/run_regtest.sh \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.out \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb \ + src/tools/windows/symupload/symupload.cc \ + src/tools/windows/symupload/symupload.vcproj all: all-am @@ -579,6 +618,8 @@ src/processor/$(am__dirstamp): src/processor/$(DEPDIR)/$(am__dirstamp): @$(mkdir_p) src/processor/$(DEPDIR) @: > src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/basic_code_modules.lo: src/processor/$(am__dirstamp) \ + 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) \ @@ -710,6 +751,8 @@ src/processor/stackwalker_selftest$(EXEEXT): $(src_processor_stackwalker_selftes mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f src/processor/address_map_unittest.$(OBJEXT) + -rm -f src/processor/basic_code_modules.$(OBJEXT) + -rm -f src/processor/basic_code_modules.lo -rm -f src/processor/call_stack.$(OBJEXT) -rm -f src/processor/call_stack.lo -rm -f src/processor/contained_range_map_unittest.$(OBJEXT) @@ -744,6 +787,7 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/address_map_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/basic_code_modules.Plo@am__quote@ @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@ @@ -941,7 +985,7 @@ check-TESTS: $(TESTS) distdir: $(DISTFILES) $(am__remove_distdir) mkdir $(distdir) - $(mkdir_p) $(distdir)/autotools $(distdir)/src $(distdir)/src/processor $(distdir)/src/processor/testdata + $(mkdir_p) $(distdir)/autotools $(distdir)/src $(distdir)/src/client $(distdir)/src/client/windows $(distdir)/src/client/windows/handler $(distdir)/src/client/windows/sender $(distdir)/src/common/windows $(distdir)/src/processor $(distdir)/src/processor/testdata $(distdir)/src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542 $(distdir)/src/processor/testdata/symbols/test_app.pdb/8DDB7E9A365748938D6EB08B1DCA31AA1 $(distdir)/src/tools/windows/dump_syms $(distdir)/src/tools/windows/dump_syms/testdata $(distdir)/src/tools/windows/symupload @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ diff --git a/aclocal.m4 b/aclocal.m4 index 094df7b9..12d1afbe 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -7061,6 +7061,23 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) +# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 diff --git a/configure b/configure index f51a9c5a..d0a4b9e7 100755 --- a/configure +++ b/configure @@ -2375,7 +2375,88 @@ INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" AMTAR=${AMTAR-"${am_missing_run}tar"} -am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -' + +{ echo "$as_me:$LINENO: checking how to create a ustar tar archive" >&5 +echo $ECHO_N "checking how to create a ustar tar archive... $ECHO_C" >&6; } +# Loop over all known methods to create a tar archive until one works. +_am_tools='gnutar plaintar pax cpio none' +_am_tools=${am_cv_prog_tar_ustar-$_am_tools} +# Do not fold the above two line into one, because Tru64 sh and +# Solaris sh will not grok spaces in the rhs of `-'. +for _am_tool in $_am_tools +do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; + do + { echo "$as_me:$LINENO: $_am_tar --version" >&5 + ($_am_tar --version) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && break + done + am__tar="$_am_tar --format=ustar -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=ustar -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x ustar -w "$$tardir"' + am__tar_='pax -L -x ustar -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H ustar -L' + am__tar_='find "$tardir" -print | cpio -o -H ustar -L' + am__untar='cpio -i -H ustar -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_ustar}" && break + + # tar/untar a dummy directory, and stop if the command works + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5 + (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + rm -rf conftest.dir + if test -s conftest.tar; then + { echo "$as_me:$LINENO: $am__untar &5 + ($am__untar &5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + grep GrepMe conftest.dir/file >/dev/null 2>&1 && break + fi +done +rm -rf conftest.dir + +if test "${am_cv_prog_tar_ustar+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + am_cv_prog_tar_ustar=$_am_tool +fi + +{ echo "$as_me:$LINENO: result: $am_cv_prog_tar_ustar" >&5 +echo "${ECHO_T}$am_cv_prog_tar_ustar" >&6; } @@ -5154,7 +5235,7 @@ ia64-*-hpux*) ;; *-*-irix6*) # Find out which ABI we are using. - echo '#line 5157 "configure"' > conftest.$ac_ext + echo '#line 5238 "configure"' > conftest.$ac_ext if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 (eval $ac_compile) 2>&5 ac_status=$? @@ -7319,11 +7400,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7322: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7403: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7326: \$? = $ac_status" >&5 + echo "$as_me:7407: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7587,11 +7668,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7590: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7671: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:7594: \$? = $ac_status" >&5 + echo "$as_me:7675: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -7691,11 +7772,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7694: $lt_compile\"" >&5) + (eval echo "\"\$as_me:7775: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:7698: \$? = $ac_status" >&5 + echo "$as_me:7779: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -10143,7 +10224,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext < conftest.$ac_ext <&5) + (eval echo "\"\$as_me:12695: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:12618: \$? = $ac_status" >&5 + echo "$as_me:12699: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -12715,11 +12796,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:12718: $lt_compile\"" >&5) + (eval echo "\"\$as_me:12799: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:12722: \$? = $ac_status" >&5 + echo "$as_me:12803: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -14285,11 +14366,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14288: $lt_compile\"" >&5) + (eval echo "\"\$as_me:14369: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:14292: \$? = $ac_status" >&5 + echo "$as_me:14373: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -14389,11 +14470,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14392: $lt_compile\"" >&5) + (eval echo "\"\$as_me:14473: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:14396: \$? = $ac_status" >&5 + echo "$as_me:14477: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized @@ -16619,11 +16700,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16622: $lt_compile\"" >&5) + (eval echo "\"\$as_me:16703: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:16626: \$? = $ac_status" >&5 + echo "$as_me:16707: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -16887,11 +16968,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16890: $lt_compile\"" >&5) + (eval echo "\"\$as_me:16971: $lt_compile\"" >&5) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&5 - echo "$as_me:16894: \$? = $ac_status" >&5 + echo "$as_me:16975: \$? = $ac_status" >&5 if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. @@ -16991,11 +17072,11 @@ else -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16994: $lt_compile\"" >&5) + (eval echo "\"\$as_me:17075: $lt_compile\"" >&5) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&5 - echo "$as_me:16998: \$? = $ac_status" >&5 + echo "$as_me:17079: \$? = $ac_status" >&5 if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized diff --git a/configure.ac b/configure.ac index f1c2cc86..4ad927ff 100644 --- a/configure.ac +++ b/configure.ac @@ -35,7 +35,7 @@ dnl Sanity check: the argument is just a file that should exist. AC_CONFIG_SRCDIR(README) AC_CONFIG_AUX_DIR(autotools) -AM_INIT_AUTOMAKE(subdir-objects) +AM_INIT_AUTOMAKE(subdir-objects tar-ustar) AM_CONFIG_HEADER(src/config.h) AC_PROG_CC diff --git a/src/google_airbag/processor/code_module.h b/src/google_airbag/processor/code_module.h new file mode 100644 index 00000000..2f6be0d6 --- /dev/null +++ b/src/google_airbag/processor/code_module.h @@ -0,0 +1,93 @@ +// 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. + +// code_module.h: Carries information about code modules that are loaded +// into a process. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_AIRBAG_PROCESSOR_CODE_MODULE_H__ +#define GOOGLE_AIRBAG_PROCESSOR_CODE_MODULE_H__ + +#include + +namespace google_airbag { + +using std::string; + +class CodeModule { + public: + virtual ~CodeModule() {} + + // The base address of this code module as it was loaded by the process. + // (u_int64_t)-1 on error. + virtual u_int64_t base_address() const = 0; + + // The size of the code module. 0 on error. + virtual u_int64_t size() const = 0; + + // The path or file name that the code module was loaded from. Empty on + // error. + virtual string code_file() const = 0; + + // An identifying string used to discriminate between multiple versions and + // builds of the same code module. This may contain a uuid, timestamp, + // version number, or any combination of this or other information, in an + // implementation-defined format. Empty on error. + virtual string code_identifier() const = 0; + + // The filename containing debugging information associated with the code + // module. If debugging information is stored in a file separate from the + // code module itself (as is the case when .pdb or .dSYM files are used), + // this will be different from code_file. If debugging information is + // stored in the code module itself (possibly prior to stripping), this + // will be the same as code_file. Empty on error. + virtual string debug_file() const = 0; + + // An identifying string similar to code_identifier, but identifies a + // specific version and build of the associated debug file. This may be + // the same as code_identifier when the debug_file and code_file are + // identical or when the same identifier is used to identify distinct + // debug and code files. + virtual string debug_identifier() const = 0; + + // A human-readable representation of the code module's version. Empty on + // error. + virtual string version() const = 0; + + // Creates a new copy of this CodeModule object, which the caller takes + // ownership of. The new CodeModule may be of a different concrete class + // than the CodeModule being copied, but will behave identically to the + // copied CodeModule as far as the CodeModule interface is concerned. + virtual const CodeModule* Copy() const = 0; +}; + +} // namespace google_airbag + +#endif // GOOGLE_AIRBAG_PROCESSOR_CODE_MODULE_H__ diff --git a/src/google_airbag/processor/code_modules.h b/src/google_airbag/processor/code_modules.h new file mode 100644 index 00000000..9373adaf --- /dev/null +++ b/src/google_airbag/processor/code_modules.h @@ -0,0 +1,98 @@ +// 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. + +// code_modules.h: Contains all of the CodeModule objects that were loaded +// into a single process. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_AIRBAG_PROCESSOR_CODE_MODULES_H__ +#define GOOGLE_AIRBAG_PROCESSOR_CODE_MODULES_H__ + +#include "google_airbag/common/airbag_types.h" + +namespace google_airbag { + +class CodeModule; + +class CodeModules { + public: + virtual ~CodeModules() {} + + // The number of contained CodeModule objects. + virtual unsigned int module_count() const = 0; + + // Random access to modules. Returns the module whose code is present + // at the address indicated by |address|. If no module is present at this + // address, returns NULL. Ownership of the returned CodeModule is retained + // by the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleForAddress(u_int64_t address) const = 0; + + // Returns the module corresponding to the main executable. If there is + // no main executable, returns NULL. Ownership of the returned CodeModule + // is retained by the CodeModules object; pointers returned by this method + // are valid for comparison with pointers returned by the other Get + // methods. + virtual const CodeModule* GetMainModule() const = 0; + + // Sequential access to modules. A sequence number of 0 corresponds to the + // module residing lowest in memory. If the sequence number is out of + // range, returns NULL. Ownership of the returned CodeModule is retained + // by the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleAtSequence( + unsigned int sequence) const = 0; + + // Sequential access to modules. This is similar to GetModuleAtSequence, + // except no ordering requirement is enforced. A CodeModules implementation + // may return CodeModule objects from GetModuleAtIndex in any order it + // wishes, provided that the order remain the same throughout the life of + // the CodeModules object. Typically, GetModuleAtIndex would be used by + // a caller to enumerate all CodeModule objects quickly when the enumeration + // does not require any ordering. If the index argument is out of range, + // returns NULL. Ownership of the returned CodeModule is retained by + // the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleAtIndex(unsigned int index) const = 0; + + // Creates a new copy of this CodeModules object, which the caller takes + // ownership of. The new object will also contain copies of the existing + // object's child CodeModule objects. The new CodeModules object may be of + // a different concrete class than the object being copied, but will behave + // identically to the copied object as far as the CodeModules and CodeModule + // interfaces are concerned, except that the order that GetModuleAtIndex + // returns objects in may differ between a copy and the original CodeModules + // object. + virtual const CodeModules* Copy() const = 0; +}; + +} // namespace google_airbag + +#endif // GOOGLE_AIRBAG_PROCESSOR_CODE_MODULES_H__ diff --git a/src/google_airbag/processor/minidump.h b/src/google_airbag/processor/minidump.h index 2a666fe7..d9392655 100644 --- a/src/google_airbag/processor/minidump.h +++ b/src/google_airbag/processor/minidump.h @@ -85,6 +85,8 @@ #include #include "google_airbag/common/minidump_format.h" +#include "google_airbag/processor/code_module.h" +#include "google_airbag/processor/code_modules.h" #include "google_airbag/processor/memory_region.h" @@ -162,7 +164,7 @@ class MinidumpStream : public MinidumpObject { // user wants). class MinidumpContext : public MinidumpStream { public: - ~MinidumpContext(); + virtual ~MinidumpContext(); // Returns an MD_CONTEXT_* value such as MD_CONTEXT_X86 or MD_CONTEXT_PPC // identifying the CPU type that the context was collected from. The @@ -217,7 +219,7 @@ class MinidumpContext : public MinidumpStream { class MinidumpMemoryRegion : public MinidumpObject, public MemoryRegion { public: - ~MinidumpMemoryRegion(); + virtual ~MinidumpMemoryRegion(); // Returns a pointer to the base of the memory region. Returns the // cached value if available, otherwise, reads the minidump file and @@ -272,7 +274,7 @@ class MinidumpMemoryRegion : public MinidumpObject, // provided here. class MinidumpThread : public MinidumpObject { public: - ~MinidumpThread(); + virtual ~MinidumpThread(); const MDRawThread* thread() const { return valid_ ? &thread_ : NULL; } MinidumpMemoryRegion* GetMemory(); @@ -308,7 +310,7 @@ class MinidumpThread : public MinidumpObject { // a process. class MinidumpThreadList : public MinidumpStream { public: - ~MinidumpThreadList(); + virtual ~MinidumpThreadList(); unsigned int thread_count() const { return valid_ ? thread_count_ : 0; } @@ -346,19 +348,24 @@ class MinidumpThreadList : public MinidumpStream { // code modules. Access is provided to various data referenced indirectly // by MDRawModule, such as the module's name and a specification for where // to locate debugging information for the module. -class MinidumpModule : public MinidumpObject { +class MinidumpModule : public MinidumpObject, + public CodeModule { public: - ~MinidumpModule(); + virtual ~MinidumpModule(); const MDRawModule* module() const { return valid_ ? &module_ : NULL; } - u_int64_t base_address() const { + + // CodeModule implementation + virtual u_int64_t base_address() const { return valid_ ? module_.base_of_image : static_cast(-1); } - u_int32_t size() const { return valid_ ? module_.size_of_image : 0; } - - // The name of the file containing this module's code (exe, dll, so, - // dylib). - const string* GetName(); + virtual u_int64_t size() const { return valid_ ? module_.size_of_image : 0; } + virtual string code_file() const; + virtual string code_identifier() const; + virtual string debug_file() const; + virtual string debug_identifier() const; + virtual string version() const; + virtual const CodeModule* Copy() const; // The CodeView record, which contains information to locate the module's // debugging information (pdb). This is returned as u_int8_t* because @@ -372,12 +379,6 @@ class MinidumpModule : public MinidumpObject { // field is not expected to be present. const MDImageDebugMisc* GetMiscRecord(); - // The filename of the file containing debugging information for this - // module. This data is supplied by the CodeView record, if present, or - // the miscellaneous debug record. As such, it will reference either a - // pdb or dbg file. - const string* GetDebugFilename(); - // Print a human-readable representation of the object to stdout. void Print(); @@ -392,6 +393,21 @@ class MinidumpModule : public MinidumpObject { // MinidumpModuleList handles that directly. bool Read(); + // Reads indirectly-referenced data, including the module name, CodeView + // record, and miscellaneous debugging record. This is necessary to allow + // MinidumpModuleList to fully construct MinidumpModule objects without + // requiring seeks to read a contiguous set of MinidumpModule objects. + // All auxiliary data should be available when Read is called, in order to + // allow the CodeModule getters to be const methods. + bool ReadAuxiliaryData(); + + // True after a successful Read. This is different from valid_, which is + // not set true until ReadAuxiliaryData also completes successfully. + // module_valid_ is only used by ReadAuxiliaryData and the functions it + // calls to determine whether the object is ready for auxiliary data to + // be read. + bool module_valid_; + MDRawModule module_; // Cached module name. @@ -407,9 +423,6 @@ class MinidumpModule : public MinidumpObject { // because the structure contains a variable-sized string and its exact // size cannot be known until it is processed. vector* misc_record_; - - // Cached debug filename. - const string* debug_filename_; }; @@ -417,18 +430,21 @@ class MinidumpModule : public MinidumpObject { // in the form of MinidumpModules. It maintains a map of these modules // so that it may easily provide a code module corresponding to a specific // address. -class MinidumpModuleList : public MinidumpStream { +class MinidumpModuleList : public MinidumpStream, + public CodeModules { public: - ~MinidumpModuleList(); + virtual ~MinidumpModuleList(); - unsigned int module_count() const { return valid_ ? module_count_ : 0; } - - // Sequential access to modules. - MinidumpModule* GetModuleAtIndex(unsigned int index) const; - - // Random access to modules. Returns the module whose code is present - // at the address identified by address. - MinidumpModule* GetModuleForAddress(u_int64_t address); + // CodeModules implementation. + virtual unsigned int module_count() const { + return valid_ ? module_count_ : 0; + } + virtual const MinidumpModule* GetModuleForAddress(u_int64_t address) const; + virtual const MinidumpModule* GetMainModule() const; + virtual const MinidumpModule* GetModuleAtSequence( + unsigned int sequence) const; + virtual const MinidumpModule* GetModuleAtIndex(unsigned int index) const; + virtual const CodeModules* Copy() const; // Print a human-readable representation of the object to stdout. void Print(); @@ -463,7 +479,7 @@ class MinidumpModuleList : public MinidumpStream { // memory minidumps contain all of a process' mapped memory. class MinidumpMemoryList : public MinidumpStream { public: - ~MinidumpMemoryList(); + virtual ~MinidumpMemoryList(); unsigned int region_count() const { return valid_ ? region_count_ : 0; } @@ -512,7 +528,7 @@ class MinidumpMemoryList : public MinidumpStream { // the exception occurred. class MinidumpException : public MinidumpStream { public: - ~MinidumpException(); + virtual ~MinidumpException(); const MDRawExceptionStream* exception() const { return valid_ ? &exception_ : NULL; @@ -547,7 +563,7 @@ class MinidumpException : public MinidumpStream { // the system on which the minidump was generated. See also MinidumpMiscInfo. class MinidumpSystemInfo : public MinidumpStream { public: - ~MinidumpSystemInfo(); + virtual ~MinidumpSystemInfo(); const MDRawSystemInfo* system_info() const { return valid_ ? &system_info_ : NULL; diff --git a/src/google_airbag/processor/process_state.h b/src/google_airbag/processor/process_state.h index eb75703a..6fd005b7 100644 --- a/src/google_airbag/processor/process_state.h +++ b/src/google_airbag/processor/process_state.h @@ -43,6 +43,7 @@ using std::string; using std::vector; class CallStack; +class CodeModules; class ProcessState { public: @@ -58,6 +59,7 @@ class ProcessState { string os_version() const { return os_version_; } string cpu() const { return cpu_; } string cpu_info() const { return cpu_info_; } + const CodeModules* modules() const { return modules_; } private: // MinidumpProcessor is responsible for building ProcessState objects. @@ -66,7 +68,7 @@ class ProcessState { // Disallow instantiation other than by friends. ProcessState() : crashed_(false), crash_reason_(), crash_address_(0), requesting_thread_(-1), threads_(), os_(), os_version_(), - cpu_(), cpu_info_() {} + cpu_(), cpu_info_(), modules_(NULL) {} // True if the process crashed, false if the dump was produced outside // of an exception handler. @@ -120,6 +122,10 @@ class ProcessState { // present in the dump, or additional identifying information is not // defined for the CPU family, this field will be empty. string cpu_info_; + + // The modules that were loaded into the process represented by the + // ProcessState. + const CodeModules *modules_; }; } // namespace google_airbag diff --git a/src/google_airbag/processor/stack_frame.h b/src/google_airbag/processor/stack_frame.h index fd9e9f40..5d75b9f3 100644 --- a/src/google_airbag/processor/stack_frame.h +++ b/src/google_airbag/processor/stack_frame.h @@ -35,15 +35,16 @@ namespace google_airbag { +class CodeModule; + using std::string; struct StackFrame { StackFrame() : instruction(), - module_base(), - module_name(), - function_base(), + module(NULL), function_name(), + function_base(), source_file_name(), source_line(), source_line_base() {} @@ -56,19 +57,16 @@ struct StackFrame { // but may not necessarily point to the exact beginning of that instruction. u_int64_t instruction; - // The base address of the module. - u_int64_t module_base; - // The module in which the instruction resides. - string module_name; + const CodeModule *module; + + // The function name, may be omitted if debug symbols are not available. + string function_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. - string function_name; - // The source file name, may be omitted if debug symbols are not available. string source_file_name; diff --git a/src/google_airbag/processor/stackwalker.h b/src/google_airbag/processor/stackwalker.h index e6c6e6e8..b3f2333e 100644 --- a/src/google_airbag/processor/stackwalker.h +++ b/src/google_airbag/processor/stackwalker.h @@ -46,10 +46,10 @@ namespace google_airbag { class CallStack; +class CodeModules; template class linked_ptr; class MemoryRegion; class MinidumpContext; -class MinidumpModuleList; struct StackFrame; struct StackFrameInfo; class SymbolSupplier; @@ -71,18 +71,18 @@ class Stackwalker { // argument. If no suitable concrete subclass exists, returns NULL. static Stackwalker* StackwalkerForCPU(MinidumpContext *context, MemoryRegion *memory, - MinidumpModuleList *modules, + const CodeModules *modules, SymbolSupplier *supplier); protected: // memory identifies a MemoryRegion that provides the stack memory - // for the stack to walk. modules, if non-NULL, is a MinidumpModuleList - // that is used to look up which code module each stack frame is + // for the stack to walk. modules, if non-NULL, is a CodeModules + // object that is used to look up which code module each stack frame is // associated with. supplier is an optional caller-supplied SymbolSupplier // implementation. If supplier is NULL, source line info will not be // resolved. Stackwalker(MemoryRegion *memory, - MinidumpModuleList *modules, + const CodeModules *modules, SymbolSupplier *supplier); // The stack memory to walk. Subclasses will require this region to @@ -110,7 +110,7 @@ class Stackwalker { // A list of modules, for populating each StackFrame's module information. // This field is optional and may be NULL. - MinidumpModuleList *modules_; + const CodeModules *modules_; // The optional SymbolSupplier for resolving source line info. SymbolSupplier *supplier_; diff --git a/src/google_airbag/processor/symbol_supplier.h b/src/google_airbag/processor/symbol_supplier.h index 264dfcf2..47a2be08 100644 --- a/src/google_airbag/processor/symbol_supplier.h +++ b/src/google_airbag/processor/symbol_supplier.h @@ -38,14 +38,14 @@ namespace google_airbag { using std::string; -class MinidumpModule; +class CodeModule; class SymbolSupplier { public: virtual ~SymbolSupplier() {} // Returns the path to the symbol file for the given module. - virtual string GetSymbolFile(MinidumpModule *module) = 0; + virtual string GetSymbolFile(const CodeModule *module) = 0; }; } // namespace google_airbag diff --git a/src/processor/basic_code_module.h b/src/processor/basic_code_module.h new file mode 100644 index 00000000..b05a4a76 --- /dev/null +++ b/src/processor/basic_code_module.h @@ -0,0 +1,95 @@ +// 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. + +// basic_code_module.h: Carries information about code modules that are loaded +// into a process. +// +// This is a basic concrete implementation of CodeModule. It cannot be +// instantiated directly, only based on other objects that implement +// the CodeModule interface. It exists to provide a CodeModule implementation +// a place to store information when the life of the original object (such as +// a MinidumpModule) cannot be guaranteed. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_BASIC_CODE_MODULE_H__ +#define PROCESSOR_BASIC_CODE_MODULE_H__ + +#include + +#include "google_airbag/processor/code_module.h" + +namespace google_airbag { + +using std::string; + +class BasicCodeModule : public CodeModule { + public: + // Creates a new BasicCodeModule given any existing CodeModule + // implementation. This is useful to make a copy of the data relevant to + // the CodeModule interface without requiring all of the resources that + // other CodeModule implementations may require. + explicit BasicCodeModule(const CodeModule *that) + : base_address_(that->base_address()), + size_(that->size()), + code_file_(that->code_file()), + code_identifier_(that->code_identifier()), + debug_file_(that->debug_file()), + debug_identifier_(that->debug_identifier()), + version_(that->version()) {} + virtual ~BasicCodeModule() {} + + // See code_module.h for descriptions of these methods and the associated + // members. + virtual u_int64_t base_address() const { return base_address_; } + virtual u_int64_t size() const { return size_; } + virtual string code_file() const { return code_file_; } + virtual string code_identifier() const { return code_identifier_; } + virtual string debug_file() const { return debug_file_; } + virtual string debug_identifier() const { return debug_identifier_; } + virtual string version() const { return version_; } + virtual const CodeModule* Copy() const { return new BasicCodeModule(this); } + + private: + u_int64_t base_address_; + u_int64_t size_; + string code_file_; + string code_identifier_; + string debug_file_; + string debug_identifier_; + string version_; + + // Disallow copy constructor and assignment operator. + BasicCodeModule(const BasicCodeModule &that); + void operator=(const BasicCodeModule &that); +}; + +} // namespace google_airbag + +#endif // PROCESSOR_BASIC_CODE_MODULE_H__ diff --git a/src/processor/basic_code_modules.cc b/src/processor/basic_code_modules.cc new file mode 100644 index 00000000..82395109 --- /dev/null +++ b/src/processor/basic_code_modules.cc @@ -0,0 +1,112 @@ +// 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. + +// basic_code_modules.cc: Contains all of the CodeModule objects that +// were loaded into a single process. +// +// See basic_code_modules.h for documentation. +// +// Author: Mark Mentovai + +#include + +#include "processor/basic_code_modules.h" +#include "google_airbag/processor/code_module.h" +#include "processor/linked_ptr.h" +#include "processor/range_map-inl.h" + +namespace google_airbag { + +BasicCodeModules::BasicCodeModules(const CodeModules *that) + : main_address_(0), + map_(new RangeMap >()) { + assert(that); + + const CodeModule *main_module = that->GetMainModule(); + if (main_module) + main_address_ = main_module->base_address(); + + unsigned int count = that->module_count(); + for (unsigned int module_sequence = 0; + module_sequence < count; + ++module_sequence) { + // Make a copy of the module and insert it into the map. Use + // GetModuleAtIndex because ordering is unimportant when slurping the + // entire list, and GetModuleAtIndex may be faster than + // GetModuleAtSequence. + const CodeModule *module = that->GetModuleAtIndex(module_sequence)->Copy(); + map_->StoreRange(module->base_address(), module->size(), + linked_ptr(module)); + } +} + +BasicCodeModules::~BasicCodeModules() { + delete map_; +} + +unsigned int BasicCodeModules::module_count() const { + return map_->GetCount(); +} + +const CodeModule* BasicCodeModules::GetModuleForAddress( + u_int64_t address) const { + linked_ptr module; + if (!map_->RetrieveRange(address, &module, NULL, NULL)) + return NULL; + + return module.get(); +} + +const CodeModule* BasicCodeModules::GetMainModule() const { + return GetModuleForAddress(main_address_); +} + +const CodeModule* BasicCodeModules::GetModuleAtSequence( + unsigned int sequence) const { + linked_ptr module; + if (!map_->RetrieveRangeAtIndex(sequence, &module, NULL, NULL)) + return NULL; + + return module.get(); +} + +const CodeModule* BasicCodeModules::GetModuleAtIndex( + unsigned int index) const { + // This class stores everything in a RangeMap, without any more-efficient + // way to walk the list of CodeModule objects. Implement GetModuleAtIndex + // using GetModuleAtSequence, which meets all of the requirements, and + // in addition, guarantees ordering. + return GetModuleAtSequence(index); +} + +const CodeModules* BasicCodeModules::Copy() const { + return new BasicCodeModules(this); +} + +} // namespace google_airbag diff --git a/src/processor/basic_code_modules.h b/src/processor/basic_code_modules.h new file mode 100644 index 00000000..73bccdfd --- /dev/null +++ b/src/processor/basic_code_modules.h @@ -0,0 +1,85 @@ +// 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. + +// basic_code_modules.h: Contains all of the CodeModule objects that +// were loaded into a single process. +// +// This is a basic concrete implementation of CodeModules. It cannot be +// instantiated directly, only based on other objects that implement +// the CodeModules interface. It exists to provide a CodeModules +// implementation a place to store information when the life of the original +// object (such as a MinidumpModuleList) cannot be guaranteed. +// +// Author: Mark Mentovai + +#ifndef PROCESSOR_BASIC_CODE_MODULES_H__ +#define PROCESSOR_BASIC_CODE_MODULES_H__ + +#include "google_airbag/processor/code_modules.h" + +namespace google_airbag { + +template class linked_ptr; +template class RangeMap; + +class BasicCodeModules : public CodeModules { + public: + // Creates a new BasicCodeModules object given any existing CodeModules + // implementation. This is useful to make a copy of the data relevant to + // the CodeModules and CodeModule interfaces without requiring all of the + // resources that other implementations may require. A copy will be + // made of each contained CodeModule using CodeModule::Copy. + explicit BasicCodeModules(const CodeModules *that); + + virtual ~BasicCodeModules(); + + // See code_modules.h for descriptions of these methods. + virtual unsigned int module_count() const; + virtual const CodeModule* GetModuleForAddress(u_int64_t address) const; + virtual const CodeModule* GetMainModule() const; + virtual const CodeModule* GetModuleAtSequence(unsigned int sequence) const; + virtual const CodeModule* GetModuleAtIndex(unsigned int index) const; + virtual const CodeModules* Copy() const; + + private: + // The base address of the main module. + u_int64_t main_address_; + + // The map used to contain each CodeModule, keyed by each CodeModule's + // address range. + RangeMap > *map_; + + // Disallow copy constructor and assignment operator. + BasicCodeModules(const BasicCodeModules &that); + void operator=(const BasicCodeModules &that); +}; + +} // namespace google_airbag + +#endif // PROCESSOR_BASIC_CODE_MODULES_H__ diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index ed2e2df5..9ffbc1d0 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -54,6 +54,8 @@ typedef SSIZE_T ssize_t; #include "processor/range_map-inl.h" #include "google_airbag/processor/minidump.h" +#include "processor/basic_code_module.h" +#include "processor/basic_code_modules.h" #include "processor/scoped_ptr.h" @@ -999,11 +1001,11 @@ void MinidumpThreadList::Print() { MinidumpModule::MinidumpModule(Minidump* minidump) : MinidumpObject(minidump), + module_valid_(false), module_(), name_(NULL), cv_record_(NULL), - misc_record_(NULL), - debug_filename_(NULL) { + misc_record_(NULL) { } @@ -1011,7 +1013,6 @@ MinidumpModule::~MinidumpModule() { delete name_; delete cv_record_; delete misc_record_; - delete debug_filename_; } @@ -1023,9 +1024,8 @@ bool MinidumpModule::Read() { cv_record_ = NULL; delete misc_record_; misc_record_ = NULL; - delete debug_filename_; - debug_filename_ = NULL; + module_valid_ = false; valid_ = false; if (!minidump_->ReadBytes(&module_, MD_MODULE_SIZE)) @@ -1062,24 +1062,239 @@ bool MinidumpModule::Read() { if (module_.size_of_image == 0 || high_address < module_.base_of_image) return false; + module_valid_ = true; + return true; +} + + +bool MinidumpModule::ReadAuxiliaryData() { + if (!module_valid_) + return false; + + // Each module must have a name. + name_ = minidump_->ReadString(module_.module_name_rva); + if (!name_) + return false; + + // CodeView and miscellaneous debug records are only required if the + // module indicates that they exist. + if (module_.cv_record.data_size && !GetCVRecord()) + return false; + + if (module_.misc_record.data_size && !GetMiscRecord()) + return false; + valid_ = true; return true; } -const string* MinidumpModule::GetName() { +string MinidumpModule::code_file() const { if (!valid_) - return NULL; + return ""; - if (!name_) - name_ = minidump_->ReadString(module_.module_name_rva); + return *name_; +} - return name_; + +string MinidumpModule::code_identifier() const { + if (!valid_) + return ""; + + MinidumpSystemInfo *minidump_system_info = minidump_->GetSystemInfo(); + if (!minidump_system_info) + return ""; + + const MDRawSystemInfo *raw_system_info = minidump_system_info->system_info(); + if (!raw_system_info) + return ""; + + string identifier; + + switch (raw_system_info->platform_id) { + case MD_OS_WIN32_NT: + case MD_OS_WIN32_WINDOWS: { + char identifier_string[17]; + snprintf(identifier_string, sizeof(identifier_string), "%08x%x", + module_.time_date_stamp, module_.size_of_image); + identifier = identifier_string; + break; + } + + case MD_OS_MAC_OS_X: { + // TODO(mmentovai): support uuid extension if present, otherwise fall + // back to version (from LC_ID_DYLIB?), otherwise fall back to something + // else. + identifier = "id"; + break; + } + + default: { + // Without knowing what OS generated the dump, we can't generate a good + // identifier. Return an empty string, signalling failure. + break; + } + } + + return identifier; +} + + +string MinidumpModule::debug_file() const { + if (!valid_) + return ""; + + string file; + // Prefer the CodeView record if present. + const MDCVInfoPDB70* cv_record_70 = + reinterpret_cast(&(*cv_record_)[0]); + if (cv_record_70) { + if (cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE) { + // GetCVRecord guarantees pdb_file_name is null-terminated. + file = reinterpret_cast(cv_record_70->pdb_file_name); + } else if (cv_record_70->cv_signature == MD_CVINFOPDB20_SIGNATURE) { + // It's actually a MDCVInfoPDB20 structure. + const MDCVInfoPDB20* cv_record_20 = + reinterpret_cast(&(*cv_record_)[0]); + + // GetCVRecord guarantees pdb_file_name is null-terminated. + file = reinterpret_cast(cv_record_20->pdb_file_name); + } + + // If there's a CodeView record but it doesn't match a known signature, + // try the miscellaneous record - but it's suspicious because + // GetCVRecord shouldn't have accepted a CodeView record that doesn't + // match a known signature. + } + + if (file.empty()) { + // No usable CodeView record. Try the miscellaneous debug record. + const MDImageDebugMisc* misc_record = + reinterpret_cast(&(*misc_record_)[0]); + if (misc_record) { + if (!misc_record->unicode) { + // If it's not Unicode, just stuff it into the string. It's unclear + // if misc_record->data is 0-terminated, so use an explicit size. + file = string( + reinterpret_cast(misc_record->data), + module_.misc_record.data_size - sizeof(MDImageDebugMisc)); + } else { + // There's a misc_record but it encodes the debug filename in UTF-16. + // (Actually, because miscellaneous records are so old, it's probably + // UCS-2.) Convert it to UTF-8 for congruity with the other strings + // that this method (and all other methods in the Minidump family) + // return. + + unsigned int bytes = + module_.misc_record.data_size - sizeof(MDImageDebugMisc); + if (bytes % 2 == 0) { + unsigned int utf16_words = bytes / 2; + + // UTF16ToUTF8 expects a vector, so create a temporary one + // and copy the UTF-16 data into it. + vector string_utf16(utf16_words); + if (utf16_words) + memcpy(&string_utf16[0], &misc_record->data, bytes); + + // GetMiscRecord already byte-swapped the data[] field if it contains + // UTF-16, so pass false as the swap argument. + scoped_ptr new_file(UTF16ToUTF8(string_utf16, false)); + file = *new_file; + } + } + } + } + + return file; +} + + +string MinidumpModule::debug_identifier() const { + if (!valid_) + return ""; + + string identifier; + + // Use the CodeView record if present. + const MDCVInfoPDB70* cv_record_70 = + reinterpret_cast(&(*cv_record_)[0]); + if (cv_record_70) { + if (cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE) { + char identifier_string[41]; + snprintf(identifier_string, sizeof(identifier_string), + "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X", + cv_record_70->signature.data1, + cv_record_70->signature.data2, + cv_record_70->signature.data3, + cv_record_70->signature.data4[0], + cv_record_70->signature.data4[1], + cv_record_70->signature.data4[2], + cv_record_70->signature.data4[3], + cv_record_70->signature.data4[4], + cv_record_70->signature.data4[5], + cv_record_70->signature.data4[6], + cv_record_70->signature.data4[7], + cv_record_70->age); + identifier = identifier_string; + } else if (cv_record_70->cv_signature == MD_CVINFOPDB20_SIGNATURE) { + // It's actually a MDCVInfoPDB20 structure. + const MDCVInfoPDB20* cv_record_20 = + reinterpret_cast(&(*cv_record_)[0]); + + char identifier_string[17]; + snprintf(identifier_string, sizeof(identifier_string), + "%08x%x", cv_record_20->signature, cv_record_20->age); + identifier = identifier_string; + } + } + + // TODO(mmentovai): if there's no CodeView record, there might be a + // miscellaneous debug record. It only carries a filename, though, and no + // identifier. I'm not sure what the right thing to do for the identifier + // is in that case, but I don't expect to find many modules without a + // CodeView record (or some other Airbag extension structure in place of + // a CodeView record). Treat it as an error (empty identifier) for now. + + // TODO(mmentovai): on the Mac, provide fallbacks as in code_identifier(). + + return identifier; +} + + +string MinidumpModule::version() const { + if (!valid_) + return ""; + + string version; + + if (module_.version_info.signature == MD_VSFIXEDFILEINFO_SIGNATURE && + module_.version_info.struct_version & MD_VSFIXEDFILEINFO_VERSION) { + char version_string[24]; + snprintf(version_string, sizeof(version_string), "%u.%u.%u.%u", + module_.version_info.file_version_hi >> 16, + module_.version_info.file_version_hi & 0xffff, + module_.version_info.file_version_lo >> 16, + module_.version_info.file_version_lo & 0xffff); + version = version_string; + } + + // TODO(mmentovai): possibly support other struct types in place of + // the one used with MD_VSFIXEDFILEINFO_SIGNATURE. We can possibly use + // a different structure that better represents versioning facilities on + // Mac OS X and Linux, instead of forcing them to adhere to the dotted + // quad of 16-bit ints that Windows uses. + + return version; +} + + +const CodeModule* MinidumpModule::Copy() const { + return new BasicCodeModule(this); } const u_int8_t* MinidumpModule::GetCVRecord() { - if (!valid_) + if (!module_valid_) return NULL; if (!cv_record_) { @@ -1157,7 +1372,7 @@ const u_int8_t* MinidumpModule::GetCVRecord() { const MDImageDebugMisc* MinidumpModule::GetMiscRecord() { - if (!valid_) + if (!module_valid_) return NULL; if (!misc_record_) { @@ -1216,83 +1431,6 @@ const MDImageDebugMisc* MinidumpModule::GetMiscRecord() { } -// This method will perform no allocation-size checking on its own; it relies -// on GetCVRecord() and GetMiscRecord() to have made the determination that -// the necessary structures aren't oversized. -const string* MinidumpModule::GetDebugFilename() { - if (!valid_) - return NULL; - - if (!debug_filename_) { - // Prefer the CodeView record if present. - const MDCVInfoPDB70* cv_record_70 = - reinterpret_cast(GetCVRecord()); - if (cv_record_70) { - if (cv_record_70->cv_signature == MD_CVINFOPDB70_SIGNATURE) { - // GetCVRecord guarantees pdb_file_name is null-terminated. - debug_filename_ = new string( - reinterpret_cast(cv_record_70->pdb_file_name)); - - return debug_filename_; - } else if (cv_record_70->cv_signature == MD_CVINFOPDB20_SIGNATURE) { - // It's actually a MDCVInfoPDB20 structure. - const MDCVInfoPDB20* cv_record_20 = - reinterpret_cast(cv_record_70); - - // GetCVRecord guarantees pdb_file_name is null-terminated. - debug_filename_ = new string( - reinterpret_cast(cv_record_20->pdb_file_name)); - - return debug_filename_; - } - - // If there's a CodeView record but it doesn't match either of those - // signatures, try the miscellaneous record - but it's suspicious because - // GetCVRecord shouldn't have returned a CodeView record that doesn't - // match either signature. - } - - // No usable CodeView record. Try the miscellaneous debug record. - const MDImageDebugMisc* misc_record = GetMiscRecord(); - if (!misc_record) - return NULL; - - if (!misc_record->unicode) { - // If it's not Unicode, just stuff it into the string. It's unclear - // if misc_record->data is 0-terminated, so use an explicit size. - debug_filename_ = new string( - reinterpret_cast(misc_record->data), - module_.misc_record.data_size - sizeof(MDImageDebugMisc)); - - return debug_filename_; - } - - // There's a misc_record but it encodes the debug filename in UTF-16. - // (Actually, because miscellaneous records are so old, it's probably - // UCS-2.) Convert it to UTF-8 for congruity with the other strings that - // this method (and all other methods in the Minidump family) return. - - unsigned int bytes = - module_.misc_record.data_size - sizeof(MDImageDebugMisc); - if (bytes % 2 != 0) - return NULL; - unsigned int utf16_words = bytes / 2; - - // UTF16ToUTF8 expects a vector, so create a temporary one and - // copy the UTF-16 data into it. - vector string_utf16(utf16_words); - if (utf16_words) - memcpy(&string_utf16[0], &misc_record->data, bytes); - - // GetMiscRecord already byte-swapped the data[] field if it contains - // UTF-16, so pass false as the swap argument. - debug_filename_ = UTF16ToUTF8(string_utf16, false); - } - - return debug_filename_; -} - - void MinidumpModule::Print() { if (!valid_) return; @@ -1340,11 +1478,9 @@ void MinidumpModule::Print() { printf(" misc_record.rva = 0x%x\n", module_.misc_record.rva); - const char* module_name = GetName()->c_str(); - if (module_name) - printf(" (module_name) = \"%s\"\n", module_name); - else - printf(" (module_name) = (null)\n"); + printf(" (code_file) = \"%s\"\n", code_file().c_str()); + printf(" (code_identifier) = \"%s\"\n", + code_identifier().c_str()); const MDCVInfoPDB70* cv_record = reinterpret_cast(GetCVRecord()); @@ -1405,13 +1541,10 @@ void MinidumpModule::Print() { printf(" (misc_record) = (null)\n"); } - const string* debug_filename = GetDebugFilename(); - if (debug_filename) { - printf(" (debug_filename) = \"%s\"\n", - debug_filename->c_str()); - } else { - printf(" (debug_filename) = (null)\n"); - } + printf(" (debug_file) = \"%s\"\n", debug_file().c_str()); + printf(" (debug_identifier) = \"%s\"\n", + debug_identifier().c_str()); + printf(" (version) = \"%s\"\n", version().c_str()); printf("\n"); } @@ -1471,6 +1604,20 @@ bool MinidumpModuleList::Read(u_int32_t expected_size) { // Assume that the file offset is correct after the last read. if (!module->Read()) return false; + } + + // Loop through the module list once more to read additional data and + // build the range map. This is done in a second pass because + // MinidumpModule::ReadAuxiliaryData seeks around, and if it were + // included in the loop above, additional seeks would be needed where + // none are now to read contiguous data. + for (unsigned int module_index = 0; + module_index < module_count; + ++module_index) { + MinidumpModule* module = &(*modules)[module_index]; + + if (!module->ReadAuxiliaryData()) + return false; u_int64_t base_address = module->base_address(); u_int64_t module_size = module->size(); @@ -1491,16 +1638,8 @@ bool MinidumpModuleList::Read(u_int32_t expected_size) { } -MinidumpModule* MinidumpModuleList::GetModuleAtIndex(unsigned int index) - const { - if (!valid_ || index >= module_count_) - return NULL; - - return &(*modules_)[index]; -} - - -MinidumpModule* MinidumpModuleList::GetModuleForAddress(u_int64_t address) { +const MinidumpModule* MinidumpModuleList::GetModuleForAddress( + u_int64_t address) const { if (!valid_) return NULL; @@ -1512,6 +1651,43 @@ MinidumpModule* MinidumpModuleList::GetModuleForAddress(u_int64_t address) { } +const MinidumpModule* MinidumpModuleList::GetMainModule() const { + if (!valid_) + return NULL; + + // The main code module is the first one present in a minidump file's + // MDRawModuleList. + return GetModuleAtSequence(0); +} + + +const MinidumpModule* MinidumpModuleList::GetModuleAtSequence( + unsigned int sequence) const { + if (!valid_ || sequence >= module_count_) + return NULL; + + unsigned int module_index; + if (!range_map_->RetrieveRangeAtIndex(sequence, &module_index, NULL, NULL)) + return NULL; + + return GetModuleAtIndex(module_index); +} + + +const MinidumpModule* MinidumpModuleList::GetModuleAtIndex( + unsigned int index) const { + if (!valid_ || index >= module_count_) + return NULL; + + return &(*modules_)[index]; +} + + +const CodeModules* MinidumpModuleList::Copy() const { + return new BasicCodeModules(this); +} + + void MinidumpModuleList::Print() { if (!valid_) return; diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc index f9f8066a..d2c44159 100644 --- a/src/processor/minidump_processor.cc +++ b/src/processor/minidump_processor.cc @@ -75,6 +75,14 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) { &dump, &process_state->crash_address_); } + MinidumpModuleList *module_list = dump.GetModuleList(); + + // Put a copy of the module list into ProcessState object. This is not + // necessarily a MinidumpModuleList, but it adheres to the CodeModules + // interface, which is all that ProcessState needs to expose. + if (module_list) + process_state->modules_ = module_list->Copy(); + MinidumpThreadList *threads = dump.GetThreadList(); if (!threads) { return NULL; @@ -137,10 +145,18 @@ ProcessState* MinidumpProcessor::Process(const string &minidump_file) { return NULL; } + // Use process_state->modules_ instead of module_list, because the + // |modules| argument will be used to populate the |module| fields in + // the returned StackFrame objects, which will be placed into the + // returned ProcessState object. module_list's lifetime is only as + // long as the Minidump object: it will be deleted when this function + // returns. process_state->modules_ is owned by the ProcessState object + // (just like the StackFrame objects), and is much more suitable for this + // task. scoped_ptr stackwalker( Stackwalker::StackwalkerForCPU(context, thread_memory, - dump.GetModuleList(), + process_state->modules_, supplier_)); if (!stackwalker.get()) { return NULL; diff --git a/src/processor/minidump_processor_unittest.cc b/src/processor/minidump_processor_unittest.cc index 9166be92..f4b7b3c0 100644 --- a/src/processor/minidump_processor_unittest.cc +++ b/src/processor/minidump_processor_unittest.cc @@ -32,7 +32,8 @@ #include #include "google_airbag/processor/call_stack.h" -#include "google_airbag/processor/minidump.h" +#include "google_airbag/processor/code_module.h" +#include "google_airbag/processor/code_modules.h" #include "google_airbag/processor/minidump_processor.h" #include "google_airbag/processor/process_state.h" #include "google_airbag/processor/stack_frame.h" @@ -43,7 +44,7 @@ namespace { using std::string; using google_airbag::CallStack; -using google_airbag::MinidumpModule; +using google_airbag::CodeModule; using google_airbag::MinidumpProcessor; using google_airbag::ProcessState; using google_airbag::scoped_ptr; @@ -55,15 +56,17 @@ using google_airbag::SymbolSupplier; return false; \ } +#define ASSERT_FALSE(cond) ASSERT_TRUE(!(cond)) + #define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) class TestSymbolSupplier : public SymbolSupplier { public: - virtual string GetSymbolFile(MinidumpModule *module); + virtual string GetSymbolFile(const CodeModule *module); }; -string TestSymbolSupplier::GetSymbolFile(MinidumpModule *module) { - if (*(module->GetName()) == "c:\\test_app.exe") { +string TestSymbolSupplier::GetSymbolFile(const CodeModule *module) { + if (module && module->code_file() == "c:\\test_app.exe") { // The funny-looking pathname is so that the symbol file can also be // reached by a SimpleSymbolSupplier. return string(getenv("srcdir") ? getenv("srcdir") : ".") + @@ -92,38 +95,54 @@ static bool RunTests() { ASSERT_EQ(state->crash_address(), 0x45); ASSERT_EQ(state->threads()->size(), 1); ASSERT_EQ(state->requesting_thread(), 0); + CallStack *stack = state->threads()->at(0); ASSERT_TRUE(stack); ASSERT_EQ(stack->frames()->size(), 4); - ASSERT_EQ(stack->frames()->at(0)->module_base, 0x400000); - ASSERT_EQ(stack->frames()->at(0)->module_name, "c:\\test_app.exe"); + ASSERT_TRUE(stack->frames()->at(0)->module); + ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000); + ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "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, 51); - ASSERT_EQ(stack->frames()->at(1)->module_base, 0x400000); - ASSERT_EQ(stack->frames()->at(1)->module_name, "c:\\test_app.exe"); + ASSERT_TRUE(stack->frames()->at(1)->module); + ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000); + ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "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, 56); // This comes from the CRT - ASSERT_EQ(stack->frames()->at(2)->module_base, 0x400000); - ASSERT_EQ(stack->frames()->at(2)->module_name, "c:\\test_app.exe"); + ASSERT_TRUE(stack->frames()->at(2)->module); + ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000); + ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "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()->at(2)->source_line, 318); // No debug info available for kernel32.dll - ASSERT_EQ(stack->frames()->at(3)->module_base, 0x7c800000); - ASSERT_EQ(stack->frames()->at(3)->module_name, + ASSERT_TRUE(stack->frames()->at(3)->module); + ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000); + ASSERT_EQ(stack->frames()->at(3)->module->code_file(), "C:\\WINDOWS\\system32\\kernel32.dll"); 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); + ASSERT_EQ(state->modules()->module_count(), 13); + ASSERT_TRUE(state->modules()->GetMainModule()); + ASSERT_EQ(state->modules()->GetMainModule()->code_file(), "c:\\test_app.exe"); + ASSERT_FALSE(state->modules()->GetModuleForAddress(0)); + ASSERT_EQ(state->modules()->GetMainModule(), + state->modules()->GetModuleForAddress(0x400000)); + ASSERT_EQ(state->modules()->GetModuleForAddress(0x7c801234)->debug_file(), + "kernel32.pdb"); + ASSERT_EQ(state->modules()->GetModuleForAddress(0x77d43210)->version(), + "5.1.2600.2622"); + return true; } diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc index 4fbee425..62bfd555 100644 --- a/src/processor/minidump_stackwalk.cc +++ b/src/processor/minidump_stackwalk.cc @@ -37,6 +37,8 @@ #include #include "google_airbag/processor/call_stack.h" +#include "google_airbag/processor/code_module.h" +#include "google_airbag/processor/code_modules.h" #include "google_airbag/processor/minidump.h" #include "google_airbag/processor/minidump_processor.h" #include "google_airbag/processor/process_state.h" @@ -49,6 +51,8 @@ namespace { using std::string; using google_airbag::CallStack; +using google_airbag::CodeModule; +using google_airbag::CodeModules; using google_airbag::MinidumpModule; using google_airbag::MinidumpProcessor; using google_airbag::PathnameStripper; @@ -90,8 +94,8 @@ static void PrintStack(const CallStack *stack, const string &cpu) { const StackFrame *frame = stack->frames()->at(frame_index); printf("%2d ", frame_index); - if (!frame->module_name.empty()) { - printf("%s", PathnameStripper::File(frame->module_name).c_str()); + if (frame->module) { + printf("%s", PathnameStripper::File(frame->module->code_file()).c_str()); if (!frame->function_name.empty()) { printf("!%s", frame->function_name.c_str()); if (!frame->source_file_name.empty()) { @@ -104,7 +108,7 @@ static void PrintStack(const CallStack *stack, const string &cpu) { printf(" + 0x%llx", frame->instruction - frame->function_base); } } else { - printf(" + 0x%llx", frame->instruction - frame->module_base); + printf(" + 0x%llx", frame->instruction - frame->module->base_address()); } } else { printf("0x%llx", frame->instruction); @@ -147,6 +151,29 @@ static void PrintStack(const CallStack *stack, const string &cpu) { } } +static void PrintModules(const CodeModules *modules) { + if (!modules) + return; + + printf("\n"); + printf("Loaded modules:\n"); + + u_int64_t main_address = modules->GetMainModule()->base_address(); + + unsigned int module_count = modules->module_count(); + for (unsigned int module_sequence = 0; + module_sequence < module_count; + ++module_sequence) { + const CodeModule *module = modules->GetModuleAtSequence(module_sequence); + u_int64_t base_address = module->base_address(); + printf("0x%08llx - 0x%08llx %s %s%s\n", + base_address, base_address + module->size() - 1, + PathnameStripper::File(module->code_file()).c_str(), + module->version().empty() ? "???" : module->version().c_str(), + module->base_address() == main_address ? " (main)" : ""); + } +} + // Processes |minidump_file| using MinidumpProcessor. |symbol_path|, if // non-empty, is the base directory of a symbol storage area, laid out in // the format required by SimpleSymbolSupplier. If such a storage area @@ -217,6 +244,8 @@ static bool PrintMinidumpProcess(const string &minidump_file, } } + PrintModules(process_state->modules()); + return true; } diff --git a/src/processor/postfix_evaluator_unittest.cc b/src/processor/postfix_evaluator_unittest.cc index 9fac632d..2d16e5c9 100644 --- a/src/processor/postfix_evaluator_unittest.cc +++ b/src/processor/postfix_evaluator_unittest.cc @@ -1,16 +1,31 @@ -// Copyright (C) 2006 Google Inc. +// Copyright (c) 2006, Google Inc. +// All rights reserved. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: // -// http://www.apache.org/licenses/LICENSE-2.0 +// * 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. // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// 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. // postfix_evaluator_unittest.cc: Unit tests for PostfixEvaluator. // diff --git a/src/processor/process_state.cc b/src/processor/process_state.cc index b7c2239e..895ee5f7 100644 --- a/src/processor/process_state.cc +++ b/src/processor/process_state.cc @@ -35,6 +35,7 @@ #include "google_airbag/processor/process_state.h" #include "google_airbag/processor/call_stack.h" +#include "google_airbag/processor/code_modules.h" namespace google_airbag { @@ -44,6 +45,8 @@ ProcessState::~ProcessState() { ++iterator) { delete *iterator; } + + delete modules_; } } // namespace google_airbag diff --git a/src/processor/range_map-inl.h b/src/processor/range_map-inl.h index 74ce8513..2ead2e65 100644 --- a/src/processor/range_map-inl.h +++ b/src/processor/range_map-inl.h @@ -141,6 +141,35 @@ bool RangeMap::RetrieveNearestRange( } +template +bool RangeMap::RetrieveRangeAtIndex( + int index, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) const { + if (!entry || index >= GetCount()) + return false; + + // Walk through the map. Although it's ordered, it's not a vector, so it + // can't be addressed directly by index. + MapConstIterator iterator = map_.begin(); + for (int this_index = 0; this_index < index; ++this_index) + ++iterator; + + *entry = iterator->second.entry(); + if (entry_base) + *entry_base = iterator->first; + if (entry_size) + *entry_size = iterator->first - iterator->second.base() + 1; + + return true; +} + + +template +int RangeMap::GetCount() const { + return map_.size(); +} + + template void RangeMap::Clear() { map_.clear(); diff --git a/src/processor/range_map.h b/src/processor/range_map.h index 301b82e5..72a96a96 100644 --- a/src/processor/range_map.h +++ b/src/processor/range_map.h @@ -75,6 +75,20 @@ class RangeMap { AddressType *entry_base, AddressType *entry_size) const; + // Treating all ranges as a list ordered by the address spaces that they + // occupy, locates the range at the index specified by index. Returns + // false if index is larger than the number of ranges stored, or if another + // parameter error occurs. entry_base and entry_size, if non-NULL, are set + // to the base and size of the entry's range. + // + // RetrieveRangeAtIndex is not optimized for speedy operation. + bool RetrieveRangeAtIndex(int index, EntryType *entry, + AddressType *entry_base, AddressType *entry_size) + const; + + // Returns the number of ranges stored in the RangeMap. + int GetCount() const; + // Empties the range map, restoring it to the state it was when it was // initially created. void Clear(); diff --git a/src/processor/range_map_unittest.cc b/src/processor/range_map_unittest.cc index cff5b0a9..b9657f64 100644 --- a/src/processor/range_map_unittest.cc +++ b/src/processor/range_map_unittest.cc @@ -243,6 +243,71 @@ static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) { } +// Test RetrieveRangeAtIndex, which is supposed to return objects in order +// according to their addresses. This test is performed by looping through +// the map, calling RetrieveRangeAtIndex for all possible indices in sequence, +// and verifying that each call returns a different object than the previous +// call, and that ranges are returned with increasing base addresses. Returns +// false if the test fails. +static bool RetrieveIndexTest(TestMap *range_map, int set) { + linked_ptr object; + CountedObject *last_object = NULL; + AddressType last_base = 0; + + int object_count = range_map->GetCount(); + for (int object_index = 0; object_index < object_count; ++object_index) { + AddressType base; + if (!range_map->RetrieveRangeAtIndex(object_index, &object, &base, NULL)) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected success, observed failure\n", + set, object_index); + return false; + } + + if (!object.get()) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected object, observed NULL\n", + set, object_index); + return false; + } + + // It's impossible to do these comparisons unless there's a previous + // object to compare against. + if (last_object) { + // The object must be different from the last one. + if (object->id() == last_object->id()) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected different objects, observed same objects (%d)\n", + set, object_index, object->id()); + return false; + } + + // Each object must have a base greater than the previous object's base. + if (base <= last_base) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d, " + "expected different bases, observed same bases (%d)\n", + set, object_index, base); + return false; + } + } + + last_object = object.get(); + last_base = base; + } + + // Make sure that RetrieveRangeAtIndex doesn't allow lookups at indices that + // are too high. + if (range_map->RetrieveRangeAtIndex(object_count, &object, NULL, NULL)) { + fprintf(stderr, "FAILED: RetrieveRangeAtIndex set %d index %d (too large), " + "expected failure, observed success\n", + set, object_count); + return false; + } + + return true; +} + + // RunTests runs a series of test sets. static bool RunTests() { // These tests will be run sequentially. The first set of tests exercises @@ -373,6 +438,15 @@ static bool RunTests() { return false; } + // The RangeMap's own count of objects should also match. + if (range_map->GetCount() != stored_count) { + fprintf(stderr, "FAILED: stored object count doesn't match GetCount, " + "expected %d, observed %d\n", + stored_count, range_map->GetCount()); + + return false; + } + // Run the RetrieveRange test for (unsigned int range_test_index = 0; range_test_index < range_test_count; @@ -382,6 +456,9 @@ static bool RunTests() { return false; } + if (!RetrieveIndexTest(range_map.get(), range_test_set_index)) + return false; + // Clear the map between test sets. If this is the final test set, // delete the map instead to test destruction. if (range_test_set_index < range_test_set_count - 1) diff --git a/src/processor/simple_symbol_supplier.cc b/src/processor/simple_symbol_supplier.cc index 3b59739e..32b94342 100644 --- a/src/processor/simple_symbol_supplier.cc +++ b/src/processor/simple_symbol_supplier.cc @@ -34,61 +34,45 @@ // Author: Mark Mentovai #include "processor/simple_symbol_supplier.h" -#include "google_airbag/processor/minidump.h" +#include "google_airbag/processor/code_module.h" #include "processor/pathname_stripper.h" namespace google_airbag { -string SimpleSymbolSupplier::GetSymbolFileAtPath(MinidumpModule *module, +string SimpleSymbolSupplier::GetSymbolFileAtPath(const CodeModule *module, const string &root_path) { - // For now, only support modules that have GUIDs - which means - // MDCVInfoPDB70. - if (!module) return ""; - const MDCVInfoPDB70 *cv_record = - reinterpret_cast(module->GetCVRecord()); - if (!cv_record) - return ""; - - if (cv_record->cv_signature != MD_CVINFOPDB70_SIGNATURE) - return ""; - // Start with the base path. string path = root_path; - // Append the pdb file name as a directory name. + // Append the debug (pdb) file name as a directory name. path.append("/"); - string pdb_file_name = PathnameStripper::File( - reinterpret_cast(cv_record->pdb_file_name)); - path.append(pdb_file_name); + string debug_file_name = PathnameStripper::File(module->debug_file()); + if (debug_file_name.empty()) + return ""; + path.append(debug_file_name); - // Append the uuid and age as a directory name. + // Append the identifier as a directory name. path.append("/"); - char uuid_age_string[43]; - snprintf(uuid_age_string, sizeof(uuid_age_string), - "%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X%X", - cv_record->signature.data1, cv_record->signature.data2, - cv_record->signature.data3, - cv_record->signature.data4[0], cv_record->signature.data4[1], - cv_record->signature.data4[2], cv_record->signature.data4[3], - cv_record->signature.data4[4], cv_record->signature.data4[5], - cv_record->signature.data4[6], cv_record->signature.data4[7], - cv_record->age); - path.append(uuid_age_string); + string identifier = module->debug_identifier(); + if (identifier.empty()) + return ""; + path.append(identifier); - // Transform the pdb file name into one ending in .sym. If the existing + // Transform the debug file name into one ending in .sym. If the existing // name ends in .pdb, strip the .pdb. Otherwise, add .sym to the non-.pdb // name. path.append("/"); - string pdb_file_extension = pdb_file_name.substr(pdb_file_name.size() - 4); - transform(pdb_file_extension.begin(), pdb_file_extension.end(), - pdb_file_extension.begin(), tolower); - if (pdb_file_extension == ".pdb") { - path.append(pdb_file_name.substr(0, pdb_file_name.size() - 4)); + string debug_file_extension = + debug_file_name.substr(debug_file_name.size() - 4); + transform(debug_file_extension.begin(), debug_file_extension.end(), + debug_file_extension.begin(), tolower); + if (debug_file_extension == ".pdb") { + path.append(debug_file_name.substr(0, debug_file_name.size() - 4)); } else { - path.append(pdb_file_name); + path.append(debug_file_name); } path.append(".sym"); diff --git a/src/processor/simple_symbol_supplier.h b/src/processor/simple_symbol_supplier.h index f9f52af1..30485d24 100644 --- a/src/processor/simple_symbol_supplier.h +++ b/src/processor/simple_symbol_supplier.h @@ -33,14 +33,16 @@ // that stores symbol files in a filesystem tree. A SimpleSymbolSupplier is // created with a base directory, which is the root for all symbol files. // Each symbol file contained therien has a directory entry in the base -// directory with a name identical to the corresponding pdb file. Within -// each of these directories, there are subdirectories named for the uuid and -// age of each pdb file. The uuid is presented in hexadecimal form, with -// uppercase characters and no dashes. The age is appended to it in -// hexadecimal form, without any separators. Within that subdirectory, +// directory with a name identical to the corresponding debugging file (pdb). +// Within each of these directories, there are subdirectories named for the +// debugging file's identifier. For recent pdb files, this is a concatenation +// of the pdb's uuid and age, presented in hexadecimal form, using uppercase +// characters and no dashes or separators. Within that subdirectory, // SimpleSymbolSupplier expects to find the symbol file, which is named -// identically to the pdb file, but with a .sym extension. This sample -// hierarchy is rooted at the "symbols" base directory: +// identically to the debug file, but with a .sym extension. If the original +// debug file had a name ending in .pdb, the .pdb extension will be replaced +// with .sym. This sample hierarchy is rooted at the "symbols" base +// directory: // // symbols // symbols/test_app.pdb @@ -59,9 +61,11 @@ // SimpleSymbolServer, provided that the pdb files are transformed to dumped // format using a tool such as dump_syms, and given a .sym extension. // -// SimpleSymbolSupplier presently only supports symbol files that have -// the MSVC 7.0 CodeView record format. See MDCVInfoPDB70 in -// minidump_format.h. +// SimpleSymbolSupplier supports any debugging file which can be identified +// by a CodeModule object's debug_file and debug_identifier accessors. The +// expected ultimate source of these CodeModule objects are MinidumpModule +// objects; it is this class that is responsible for assigning appropriate +// values for debug_file and debug_identifier. // // Author: Mark Mentovai @@ -76,7 +80,7 @@ namespace google_airbag { using std::string; -class MinidumpModule; +class CodeModule; class SimpleSymbolSupplier : public SymbolSupplier { public: @@ -88,12 +92,13 @@ class SimpleSymbolSupplier : public SymbolSupplier { // Returns the path to the symbol file for the given module. See the // description above. - virtual string GetSymbolFile(MinidumpModule *module) { + virtual string GetSymbolFile(const CodeModule *module) { return GetSymbolFileAtPath(module, path_); } protected: - string GetSymbolFileAtPath(MinidumpModule *module, const string &root_path); + string GetSymbolFileAtPath(const CodeModule *module, + const string &root_path); private: string path_; diff --git a/src/processor/source_line_resolver.cc b/src/processor/source_line_resolver.cc index 66e31288..1ebc1bf4 100644 --- a/src/processor/source_line_resolver.cc +++ b/src/processor/source_line_resolver.cc @@ -39,6 +39,7 @@ #include "processor/range_map-inl.h" #include "processor/source_line_resolver.h" +#include "google_airbag/processor/code_module.h" #include "google_airbag/processor/stack_frame.h" #include "processor/linked_ptr.h" #include "processor/scoped_ptr.h" @@ -111,7 +112,7 @@ class SourceLineResolver::Module { // returned. If no additional information is available, returns NULL. // A NULL return value is not an error. The caller takes ownership of // any returned StackFrameInfo object. - StackFrameInfo* LookupAddress(MemAddr address, StackFrame *frame) const; + StackFrameInfo* LookupAddress(StackFrame *frame) const; private: friend class SourceLineResolver; @@ -206,10 +207,11 @@ bool SourceLineResolver::HasModule(const string &module_name) const { StackFrameInfo* SourceLineResolver::FillSourceLineInfo( StackFrame *frame) const { - ModuleMap::const_iterator it = modules_->find(frame->module_name); - if (it != modules_->end()) { - return it->second->LookupAddress(frame->instruction - frame->module_base, - frame); + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + return it->second->LookupAddress(frame); + } } return NULL; } @@ -273,8 +275,10 @@ bool SourceLineResolver::Module::LoadMap(const string &map_file) { return true; } -StackFrameInfo* SourceLineResolver::Module::LookupAddress( - MemAddr address, StackFrame *frame) const { +StackFrameInfo* SourceLineResolver::Module::LookupAddress(StackFrame *frame) + const { + MemAddr address = frame->instruction - frame->module->base_address(); + linked_ptr retrieved_info; // Check for debugging info first, before any possible early returns. // @@ -318,7 +322,7 @@ StackFrameInfo* SourceLineResolver::Module::LookupAddress( parameter_size = func->parameter_size; frame->function_name = func->name; - frame->function_base = frame->module_base + function_base; + frame->function_base = frame->module->base_address() + function_base; linked_ptr line; MemAddr line_base; @@ -328,7 +332,7 @@ StackFrameInfo* SourceLineResolver::Module::LookupAddress( frame->source_file_name = files_.find(line->source_file_id)->second; } frame->source_line = line->line; - frame->source_line_base = frame->module_base + line_base; + frame->source_line_base = frame->module->base_address() + line_base; } } else if (public_symbols_.Retrieve(address, &public_symbol, &public_address) && @@ -336,7 +340,7 @@ StackFrameInfo* SourceLineResolver::Module::LookupAddress( parameter_size = public_symbol->parameter_size; frame->function_name = public_symbol->name; - frame->function_base = frame->module_base + public_address; + frame->function_base = frame->module->base_address() + public_address; } else { // No FUNC or PUBLIC data available. return frame_info.release(); diff --git a/src/processor/source_line_resolver_unittest.cc b/src/processor/source_line_resolver_unittest.cc index e2ea0fee..38de9e2c 100644 --- a/src/processor/source_line_resolver_unittest.cc +++ b/src/processor/source_line_resolver_unittest.cc @@ -30,6 +30,7 @@ #include #include #include "processor/source_line_resolver.h" +#include "google_airbag/processor/code_module.h" #include "google_airbag/processor/stack_frame.h" #include "processor/linked_ptr.h" #include "processor/scoped_ptr.h" @@ -48,12 +49,33 @@ namespace { using std::string; +using google_airbag::CodeModule; using google_airbag::linked_ptr; using google_airbag::scoped_ptr; using google_airbag::SourceLineResolver; using google_airbag::StackFrame; using google_airbag::StackFrameInfo; +class TestCodeModule : public CodeModule { + public: + TestCodeModule(string code_file) : code_file_(code_file) {} + virtual ~TestCodeModule() {} + + virtual u_int64_t base_address() const { return 0; } + virtual u_int64_t size() const { return 0x4000; } + virtual string code_file() const { return code_file_; } + virtual string code_identifier() const { return ""; } + virtual string debug_file() const { return ""; } + virtual string debug_identifier() const { return ""; } + virtual string version() const { return ""; } + virtual const CodeModule* Copy() const { + return new TestCodeModule(code_file_); + } + + private: + string code_file_; +}; + static bool VerifyEmpty(const StackFrame &frame) { ASSERT_TRUE(frame.function_name.empty()); ASSERT_TRUE(frame.source_file_name.empty()); @@ -63,6 +85,7 @@ static bool VerifyEmpty(const StackFrame &frame) { static void ClearSourceLineInfo(StackFrame *frame) { frame->function_name.clear(); + frame->module = NULL; frame->source_file_name.clear(); frame->source_line = 0; } @@ -77,13 +100,28 @@ static bool RunTests() { ASSERT_TRUE(resolver.LoadModule("module2", testdata_dir + "/module2.out")); ASSERT_TRUE(resolver.HasModule("module2")); + TestCodeModule module1("module1"); + StackFrame frame; frame.instruction = 0x1000; - frame.module_name = "module1"; + frame.module = NULL; scoped_ptr frame_info(resolver.FillSourceLineInfo(&frame)); + ASSERT_FALSE(frame.module); + ASSERT_TRUE(frame.function_name.empty()); + ASSERT_EQ(frame.function_base, 0); + ASSERT_TRUE(frame.source_file_name.empty()); + ASSERT_EQ(frame.source_line, 0); + ASSERT_EQ(frame.source_line_base, 0); + + frame.module = &module1; + frame_info.reset(resolver.FillSourceLineInfo(&frame)); ASSERT_EQ(frame.function_name, "Function1_1"); + ASSERT_TRUE(frame.module); + ASSERT_EQ(frame.module->code_file(), "module1"); + ASSERT_EQ(frame.function_base, 0x1000); ASSERT_EQ(frame.source_file_name, "file1_1.cc"); ASSERT_EQ(frame.source_line, 44); + ASSERT_EQ(frame.source_line_base, 0x1000); ASSERT_TRUE(frame_info.get()); ASSERT_FALSE(frame_info->allocates_base_pointer); ASSERT_EQ(frame_info->program_string, @@ -91,6 +129,7 @@ static bool RunTests() { ClearSourceLineInfo(&frame); frame.instruction = 0x800; + frame.module = &module1; frame_info.reset(resolver.FillSourceLineInfo(&frame)); ASSERT_TRUE(VerifyEmpty(frame)); ASSERT_FALSE(frame_info.get()); @@ -113,28 +152,33 @@ static bool RunTests() { ASSERT_FALSE(frame_info->allocates_base_pointer); ASSERT_FALSE(frame_info->program_string.empty()); - frame.instruction = 0x2180; - frame.module_name = "module2"; + TestCodeModule module2("module2"); + + frame.instruction = 0x2181; + frame.module = &module2; frame_info.reset(resolver.FillSourceLineInfo(&frame)); ASSERT_EQ(frame.function_name, "Function2_2"); + ASSERT_EQ(frame.function_base, 0x2170); + ASSERT_TRUE(frame.module); + ASSERT_EQ(frame.module->code_file(), "module2"); ASSERT_EQ(frame.source_file_name, "file2_2.cc"); ASSERT_EQ(frame.source_line, 21); + ASSERT_EQ(frame.source_line_base, 0x2180); ASSERT_TRUE(frame_info.get()); ASSERT_EQ(frame_info->prolog_size, 1); frame.instruction = 0x216f; - frame.module_name = "module2"; resolver.FillSourceLineInfo(&frame); ASSERT_EQ(frame.function_name, "Public2_1"); ClearSourceLineInfo(&frame); frame.instruction = 0x219f; - frame.module_name = "module2"; + frame.module = &module2; resolver.FillSourceLineInfo(&frame); ASSERT_TRUE(frame.function_name.empty()); frame.instruction = 0x21a0; - frame.module_name = "module2"; + frame.module = &module2; resolver.FillSourceLineInfo(&frame); ASSERT_EQ(frame.function_name, "Public2_2"); diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc index 78f17363..4e4a6b9f 100644 --- a/src/processor/stackwalker.cc +++ b/src/processor/stackwalker.cc @@ -36,6 +36,8 @@ #include "google_airbag/processor/stackwalker.h" #include "google_airbag/processor/call_stack.h" +#include "google_airbag/processor/code_module.h" +#include "google_airbag/processor/code_modules.h" #include "google_airbag/processor/minidump.h" #include "google_airbag/processor/stack_frame.h" #include "google_airbag/processor/symbol_supplier.h" @@ -49,7 +51,7 @@ namespace google_airbag { -Stackwalker::Stackwalker(MemoryRegion *memory, MinidumpModuleList *modules, +Stackwalker::Stackwalker(MemoryRegion *memory, const CodeModules *modules, SymbolSupplier *supplier) : memory_(memory), modules_(modules), supplier_(supplier) { } @@ -80,15 +82,14 @@ CallStack* Stackwalker::Walk() { // Resolve the module information, if a module map was provided. if (modules_) { - MinidumpModule *module = + const CodeModule *module = modules_->GetModuleForAddress(frame->instruction); if (module) { - frame->module_name = *(module->GetName()); - frame->module_base = module->base_address(); - if (!resolver.HasModule(frame->module_name) && supplier_) { + frame->module = module; + if (!resolver.HasModule(frame->module->code_file()) && supplier_) { string symbol_file = supplier_->GetSymbolFile(module); if (!symbol_file.empty()) { - resolver.LoadModule(frame->module_name, symbol_file); + resolver.LoadModule(frame->module->code_file(), symbol_file); } } frame_info.reset(resolver.FillSourceLineInfo(frame.get())); @@ -114,7 +115,7 @@ CallStack* Stackwalker::Walk() { // static Stackwalker* Stackwalker::StackwalkerForCPU(MinidumpContext *context, MemoryRegion *memory, - MinidumpModuleList *modules, + const CodeModules *modules, SymbolSupplier *supplier) { Stackwalker *cpu_stackwalker = NULL; diff --git a/src/processor/stackwalker_ppc.cc b/src/processor/stackwalker_ppc.cc index a958f492..49b62f8c 100644 --- a/src/processor/stackwalker_ppc.cc +++ b/src/processor/stackwalker_ppc.cc @@ -36,7 +36,7 @@ #include "processor/stackwalker_ppc.h" #include "google_airbag/processor/call_stack.h" -#include "google_airbag/processor/minidump.h" +#include "google_airbag/processor/memory_region.h" #include "google_airbag/processor/stack_frame_cpu.h" namespace google_airbag { @@ -44,7 +44,7 @@ namespace google_airbag { StackwalkerPPC::StackwalkerPPC(const MDRawContextPPC *context, MemoryRegion *memory, - MinidumpModuleList *modules, + const CodeModules *modules, SymbolSupplier *supplier) : Stackwalker(memory, modules, supplier), context_(context) { diff --git a/src/processor/stackwalker_ppc.h b/src/processor/stackwalker_ppc.h index 10b6a7a5..bccc9dc2 100644 --- a/src/processor/stackwalker_ppc.h +++ b/src/processor/stackwalker_ppc.h @@ -45,19 +45,17 @@ namespace google_airbag { -class MinidumpContext; -class MinidumpModuleList; - +class CodeModules; class StackwalkerPPC : public Stackwalker { public: - // context is a MinidumpContext object that gives access to ppc-specific + // context is a ppc context object that gives access to ppc-specific // register state corresponding to the innermost called frame to be // included in the stack. The other arguments are passed directly through // to the base Stackwalker constructor. StackwalkerPPC(const MDRawContextPPC *context, MemoryRegion *memory, - MinidumpModuleList *modules, + const CodeModules *modules, SymbolSupplier *supplier); private: diff --git a/src/processor/stackwalker_selftest.cc b/src/processor/stackwalker_selftest.cc index 2534cfd5..877a8f3d 100644 --- a/src/processor/stackwalker_selftest.cc +++ b/src/processor/stackwalker_selftest.cc @@ -1,16 +1,31 @@ -// Copyright (C) 2006 Google Inc. +// Copyright (c) 2006, Google Inc. +// All rights reserved. // -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: // -// http://www.apache.org/licenses/LICENSE-2.0 +// * 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. // -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +// 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. // stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the // running process' stack as test data, if running on an x86 or ppc and diff --git a/src/processor/stackwalker_x86.cc b/src/processor/stackwalker_x86.cc index 61f42a10..1e3390f5 100644 --- a/src/processor/stackwalker_x86.cc +++ b/src/processor/stackwalker_x86.cc @@ -38,7 +38,7 @@ #include "processor/stackwalker_x86.h" #include "google_airbag/processor/call_stack.h" -#include "google_airbag/processor/minidump.h" +#include "google_airbag/processor/memory_region.h" #include "google_airbag/processor/stack_frame_cpu.h" #include "processor/linked_ptr.h" #include "processor/stack_frame_info.h" @@ -48,7 +48,7 @@ namespace google_airbag { StackwalkerX86::StackwalkerX86(const MDRawContextX86 *context, MemoryRegion *memory, - MinidumpModuleList *modules, + const CodeModules *modules, SymbolSupplier *supplier) : Stackwalker(memory, modules, supplier), context_(context) { diff --git a/src/processor/stackwalker_x86.h b/src/processor/stackwalker_x86.h index 707aa94b..fe2a7808 100644 --- a/src/processor/stackwalker_x86.h +++ b/src/processor/stackwalker_x86.h @@ -45,19 +45,18 @@ namespace google_airbag { -class MinidumpContext; -class MinidumpModuleList; +class CodeModules; class StackwalkerX86 : public Stackwalker { public: - // context is a MinidumpContext object that gives access to x86-specific + // context is an x86 context object that gives access to x86-specific // register state corresponding to the innermost called frame to be // included in the stack. The other arguments are passed directly through // to the base Stackwalker constructor. StackwalkerX86(const MDRawContextX86 *context, MemoryRegion *memory, - MinidumpModuleList *modules, + const CodeModules *modules, SymbolSupplier *supplier); private: diff --git a/src/processor/testdata/minidump2.dump.out b/src/processor/testdata/minidump2.dump.out index 492d5b5d..910f6256 100644 --- a/src/processor/testdata/minidump2.dump.out +++ b/src/processor/testdata/minidump2.dump.out @@ -200,13 +200,16 @@ MDRawModule cv_record.rva = 0x132c misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "c:\test_app.exe" + (code_file) = "c:\test_app.exe" + (code_identifier) = "454fa44d2b000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = 8ddb7e9a-3657-4893-8d6e-b08b1dca31aa (cv_record).age = 1 (cv_record).pdb_file_name = "c:\test_app.pdb" (misc_record) = (null) - (debug_filename) = "c:\test_app.pdb" + (debug_file) = "c:\test_app.pdb" + (debug_identifier) = "8DDB7E9A365748938D6EB08B1DCA31AA1" + (version) = "" module[1] MDRawModule @@ -229,13 +232,16 @@ MDRawModule cv_record.rva = 0x1354 misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\ntdll.dll" + (code_file) = "C:\WINDOWS\system32\ntdll.dll" + (code_identifier) = "411096b4b0000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = 36515fb5-d043-45e4-91f6-72fa2e2878c0 (cv_record).age = 2 (cv_record).pdb_file_name = "ntdll.pdb" (misc_record) = (null) - (debug_filename) = "ntdll.pdb" + (debug_file) = "ntdll.pdb" + (debug_identifier) = "36515FB5D04345E491F672FA2E2878C02" + (version) = "5.1.2600.2180" module[2] MDRawModule @@ -258,13 +264,16 @@ MDRawModule cv_record.rva = 0x1376 misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\kernel32.dll" + (code_file) = "C:\WINDOWS\system32\kernel32.dll" + (code_identifier) = "44ab9a84f4000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = bce8785c-57b4-4245-a669-896b6a19b954 (cv_record).age = 2 (cv_record).pdb_file_name = "kernel32.pdb" (misc_record) = (null) - (debug_filename) = "kernel32.pdb" + (debug_file) = "kernel32.pdb" + (debug_identifier) = "BCE8785C57B44245A669896B6A19B9542" + (version) = "5.1.2600.2945" module[3] MDRawModule @@ -287,13 +296,16 @@ MDRawModule cv_record.rva = 0x139b misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\ole32.dll" + (code_file) = "C:\WINDOWS\system32\ole32.dll" + (code_identifier) = "42e5be9313d000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = 683b65b2-46f4-4187-96d2-ee6d4c55eb11 (cv_record).age = 2 (cv_record).pdb_file_name = "ole32.pdb" (misc_record) = (null) - (debug_filename) = "ole32.pdb" + (debug_file) = "ole32.pdb" + (debug_identifier) = "683B65B246F4418796D2EE6D4C55EB112" + (version) = "5.1.2600.2726" module[4] MDRawModule @@ -316,13 +328,16 @@ MDRawModule cv_record.rva = 0x13bd misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\advapi32.dll" + (code_file) = "C:\WINDOWS\system32\advapi32.dll" + (code_identifier) = "411096a79b000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = 455d6c5f-184d-45bb-b5c5-f30f82975114 (cv_record).age = 2 (cv_record).pdb_file_name = "advapi32.pdb" (misc_record) = (null) - (debug_filename) = "advapi32.pdb" + (debug_file) = "advapi32.pdb" + (debug_identifier) = "455D6C5F184D45BBB5C5F30F829751142" + (version) = "5.1.2600.2180" module[5] MDRawModule @@ -345,13 +360,16 @@ MDRawModule cv_record.rva = 0x13e2 misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\rpcrt4.dll" + (code_file) = "C:\WINDOWS\system32\rpcrt4.dll" + (code_identifier) = "411096ae91000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = bea45a72-1da1-41da-a3ba-86b3a2031153 (cv_record).age = 2 (cv_record).pdb_file_name = "rpcrt4.pdb" (misc_record) = (null) - (debug_filename) = "rpcrt4.pdb" + (debug_file) = "rpcrt4.pdb" + (debug_identifier) = "BEA45A721DA141DAA3BA86B3A20311532" + (version) = "5.1.2600.2180" module[6] MDRawModule @@ -374,13 +392,16 @@ MDRawModule cv_record.rva = 0x1405 misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\gdi32.dll" + (code_file) = "C:\WINDOWS\system32\gdi32.dll" + (code_identifier) = "43b34feb47000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = c0ea66be-00a6-4bd7-aef7-9e443a91869c (cv_record).age = 2 (cv_record).pdb_file_name = "gdi32.pdb" (misc_record) = (null) - (debug_filename) = "gdi32.pdb" + (debug_file) = "gdi32.pdb" + (debug_identifier) = "C0EA66BE00A64BD7AEF79E443A91869C2" + (version) = "5.1.2600.2818" module[7] MDRawModule @@ -403,13 +424,16 @@ MDRawModule cv_record.rva = 0x1427 misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\user32.dll" + (code_file) = "C:\WINDOWS\system32\user32.dll" + (code_identifier) = "4226015990000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = ee2b714d-83a3-4c9d-8802-7621272f8326 (cv_record).age = 2 (cv_record).pdb_file_name = "user32.pdb" (misc_record) = (null) - (debug_filename) = "user32.pdb" + (debug_file) = "user32.pdb" + (debug_identifier) = "EE2B714D83A34C9D88027621272F83262" + (version) = "5.1.2600.2622" module[8] MDRawModule @@ -432,13 +456,16 @@ MDRawModule cv_record.rva = 0x144a misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\msvcrt.dll" + (code_file) = "C:\WINDOWS\system32\msvcrt.dll" + (code_identifier) = "4110975258000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = a678f3c3-0ded-426b-8390-32b996987e38 (cv_record).age = 1 (cv_record).pdb_file_name = "msvcrt.pdb" (misc_record) = (null) - (debug_filename) = "msvcrt.pdb" + (debug_file) = "msvcrt.pdb" + (debug_identifier) = "A678F3C30DED426B839032B996987E381" + (version) = "7.0.2600.2180" module[9] MDRawModule @@ -461,13 +488,16 @@ MDRawModule cv_record.rva = 0x146d misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\imm32.dll" + (code_file) = "C:\WINDOWS\system32\imm32.dll" + (code_identifier) = "411096ae1d000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = 2c17a49c-251b-4c8e-b9e2-ad13d7d9ea16 (cv_record).age = 2 (cv_record).pdb_file_name = "imm32.pdb" (misc_record) = (null) - (debug_filename) = "imm32.pdb" + (debug_file) = "imm32.pdb" + (debug_identifier) = "2C17A49C251B4C8EB9E2AD13D7D9EA162" + (version) = "5.1.2600.2180" module[10] MDRawModule @@ -490,13 +520,16 @@ MDRawModule cv_record.rva = 0x148f misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\dbghelp.dll" + (code_file) = "C:\WINDOWS\system32\dbghelp.dll" + (code_identifier) = "4110969aa1000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = 39559573-e21b-46f2-8e28-6923be9e6a76 (cv_record).age = 1 (cv_record).pdb_file_name = "dbghelp.pdb" (misc_record) = (null) - (debug_filename) = "dbghelp.pdb" + (debug_file) = "dbghelp.pdb" + (debug_identifier) = "39559573E21B46F28E286923BE9E6A761" + (version) = "5.1.2600.2180" module[11] MDRawModule @@ -519,13 +552,16 @@ MDRawModule cv_record.rva = 0x14b3 misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\version.dll" + (code_file) = "C:\WINDOWS\system32\version.dll" + (code_identifier) = "411096b78000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = 180a90c4-0384-463e-82dd-c45b2c8ab76e (cv_record).age = 2 (cv_record).pdb_file_name = "version.pdb" (misc_record) = (null) - (debug_filename) = "version.pdb" + (debug_file) = "version.pdb" + (debug_identifier) = "180A90C40384463E82DDC45B2C8AB76E2" + (version) = "5.1.2600.2180" module[12] MDRawModule @@ -548,13 +584,16 @@ MDRawModule cv_record.rva = 0x14d7 misc_record.data_size = 0 misc_record.rva = 0x0 - (module_name) = "C:\WINDOWS\system32\psapi.dll" + (code_file) = "C:\WINDOWS\system32\psapi.dll" + (code_identifier) = "411096cab000" (cv_record).cv_signature = 0x53445352 (cv_record).signature = a5c3a1f9-689f-43d8-ad22-8a0929388970 (cv_record).age = 2 (cv_record).pdb_file_name = "psapi.pdb" (misc_record) = (null) - (debug_filename) = "psapi.pdb" + (debug_file) = "psapi.pdb" + (debug_identifier) = "A5C3A1F9689F43D8AD228A09293889702" + (version) = "5.1.2600.2180" MinidumpMemoryList region_count = 3 diff --git a/src/processor/testdata/minidump2.stackwalk.out b/src/processor/testdata/minidump2.stackwalk.out index 82c42d73..c8fa3b8d 100644 --- a/src/processor/testdata/minidump2.stackwalk.out +++ b/src/processor/testdata/minidump2.stackwalk.out @@ -17,3 +17,18 @@ Thread 0 (crashed) eip = 0x0040395c esp = 0x0012ff78 ebp = 0x0012ffc0 3 kernel32.dll!BaseProcessStart + 0x22 eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0 + +Loaded modules: +0x00400000 - 0x0042afff test_app.exe ??? (main) +0x59a60000 - 0x59b00fff dbghelp.dll 5.1.2600.2180 +0x76390000 - 0x763acfff imm32.dll 5.1.2600.2180 +0x76bf0000 - 0x76bfafff psapi.dll 5.1.2600.2180 +0x774e0000 - 0x7761cfff ole32.dll 5.1.2600.2726 +0x77c00000 - 0x77c07fff version.dll 5.1.2600.2180 +0x77c10000 - 0x77c67fff msvcrt.dll 7.0.2600.2180 +0x77d40000 - 0x77dcffff user32.dll 5.1.2600.2622 +0x77dd0000 - 0x77e6afff advapi32.dll 5.1.2600.2180 +0x77e70000 - 0x77f00fff rpcrt4.dll 5.1.2600.2180 +0x77f10000 - 0x77f56fff gdi32.dll 5.1.2600.2818 +0x7c800000 - 0x7c8f3fff kernel32.dll 5.1.2600.2945 +0x7c900000 - 0x7c9affff ntdll.dll 5.1.2600.2180