Better testing for Stackwalker (#18). r=bryner

- Test StackwalkerX86 and StackwalkerPPC on the current process, if built
   by a supported compiler (gcc) on a supported (walkable) CPU (x86, ppc).
 - This test is not enabled by default because of certain optimizations
   that interfere with it (stack frame reuse, frame pointer omission).  See
   the comments at the top of stackwalker_selftest.cc.  To enable this
   test in the standard "make check" suite, configure with --enable-selftest.

http://groups.google.com/group/airbag-dev/browse_thread/thread/2ca1c8e72c809bec


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@35 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
mmentovai 2006-09-25 21:16:15 +00:00
parent 960e5277ee
commit 0dbedc973d
5 changed files with 394 additions and 27 deletions

View file

@ -89,9 +89,16 @@ check_PROGRAMS = \
src/processor/postfix_evaluator_unittest \
src/processor/range_map_unittest \
src/processor/source_line_resolver_unittest
if SELFTEST
check_PROGRAMS += \
src/processor/stackwalker_selftest
endif SELFTEST
check_SCRIPTS = \
src/processor/minidump_dump_test \
src/processor/minidump_stackwalk_test
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
TESTS_ENVIRONMENT =
@ -119,6 +126,15 @@ src_processor_source_line_resolver_unittest_SOURCES = \
src_processor_source_line_resolver_unittest_LDADD = \
src/processor/source_line_resolver.lo
src_processor_stackwalker_selftest_SOURCES = \
src/processor/stackwalker_selftest.cc
src_processor_stackwalker_selftest_LDADD = \
src/processor/minidump.lo \
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_x86.lo
## Non-installables
noinst_PROGRAMS =
noinst_SCRIPTS = $(check_SCRIPTS)

View file

@ -74,7 +74,11 @@ check_PROGRAMS = src/processor/contained_range_map_unittest$(EXEEXT) \
src/processor/minidump_processor_unittest$(EXEEXT) \
src/processor/postfix_evaluator_unittest$(EXEEXT) \
src/processor/range_map_unittest$(EXEEXT) \
src/processor/source_line_resolver_unittest$(EXEEXT)
src/processor/source_line_resolver_unittest$(EXEEXT) \
$(am__EXEEXT_1)
@SELFTEST_TRUE@am__append_1 = \
@SELFTEST_TRUE@ src/processor/stackwalker_selftest
noinst_PROGRAMS =
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
$(srcdir)/Makefile.am $(srcdir)/Makefile.in \
@ -111,6 +115,8 @@ am_src_libairbag_la_OBJECTS = src/processor/minidump.lo \
src/processor/stackwalker_x86.lo
src_libairbag_la_OBJECTS = $(am_src_libairbag_la_OBJECTS)
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
@SELFTEST_TRUE@am__EXEEXT_1 = \
@SELFTEST_TRUE@ src/processor/stackwalker_selftest$(EXEEXT)
PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
am_src_processor_contained_range_map_unittest_OBJECTS = \
src/processor/contained_range_map_unittest.$(OBJEXT)
@ -156,6 +162,15 @@ src_processor_source_line_resolver_unittest_OBJECTS = \
$(am_src_processor_source_line_resolver_unittest_OBJECTS)
src_processor_source_line_resolver_unittest_DEPENDENCIES = \
src/processor/source_line_resolver.lo
am_src_processor_stackwalker_selftest_OBJECTS = \
src/processor/stackwalker_selftest.$(OBJEXT)
src_processor_stackwalker_selftest_OBJECTS = \
$(am_src_processor_stackwalker_selftest_OBJECTS)
src_processor_stackwalker_selftest_DEPENDENCIES = \
src/processor/minidump.lo \
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_x86.lo
SCRIPTS = $(noinst_SCRIPTS)
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/src
depcomp = $(SHELL) $(top_srcdir)/autotools/depcomp
@ -183,7 +198,8 @@ SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_minidump_stackwalk_SOURCES) \
$(src_processor_postfix_evaluator_unittest_SOURCES) \
$(src_processor_range_map_unittest_SOURCES) \
$(src_processor_source_line_resolver_unittest_SOURCES)
$(src_processor_source_line_resolver_unittest_SOURCES) \
$(src_processor_stackwalker_selftest_SOURCES)
DIST_SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_contained_range_map_unittest_SOURCES) \
$(src_processor_minidump_dump_SOURCES) \
@ -191,7 +207,8 @@ DIST_SOURCES = $(src_libairbag_la_SOURCES) \
$(src_processor_minidump_stackwalk_SOURCES) \
$(src_processor_postfix_evaluator_unittest_SOURCES) \
$(src_processor_range_map_unittest_SOURCES) \
$(src_processor_source_line_resolver_unittest_SOURCES)
$(src_processor_source_line_resolver_unittest_SOURCES) \
$(src_processor_stackwalker_selftest_SOURCES)
dist_docDATA_INSTALL = $(INSTALL_DATA)
DATA = $(dist_doc_DATA)
ETAGS = etags
@ -258,6 +275,8 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SELFTEST_FALSE = @SELFTEST_FALSE@
SELFTEST_TRUE = @SELFTEST_TRUE@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
@ -379,6 +398,16 @@ src_processor_source_line_resolver_unittest_SOURCES = \
src_processor_source_line_resolver_unittest_LDADD = \
src/processor/source_line_resolver.lo
src_processor_stackwalker_selftest_SOURCES = \
src/processor/stackwalker_selftest.cc
src_processor_stackwalker_selftest_LDADD = \
src/processor/minidump.lo \
src/processor/source_line_resolver.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_x86.lo
noinst_SCRIPTS = $(check_SCRIPTS)
src_processor_minidump_dump_SOURCES = \
src/processor/minidump_dump.cc
@ -594,6 +623,12 @@ src/processor/source_line_resolver_unittest.$(OBJEXT): \
src/processor/source_line_resolver_unittest$(EXEEXT): $(src_processor_source_line_resolver_unittest_OBJECTS) $(src_processor_source_line_resolver_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
@rm -f src/processor/source_line_resolver_unittest$(EXEEXT)
$(CXXLINK) $(src_processor_source_line_resolver_unittest_LDFLAGS) $(src_processor_source_line_resolver_unittest_OBJECTS) $(src_processor_source_line_resolver_unittest_LDADD) $(LIBS)
src/processor/stackwalker_selftest.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/stackwalker_selftest$(EXEEXT): $(src_processor_stackwalker_selftest_OBJECTS) $(src_processor_stackwalker_selftest_DEPENDENCIES) src/processor/$(am__dirstamp)
@rm -f src/processor/stackwalker_selftest$(EXEEXT)
$(CXXLINK) $(src_processor_stackwalker_selftest_LDFLAGS) $(src_processor_stackwalker_selftest_OBJECTS) $(src_processor_stackwalker_selftest_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@ -614,6 +649,7 @@ mostlyclean-compile:
-rm -f src/processor/stackwalker.lo
-rm -f src/processor/stackwalker_ppc.$(OBJEXT)
-rm -f src/processor/stackwalker_ppc.lo
-rm -f src/processor/stackwalker_selftest.$(OBJEXT)
-rm -f src/processor/stackwalker_x86.$(OBJEXT)
-rm -f src/processor/stackwalker_x86.lo
@ -632,6 +668,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver_unittest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_ppc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_selftest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_x86.Plo@am__quote@
.cc.o:

91
configure vendored
View file

@ -855,6 +855,8 @@ FFLAGS
ac_ct_F77
LIBTOOL
LIBTOOL_DEPS
SELFTEST_TRUE
SELFTEST_FALSE
LIBOBJS
LTLIBOBJS'
ac_subst_files=''
@ -1458,6 +1460,8 @@ Optional Features:
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
--enable-selftest Run extra tests with "make check" (may conflict with
optimizations) (default is no)
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@ -5150,7 +5154,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 5153 "configure"' > conftest.$ac_ext
echo '#line 5157 "configure"' > conftest.$ac_ext
if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
(eval $ac_compile) 2>&5
ac_status=$?
@ -7315,11 +7319,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:7318: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7322: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7322: \$? = $ac_status" >&5
echo "$as_me:7326: \$? = $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.
@ -7583,11 +7587,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:7586: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7590: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7590: \$? = $ac_status" >&5
echo "$as_me:7594: \$? = $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.
@ -7687,11 +7691,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:7690: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7694: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7694: \$? = $ac_status" >&5
echo "$as_me:7698: \$? = $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
@ -10139,7 +10143,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
#line 10142 "configure"
#line 10146 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -10239,7 +10243,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<EOF
#line 10242 "configure"
#line 10246 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -12607,11 +12611,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:12610: $lt_compile\"" >&5)
(eval echo "\"\$as_me:12614: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:12614: \$? = $ac_status" >&5
echo "$as_me:12618: \$? = $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.
@ -12711,11 +12715,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:12714: $lt_compile\"" >&5)
(eval echo "\"\$as_me:12718: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:12718: \$? = $ac_status" >&5
echo "$as_me:12722: \$? = $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
@ -14281,11 +14285,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:14284: $lt_compile\"" >&5)
(eval echo "\"\$as_me:14288: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:14288: \$? = $ac_status" >&5
echo "$as_me:14292: \$? = $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.
@ -14385,11 +14389,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:14388: $lt_compile\"" >&5)
(eval echo "\"\$as_me:14392: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:14392: \$? = $ac_status" >&5
echo "$as_me:14396: \$? = $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
@ -16615,11 +16619,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:16618: $lt_compile\"" >&5)
(eval echo "\"\$as_me:16622: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:16622: \$? = $ac_status" >&5
echo "$as_me:16626: \$? = $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.
@ -16883,11 +16887,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:16886: $lt_compile\"" >&5)
(eval echo "\"\$as_me:16890: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:16890: \$? = $ac_status" >&5
echo "$as_me:16894: \$? = $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.
@ -16987,11 +16991,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:16990: $lt_compile\"" >&5)
(eval echo "\"\$as_me:16994: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:16994: \$? = $ac_status" >&5
echo "$as_me:16998: \$? = $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
@ -19875,6 +19879,36 @@ _ACEOF
fi
# Check whether --enable-selftest was given.
if test "${enable_selftest+set}" = set; then
enableval=$enable_selftest; case "${enableval}" in
yes)
selftest=true
;;
no)
selftest=false
;;
*)
{ { echo "$as_me:$LINENO: error: bad value ${enableval} for --enable-selftest" >&5
echo "$as_me: error: bad value ${enableval} for --enable-selftest" >&2;}
{ (exit 1); exit 1; }; }
;;
esac
else
selftest=false
fi
if test x$selftest = xtrue; then
SELFTEST_TRUE=
SELFTEST_FALSE='#'
else
SELFTEST_TRUE='#'
SELFTEST_FALSE=
fi
ac_config_files="$ac_config_files Makefile"
cat >confcache <<\_ACEOF
@ -19994,6 +20028,13 @@ echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined.
Usually this means the macro was only invoked conditionally." >&2;}
{ (exit 1); exit 1; }; }
fi
if test -z "${SELFTEST_TRUE}" && test -z "${SELFTEST_FALSE}"; then
{ { echo "$as_me:$LINENO: error: conditional \"SELFTEST\" was never defined.
Usually this means the macro was only invoked conditionally." >&5
echo "$as_me: error: conditional \"SELFTEST\" was never defined.
Usually this means the macro was only invoked conditionally." >&2;}
{ (exit 1); exit 1; }; }
fi
: ${CONFIG_STATUS=./config.status}
ac_clean_files_save=$ac_clean_files
@ -20654,11 +20695,13 @@ FFLAGS!$FFLAGS$ac_delim
ac_ct_F77!$ac_ct_F77$ac_delim
LIBTOOL!$LIBTOOL$ac_delim
LIBTOOL_DEPS!$LIBTOOL_DEPS$ac_delim
SELFTEST_TRUE!$SELFTEST_TRUE$ac_delim
SELFTEST_FALSE!$SELFTEST_FALSE$ac_delim
LIBOBJS!$LIBOBJS$ac_delim
LTLIBOBJS!$LTLIBOBJS$ac_delim
_ACEOF
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 6; then
if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 8; then
break
elif $ac_last_try; then
{ { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5

View file

@ -47,5 +47,24 @@ AC_SUBST(LIBTOOL_DEPS)
AC_HEADER_STDC
AC_ARG_ENABLE(selftest,
AS_HELP_STRING([--enable-selftest],
[Run extra tests with "make check" ]
[(may conflict with optimizations) ]
[(default is no)]),
[case "${enableval}" in
yes)
selftest=true
;;
no)
selftest=false
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --enable-selftest)
;;
esac],
[selftest=false])
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View file

@ -0,0 +1,252 @@
// Copyright (C) 2006 Google Inc.
//
// 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
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// 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.
// stackwalker_selftest.cc: Tests StackwalkerX86 or StackwalkerPPC using the
// running process' stack as test data, if running on an x86 or ppc and
// compiled with gcc. This test is not enabled in the "make check" suite
// by default, because certain optimizations interfere with its proper
// operation. To turn it on, configure with --enable-selftest.
//
// Optimizations that cause problems:
// - stack frame reuse. The Recursor function here calls itself with
// |return Recursor|. When the caller's frame is reused, it will cause
// CountCallerFrames to correctly return the same number of frames
// in both the caller and callee. This is considered an unexpected
// condition in the test, which expects a callee to have one more
// caller frame in the stack than its caller.
// - frame pointer omission. Even with a stackwalker that understands
// this optimization, the code to harness debug information currently
// only exists to retrieve it from minidumps, not the current process.
//
// This test can also serve as a developmental and debugging aid if
// PRINT_STACKS is defined.
//
// Author: Mark Mentovai
#if defined(__GNUC__) && (defined(__i386__) || defined(__ppc__))
#include <cstdio>
#include "google/airbag_types.h"
#include "google/stack_frame.h"
#include "processor/memory_region.h"
#include "processor/minidump_format.h"
using google_airbag::MemoryRegion;
using google_airbag::StackFrame;
using google_airbag::StackFrames;
#if defined(__i386__)
#include "processor/stackwalker_x86.h"
using google_airbag::StackwalkerX86;
#elif defined(__ppc__)
#include "processor/stackwalker_ppc.h"
using google_airbag::StackwalkerPPC;
#endif // __i386__ || __ppc__
#define RECURSION_DEPTH 100
// A simple MemoryRegion subclass that provides direct access to this
// process' memory space by pointer.
class SelfMemoryRegion : public MemoryRegion {
public:
virtual u_int64_t GetBase() { return 0; }
virtual u_int32_t GetSize() { return 0xffffffff; }
bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) {
return GetMemoryAtAddressInternal(address, value); }
bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) {
return GetMemoryAtAddressInternal(address, value); }
bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) {
return GetMemoryAtAddressInternal(address, value); }
bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) {
return GetMemoryAtAddressInternal(address, value); }
private:
template<typename T> bool GetMemoryAtAddressInternal(u_int64_t address,
T* value) {
u_int8_t* memory = 0;
*value = *reinterpret_cast<const T*>(&memory[address]);
return true;
}
};
#if defined(__i386__)
// GetEBP returns the current value of the %ebp register. Because it's
// implemented as a function, %ebp itself contains GetEBP's frame pointer
// and not the caller's frame pointer. Dereference %ebp to obtain the
// caller's frame pointer, which the compiler-generated preamble stored
// on the stack (provided frame pointers are not being omitted.) Because
// this function depends on the compiler-generated preamble, inlining is
// disabled.
static u_int32_t GetEBP() __attribute__((noinline));
static u_int32_t GetEBP() {
u_int32_t ebp;
__asm__ __volatile__(
"movl (%%ebp), %0"
: "=a" (ebp)
);
return ebp;
}
// GetEIP returns the instruction pointer identifying the next instruction
// to execute after GetEIP returns. It obtains this information from the
// stack, where it was placed by the call instruction that called GetEIP.
// This function depends on frame pointers not being omitted. It is possible
// to write a pure asm version of this routine that has no compiler-generated
// preamble and uses %esp instead of %ebp; that would function in the
// absence of frame pointers. However, the simpler approach is used here
// because GetEBP and stackwalking necessarily depends on access to frame
// pointers. Because this function depends on a call instruction and the
// compiler-generated preamble, inlining is disabled.
static u_int32_t GetEIP() __attribute__((noinline));
static u_int32_t GetEIP() {
u_int32_t eip;
__asm__ __volatile__(
"movl 4(%%ebp), %0"
: "=a" (eip)
);
return eip;
}
#elif defined(__ppc__)
// GetSP returns the current value of the %r1 register, which by convention,
// is the stack pointer on ppc. Because it's implemented as a function,
// %r1 itself contains GetSP's own stack pointer and not the caller's stack
// pointer. Dereference %r1 to obtain the caller's stack pointer, which the
// compiler-generated prolog stored on the stack. Because this function
// depends on the compiler-generated prolog, inlining is disabled.
static u_int32_t GetSP() __attribute__((noinline));
static u_int32_t GetSP() {
u_int32_t sp;
__asm__ __volatile__(
"lwz %0, 0(r1)"
: "=r" (sp)
);
return sp;
}
// GetPC returns the program counter identifying the next instruction to
// execute after GetPC returns. It obtains this information from the
// link register, where it was placed by the branch instruction that called
// GetPC. Because this function depends on the caller's use of a branch
// instruction, inlining is disabled.
static u_int32_t GetPC() __attribute__((noinline));
static u_int32_t GetPC() {
u_int32_t lr;
__asm__ __volatile__(
"mflr %0"
: "=r" (lr)
);
return lr;
}
#endif // __i386__ || __ppc__
// CountCallerFrames returns the number of stack frames beneath the function
// that called CountCallerFrames. Because this function's return value
// is dependent on the size of the stack beneath it, inlining is disabled,
// and any function that calls this should not be inlined either.
static unsigned int CountCallerFrames() __attribute__((noinline));
static unsigned int CountCallerFrames() {
SelfMemoryRegion memory;
#if defined(__i386__)
MDRawContextX86 context = MDRawContextX86();
context.eip = GetEIP();
context.ebp = GetEBP();
StackwalkerX86 stackwalker = StackwalkerX86(&context, &memory, NULL, NULL);
#elif defined(__ppc__)
MDRawContextPPC context = MDRawContextPPC();
context.srr0 = GetPC();
context.gpr[1] = GetSP();
StackwalkerPPC stackwalker = StackwalkerPPC(&context, &memory, NULL, NULL);
#endif // __i386__ || __ppc__
StackFrames stack;
stackwalker.Walk(&stack);
#ifdef PRINT_STACKS
printf("\n");
for(unsigned int frame_index = 0;
frame_index < stack.size();
++frame_index) {
StackFrame *frame = &stack[frame_index];
printf("frame %-3d instruction = 0x%08llx frame_pointer = 0x%08llx\n",
frame_index, frame->instruction, frame->frame_pointer);
}
#endif // PRINT_STACKS
// Subtract 1 because the caller wants the number of frames beneath
// itself. Because the caller called us, subract two for our frame and its
// frame, which are included in stack->size().
return stack.size() - 2;
}
// Recursor verifies that the number stack frames beneath itself is one more
// than the number of stack frames beneath its parent. When depth frames
// have been reached, Recursor stops checking and returns success. If the
// frame count check fails at any depth, Recursor will stop and return false.
// Because this calls CountCallerFrames, inlining is disabled.
static bool Recursor(unsigned int depth, unsigned int parent_callers)
__attribute__((noinline));
static bool Recursor(unsigned int depth, unsigned int parent_callers) {
unsigned int callers = CountCallerFrames();
if (callers != parent_callers + 1)
return false;
if (depth)
return Recursor(depth - 1, callers);
// depth == 0
return true;
}
// Because this calls CountCallerFrames, inlining is disabled - but because
// it's main (and nobody calls it other than the entry point), it wouldn't
// be inlined anyway.
int main(int argc, char** argv) __attribute__((noinline));
int main(int argc, char** argv) {
return Recursor(RECURSION_DEPTH, CountCallerFrames()) ? 0 : 1;
}
#else // __GNUC__ && (__i386__ || __ppc__)
// Not gcc? We use gcc's __asm__.
// Not i386 or ppc? We can only test stacks we know how to walk.
int main(int argc, char **argv) {
// "make check" interprets an exit status of 77 to mean that the test is
// not supported.
return 77;
}
#endif // __GNUC__ && (__i386__ || __ppc__)