Process minidumps generated on ARM64 in iOS apps.
Patch by Colin Blundell <blundell@chromium.org> BUG=542 Review URL: https://breakpad.appspot.com/704002/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1236 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
77022ac0df
commit
e9165f4353
17 changed files with 5388 additions and 2643 deletions
28
Makefile.am
28
Makefile.am
|
@ -187,12 +187,15 @@ src_libbreakpad_a_SOURCES = \
|
|||
src/processor/windows_frame_info.h \
|
||||
src/processor/source_line_resolver_base_types.h \
|
||||
src/processor/source_line_resolver_base.cc \
|
||||
src/processor/stack_frame_cpu.cc \
|
||||
src/processor/stack_frame_symbolizer.cc \
|
||||
src/processor/stackwalker.cc \
|
||||
src/processor/stackwalker_amd64.cc \
|
||||
src/processor/stackwalker_amd64.h \
|
||||
src/processor/stackwalker_arm.cc \
|
||||
src/processor/stackwalker_arm.h \
|
||||
src/processor/stackwalker_arm64.cc \
|
||||
src/processor/stackwalker_arm64.h \
|
||||
src/processor/stackwalker_address_list.cc \
|
||||
src/processor/stackwalker_address_list.h \
|
||||
src/processor/stackwalker_mips.cc \
|
||||
|
@ -293,6 +296,7 @@ check_PROGRAMS += \
|
|||
src/processor/range_map_unittest \
|
||||
src/processor/stackwalker_amd64_unittest \
|
||||
src/processor/stackwalker_arm_unittest \
|
||||
src/processor/stackwalker_arm64_unittest \
|
||||
src/processor/stackwalker_address_list_unittest \
|
||||
src/processor/stackwalker_mips_unittest \
|
||||
src/processor/stackwalker_x86_unittest \
|
||||
|
@ -636,10 +640,12 @@ src_processor_exploitability_unittest_LDADD = \
|
|||
src/processor/pathname_stripper.o \
|
||||
src/processor/simple_symbol_supplier.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
|
@ -728,10 +734,12 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
src/processor/pathname_stripper.o \
|
||||
src/processor/process_state.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
|
@ -856,10 +864,12 @@ src_processor_stackwalker_selftest_LDADD = \
|
|||
src/processor/minidump.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
|
@ -901,6 +911,22 @@ src_processor_stackwalker_arm_unittest_CPPFLAGS = \
|
|||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
src_processor_stackwalker_arm64_unittest_SOURCES = \
|
||||
src/common/test_assembler.cc \
|
||||
src/processor/stackwalker_arm64_unittest.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_processor_stackwalker_arm64_unittest_LDADD = \
|
||||
src/libbreakpad.a \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
src_processor_stackwalker_arm64_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
src_processor_stackwalker_address_list_unittest_SOURCES = \
|
||||
src/common/test_assembler.cc \
|
||||
src/processor/stackwalker_address_list_unittest.cc \
|
||||
|
@ -1012,10 +1038,12 @@ src_processor_minidump_stackwalk_LDADD = \
|
|||
src/processor/process_state.o \
|
||||
src/processor/simple_symbol_supplier.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
|
|
4031
Makefile.in
4031
Makefile.in
File diff suppressed because it is too large
Load diff
735
aclocal.m4
vendored
735
aclocal.m4
vendored
File diff suppressed because it is too large
Load diff
|
@ -1 +0,0 @@
|
|||
/usr/share/automake-1.11/compile
|
347
autotools/compile
Executable file
347
autotools/compile
Executable file
|
@ -0,0 +1,347 @@
|
|||
#! /bin/sh
|
||||
# Wrapper for compilers which do not understand '-c -o'.
|
||||
|
||||
scriptversion=2012-10-14.11; # UTC
|
||||
|
||||
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
nl='
|
||||
'
|
||||
|
||||
# We need space, tab and new line, in precisely that order. Quoting is
|
||||
# there to prevent tools from complaining about whitespace usage.
|
||||
IFS=" "" $nl"
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file lazy
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts. If the determined conversion
|
||||
# type is listed in (the comma separated) LAZY, no conversion will
|
||||
# take place.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv/,$2, in
|
||||
*,$file_conv,*)
|
||||
;;
|
||||
mingw/*)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin/*)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine/*)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_cl_dashL linkdir
|
||||
# Make cl look for libraries in LINKDIR
|
||||
func_cl_dashL ()
|
||||
{
|
||||
func_file_conv "$1"
|
||||
if test -z "$lib_path"; then
|
||||
lib_path=$file
|
||||
else
|
||||
lib_path="$lib_path;$file"
|
||||
fi
|
||||
linker_opts="$linker_opts -LIBPATH:$file"
|
||||
}
|
||||
|
||||
# func_cl_dashl library
|
||||
# Do a library search-path lookup for cl
|
||||
func_cl_dashl ()
|
||||
{
|
||||
lib=$1
|
||||
found=no
|
||||
save_IFS=$IFS
|
||||
IFS=';'
|
||||
for dir in $lib_path $LIB
|
||||
do
|
||||
IFS=$save_IFS
|
||||
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.dll.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/$lib.lib"; then
|
||||
found=yes
|
||||
lib=$dir/$lib.lib
|
||||
break
|
||||
fi
|
||||
if test -f "$dir/lib$lib.a"; then
|
||||
found=yes
|
||||
lib=$dir/lib$lib.a
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=$save_IFS
|
||||
|
||||
if test "$found" != yes; then
|
||||
lib=$lib.lib
|
||||
fi
|
||||
}
|
||||
|
||||
# func_cl_wrapper cl arg...
|
||||
# Adjust compile command to suit cl
|
||||
func_cl_wrapper ()
|
||||
{
|
||||
# Assume a capable shell
|
||||
lib_path=
|
||||
shared=:
|
||||
linker_opts=
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.[oO][bB][jJ])
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fo"$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$2"
|
||||
set x "$@" -Fe"$file"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
-I)
|
||||
eat=1
|
||||
func_file_conv "$2" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-I*)
|
||||
func_file_conv "${1#-I}" mingw
|
||||
set x "$@" -I"$file"
|
||||
shift
|
||||
;;
|
||||
-l)
|
||||
eat=1
|
||||
func_cl_dashl "$2"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-l*)
|
||||
func_cl_dashl "${1#-l}"
|
||||
set x "$@" "$lib"
|
||||
shift
|
||||
;;
|
||||
-L)
|
||||
eat=1
|
||||
func_cl_dashL "$2"
|
||||
;;
|
||||
-L*)
|
||||
func_cl_dashL "${1#-L}"
|
||||
;;
|
||||
-static)
|
||||
shared=false
|
||||
;;
|
||||
-Wl,*)
|
||||
arg=${1#-Wl,}
|
||||
save_ifs="$IFS"; IFS=','
|
||||
for flag in $arg; do
|
||||
IFS="$save_ifs"
|
||||
linker_opts="$linker_opts $flag"
|
||||
done
|
||||
IFS="$save_ifs"
|
||||
;;
|
||||
-Xlinker)
|
||||
eat=1
|
||||
linker_opts="$linker_opts $2"
|
||||
;;
|
||||
-*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||
func_file_conv "$1"
|
||||
set x "$@" -Tp"$file"
|
||||
shift
|
||||
;;
|
||||
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||
func_file_conv "$1" mingw
|
||||
set x "$@" "$file"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
if test -n "$linker_opts"; then
|
||||
linker_opts="-link$linker_opts"
|
||||
fi
|
||||
exec "$@" $linker_opts
|
||||
exit 1
|
||||
}
|
||||
|
||||
eat=
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||
exit 1;
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<\EOF
|
||||
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||
|
||||
Wrapper for compilers which do not understand '-c -o'.
|
||||
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||
arguments, and rename the output as expected.
|
||||
|
||||
If you are trying to build a whole package this is not the
|
||||
right script to run: please start by reading the file 'INSTALL'.
|
||||
|
||||
Report bugs to <bug-automake@gnu.org>.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "compile $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||
func_cl_wrapper "$@" # Doesn't return...
|
||||
;;
|
||||
esac
|
||||
|
||||
ofile=
|
||||
cfile=
|
||||
|
||||
for arg
|
||||
do
|
||||
if test -n "$eat"; then
|
||||
eat=
|
||||
else
|
||||
case $1 in
|
||||
-o)
|
||||
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||
# So we strip '-o arg' only if arg is an object.
|
||||
eat=1
|
||||
case $2 in
|
||||
*.o | *.obj)
|
||||
ofile=$2
|
||||
;;
|
||||
*)
|
||||
set x "$@" -o "$2"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*.c)
|
||||
cfile=$1
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
set x "$@" "$1"
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
shift
|
||||
done
|
||||
|
||||
if test -z "$ofile" || test -z "$cfile"; then
|
||||
# If no '-o' option was seen then we might have been invoked from a
|
||||
# pattern rule where we don't need one. That is ok -- this is a
|
||||
# normal compilation that the losing compiler can handle. If no
|
||||
# '.c' file was seen then we are probably linking. That is also
|
||||
# ok.
|
||||
exec "$@"
|
||||
fi
|
||||
|
||||
# Name of file we expect compiler to create.
|
||||
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||
|
||||
# Create the lock directory.
|
||||
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||
# that we are using for the .o file. Also, base the name on the expected
|
||||
# object file name, since that is what matters with a parallel build.
|
||||
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||
while true; do
|
||||
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||
break
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
# FIXME: race condition here if user kills between mkdir and trap.
|
||||
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||
|
||||
# Run the compile.
|
||||
"$@"
|
||||
ret=$?
|
||||
|
||||
if test -f "$cofile"; then
|
||||
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||
elif test -f "${cofile}bj"; then
|
||||
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||
fi
|
||||
|
||||
rmdir "$lockdir"
|
||||
exit $ret
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
127
autotools/test-driver
Executable file
127
autotools/test-driver
Executable file
|
@ -0,0 +1,127 @@
|
|||
#! /bin/sh
|
||||
# test-driver - basic testsuite driver script.
|
||||
|
||||
scriptversion=2012-06-27.10; # UTC
|
||||
|
||||
# Copyright (C) 2011-2013 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
# Make unconditional expansion of undefined variables an error. This
|
||||
# helps a lot in preventing typo-related bugs.
|
||||
set -u
|
||||
|
||||
usage_error ()
|
||||
{
|
||||
echo "$0: $*" >&2
|
||||
print_usage >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
print_usage ()
|
||||
{
|
||||
cat <<END
|
||||
Usage:
|
||||
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||
[--enable-hard-errors={yes|no}] [--] TEST-SCRIPT
|
||||
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||
END
|
||||
}
|
||||
|
||||
# TODO: better error handling in option parsing (in particular, ensure
|
||||
# TODO: $log_file, $trs_file and $test_name are defined).
|
||||
test_name= # Used for reporting.
|
||||
log_file= # Where to save the output of the test script.
|
||||
trs_file= # Where to save the metadata of the test run.
|
||||
expect_failure=no
|
||||
color_tests=no
|
||||
enable_hard_errors=yes
|
||||
while test $# -gt 0; do
|
||||
case $1 in
|
||||
--help) print_usage; exit $?;;
|
||||
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||
--test-name) test_name=$2; shift;;
|
||||
--log-file) log_file=$2; shift;;
|
||||
--trs-file) trs_file=$2; shift;;
|
||||
--color-tests) color_tests=$2; shift;;
|
||||
--expect-failure) expect_failure=$2; shift;;
|
||||
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||
--) shift; break;;
|
||||
-*) usage_error "invalid option: '$1'";;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if test $color_tests = yes; then
|
||||
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||
red='[0;31m' # Red.
|
||||
grn='[0;32m' # Green.
|
||||
lgn='[1;32m' # Light green.
|
||||
blu='[1;34m' # Blue.
|
||||
mgn='[0;35m' # Magenta.
|
||||
std='[m' # No color.
|
||||
else
|
||||
red= grn= lgn= blu= mgn= std=
|
||||
fi
|
||||
|
||||
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||
trap "st=129; $do_exit" 1
|
||||
trap "st=130; $do_exit" 2
|
||||
trap "st=141; $do_exit" 13
|
||||
trap "st=143; $do_exit" 15
|
||||
|
||||
# Test script is run here.
|
||||
"$@" >$log_file 2>&1
|
||||
estatus=$?
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
estatus=1
|
||||
fi
|
||||
|
||||
case $estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
# Register the test result, and other relevant metadata.
|
||||
echo ":test-result: $res" > $trs_file
|
||||
echo ":global-test-result: $res" >> $trs_file
|
||||
echo ":recheck: $recheck" >> $trs_file
|
||||
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||
|
||||
# Local Variables:
|
||||
# mode: shell-script
|
||||
# sh-indentation: 2
|
||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||
# time-stamp-start: "scriptversion="
|
||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||
# time-stamp-time-zone: "UTC"
|
||||
# time-stamp-end: "; # UTC"
|
||||
# End:
|
|
@ -33,9 +33,6 @@
|
|||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
|
||||
#undef NO_MINUS_C_MINUS_O
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
|
|
|
@ -187,6 +187,7 @@ class MinidumpContext : public MinidumpStream {
|
|||
// NULL.
|
||||
const MDRawContextAMD64* GetContextAMD64() const;
|
||||
const MDRawContextARM* GetContextARM() const;
|
||||
const MDRawContextARM64* GetContextARM64() const;
|
||||
const MDRawContextMIPS* GetContextMIPS() const;
|
||||
const MDRawContextPPC* GetContextPPC() const;
|
||||
const MDRawContextPPC64* GetContextPPC64() const;
|
||||
|
@ -210,6 +211,7 @@ class MinidumpContext : public MinidumpStream {
|
|||
// so variables can NOT be named as sparc
|
||||
MDRawContextSPARC* ctx_sparc;
|
||||
MDRawContextARM* arm;
|
||||
MDRawContextARM64* arm64;
|
||||
MDRawContextMIPS* ctx_mips;
|
||||
} context_;
|
||||
|
||||
|
|
|
@ -270,6 +270,70 @@ struct StackFrameARM : public StackFrame {
|
|||
int context_validity;
|
||||
};
|
||||
|
||||
struct StackFrameARM64 : public StackFrame {
|
||||
// A flag for each register we might know. Note that we can't use an enum
|
||||
// here as there are 33 values to represent.
|
||||
static const uint64_t CONTEXT_VALID_NONE = 0;
|
||||
static const uint64_t CONTEXT_VALID_X0 = 1ULL << 0;
|
||||
static const uint64_t CONTEXT_VALID_X1 = 1ULL << 1;
|
||||
static const uint64_t CONTEXT_VALID_X2 = 1ULL << 2;
|
||||
static const uint64_t CONTEXT_VALID_X3 = 1ULL << 3;
|
||||
static const uint64_t CONTEXT_VALID_X4 = 1ULL << 4;
|
||||
static const uint64_t CONTEXT_VALID_X5 = 1ULL << 5;
|
||||
static const uint64_t CONTEXT_VALID_X6 = 1ULL << 6;
|
||||
static const uint64_t CONTEXT_VALID_X7 = 1ULL << 7;
|
||||
static const uint64_t CONTEXT_VALID_X8 = 1ULL << 8;
|
||||
static const uint64_t CONTEXT_VALID_X9 = 1ULL << 9;
|
||||
static const uint64_t CONTEXT_VALID_X10 = 1ULL << 10;
|
||||
static const uint64_t CONTEXT_VALID_X11 = 1ULL << 11;
|
||||
static const uint64_t CONTEXT_VALID_X12 = 1ULL << 12;
|
||||
static const uint64_t CONTEXT_VALID_X13 = 1ULL << 13;
|
||||
static const uint64_t CONTEXT_VALID_X14 = 1ULL << 14;
|
||||
static const uint64_t CONTEXT_VALID_X15 = 1ULL << 15;
|
||||
static const uint64_t CONTEXT_VALID_X16 = 1ULL << 16;
|
||||
static const uint64_t CONTEXT_VALID_X17 = 1ULL << 17;
|
||||
static const uint64_t CONTEXT_VALID_X18 = 1ULL << 18;
|
||||
static const uint64_t CONTEXT_VALID_X19 = 1ULL << 19;
|
||||
static const uint64_t CONTEXT_VALID_X20 = 1ULL << 20;
|
||||
static const uint64_t CONTEXT_VALID_X21 = 1ULL << 21;
|
||||
static const uint64_t CONTEXT_VALID_X22 = 1ULL << 22;
|
||||
static const uint64_t CONTEXT_VALID_X23 = 1ULL << 23;
|
||||
static const uint64_t CONTEXT_VALID_X24 = 1ULL << 24;
|
||||
static const uint64_t CONTEXT_VALID_X25 = 1ULL << 25;
|
||||
static const uint64_t CONTEXT_VALID_X26 = 1ULL << 26;
|
||||
static const uint64_t CONTEXT_VALID_X27 = 1ULL << 27;
|
||||
static const uint64_t CONTEXT_VALID_X28 = 1ULL << 28;
|
||||
static const uint64_t CONTEXT_VALID_X29 = 1ULL << 29;
|
||||
static const uint64_t CONTEXT_VALID_X30 = 1ULL << 30;
|
||||
static const uint64_t CONTEXT_VALID_X31 = 1ULL << 31;
|
||||
static const uint64_t CONTEXT_VALID_X32 = 1ULL << 32;
|
||||
static const uint64_t CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE;
|
||||
|
||||
// Aliases for registers with dedicated or conventional roles.
|
||||
static const uint64_t CONTEXT_VALID_FP = CONTEXT_VALID_X29;
|
||||
static const uint64_t CONTEXT_VALID_LR = CONTEXT_VALID_X30;
|
||||
static const uint64_t CONTEXT_VALID_SP = CONTEXT_VALID_X31;
|
||||
static const uint64_t CONTEXT_VALID_PC = CONTEXT_VALID_X32;
|
||||
|
||||
StackFrameARM64() : context(),
|
||||
context_validity(CONTEXT_VALID_NONE) {}
|
||||
|
||||
// Return the validity flag for register xN.
|
||||
static uint64_t RegisterValidFlag(int n) {
|
||||
return 1ULL << n;
|
||||
}
|
||||
|
||||
// Register state. This is only fully valid for the topmost frame in a
|
||||
// stack. In other frames, the values of nonvolatile registers may be
|
||||
// present, given sufficient debugging information. Refer to
|
||||
// context_validity.
|
||||
MDRawContextARM64 context;
|
||||
|
||||
// For each register in context whose value has been recovered, we set
|
||||
// the corresponding CONTEXT_VALID_ bit in context_validity.
|
||||
uint64_t context_validity;
|
||||
};
|
||||
|
||||
struct StackFrameMIPS : public StackFrame {
|
||||
// MIPS callee save registers for o32 ABI (32bit registers) are:
|
||||
// 1. $s0-$s7,
|
||||
|
|
|
@ -73,6 +73,30 @@ using std::ifstream;
|
|||
using std::numeric_limits;
|
||||
using std::vector;
|
||||
|
||||
// Returns true iff |context_size| matches exactly one of the sizes of the
|
||||
// various MDRawContext* types.
|
||||
// TODO(blundell): This function can be removed once
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550 is fixed.
|
||||
static bool IsContextSizeUnique(uint32_t context_size) {
|
||||
int num_matching_contexts = 0;
|
||||
if (context_size == sizeof(MDRawContextX86))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextPPC))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextPPC64))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextAMD64))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextSPARC))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextARM))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextARM64))
|
||||
num_matching_contexts++;
|
||||
if (context_size == sizeof(MDRawContextMIPS))
|
||||
num_matching_contexts++;
|
||||
return num_matching_contexts == 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Swapping routines
|
||||
|
@ -361,6 +385,23 @@ MinidumpContext::~MinidumpContext() {
|
|||
bool MinidumpContext::Read(uint32_t expected_size) {
|
||||
valid_ = false;
|
||||
|
||||
// Certain raw context types are currently assumed to have unique sizes.
|
||||
if (!IsContextSizeUnique(sizeof(MDRawContextAMD64))) {
|
||||
BPLOG(ERROR) << "sizeof(MDRawContextAMD64) cannot match the size of any "
|
||||
<< "other raw context";
|
||||
return false;
|
||||
}
|
||||
if (!IsContextSizeUnique(sizeof(MDRawContextPPC64))) {
|
||||
BPLOG(ERROR) << "sizeof(MDRawContextPPC64) cannot match the size of any "
|
||||
<< "other raw context";
|
||||
return false;
|
||||
}
|
||||
if (!IsContextSizeUnique(sizeof(MDRawContextARM64))) {
|
||||
BPLOG(ERROR) << "sizeof(MDRawContextARM64) cannot match the size of any "
|
||||
<< "other raw context";
|
||||
return false;
|
||||
}
|
||||
|
||||
FreeContext();
|
||||
|
||||
// First, figure out what type of CPU this context structure is for.
|
||||
|
@ -390,9 +431,8 @@ bool MinidumpContext::Read(uint32_t expected_size) {
|
|||
}
|
||||
|
||||
if (cpu_type != MD_CONTEXT_AMD64) {
|
||||
// TODO: fall through to switch below?
|
||||
// need a Tell method to be able to SeekSet back to beginning
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=224
|
||||
// TODO: Fall through to switch below.
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
||||
BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
|
||||
return false;
|
||||
}
|
||||
|
@ -483,6 +523,21 @@ bool MinidumpContext::Read(uint32_t expected_size) {
|
|||
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
||||
scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64());
|
||||
|
||||
if (cpu_type == 0) {
|
||||
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
||||
context_ppc64->context_flags |= cpu_type;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_type != MD_CONTEXT_PPC64) {
|
||||
// TODO: Fall through to switch below.
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
||||
BPLOG(ERROR) << "MinidumpContext not actually ppc64 context";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the context_flags member, which has already been read, and
|
||||
// read the rest of the structure beginning with the first member
|
||||
|
@ -548,6 +603,83 @@ bool MinidumpContext::Read(uint32_t expected_size) {
|
|||
}
|
||||
|
||||
context_.ppc64 = context_ppc64.release();
|
||||
context_flags_ = context_flags;
|
||||
} else if (expected_size == sizeof(MDRawContextARM64)) {
|
||||
// |context_flags| of MDRawContextARM64 is 64 bits, but other MDRawContext
|
||||
// in the else case have 32 bits |context_flags|, so special case it here.
|
||||
uint64_t context_flags;
|
||||
|
||||
BPLOG(INFO) << "MinidumpContext: looks like ARM64 context";
|
||||
|
||||
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
|
||||
BPLOG(ERROR) << "MinidumpContext could not read context flags";
|
||||
return false;
|
||||
}
|
||||
if (minidump_->swap())
|
||||
Swap(&context_flags);
|
||||
|
||||
scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64());
|
||||
|
||||
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
||||
if (cpu_type == 0) {
|
||||
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
|
||||
context_arm64->context_flags |= cpu_type;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Failed to preserve the current stream position";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (cpu_type != MD_CONTEXT_ARM64) {
|
||||
// TODO: Fall through to switch below.
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=550
|
||||
BPLOG(ERROR) << "MinidumpContext not actually arm64 context";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set the context_flags member, which has already been read, and
|
||||
// read the rest of the structure beginning with the first member
|
||||
// after context_flags.
|
||||
context_arm64->context_flags = context_flags;
|
||||
|
||||
size_t flags_size = sizeof(context_arm64->context_flags);
|
||||
uint8_t* context_after_flags =
|
||||
reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size;
|
||||
if (!minidump_->ReadBytes(context_after_flags,
|
||||
sizeof(MDRawContextARM64) - flags_size)) {
|
||||
BPLOG(ERROR) << "MinidumpContext could not read arm64 context";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do this after reading the entire MDRawContext structure because
|
||||
// GetSystemInfo may seek minidump to a new position.
|
||||
if (!CheckAgainstSystemInfo(cpu_type)) {
|
||||
BPLOG(ERROR) << "MinidumpContext arm64 does not match system info";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (minidump_->swap()) {
|
||||
// context_arm64->context_flags was already swapped.
|
||||
for (unsigned int ireg_index = 0;
|
||||
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
|
||||
++ireg_index) {
|
||||
Swap(&context_arm64->iregs[ireg_index]);
|
||||
}
|
||||
Swap(&context_arm64->cpsr);
|
||||
Swap(&context_arm64->float_save.fpsr);
|
||||
Swap(&context_arm64->float_save.fpcr);
|
||||
for (unsigned int fpr_index = 0;
|
||||
fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
|
||||
++fpr_index) {
|
||||
// While ARM64 is bi-endian, iOS (currently the only platform
|
||||
// for which ARM64 support has been brought up) uses ARM64 exclusively
|
||||
// in little-endian mode.
|
||||
Normalize128(&context_arm64->float_save.regs[fpr_index], false);
|
||||
Swap(&context_arm64->float_save.regs[fpr_index]);
|
||||
}
|
||||
}
|
||||
context_.arm64 = context_arm64.release();
|
||||
context_flags_ = context_flags;
|
||||
} else {
|
||||
uint32_t context_flags;
|
||||
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
|
||||
|
@ -954,6 +1086,9 @@ bool MinidumpContext::GetInstructionPointer(uint64_t* ip) const {
|
|||
case MD_CONTEXT_ARM:
|
||||
*ip = context_.arm->iregs[MD_CONTEXT_ARM_REG_PC];
|
||||
break;
|
||||
case MD_CONTEXT_ARM64:
|
||||
*ip = context_.arm64->iregs[MD_CONTEXT_ARM64_REG_PC];
|
||||
break;
|
||||
case MD_CONTEXT_PPC:
|
||||
*ip = context_.ppc->srr0;
|
||||
break;
|
||||
|
@ -1033,6 +1168,15 @@ const MDRawContextARM* MinidumpContext::GetContextARM() const {
|
|||
return context_.arm;
|
||||
}
|
||||
|
||||
const MDRawContextARM64* MinidumpContext::GetContextARM64() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_ARM64) {
|
||||
BPLOG(ERROR) << "MinidumpContext cannot get arm64 context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return context_.arm64;
|
||||
}
|
||||
|
||||
const MDRawContextMIPS* MinidumpContext::GetContextMIPS() const {
|
||||
if (GetContextCPU() != MD_CONTEXT_MIPS) {
|
||||
BPLOG(ERROR) << "MinidumpContext cannot get MIPS context";
|
||||
|
@ -1068,6 +1212,10 @@ void MinidumpContext::FreeContext() {
|
|||
delete context_.arm;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
delete context_.arm64;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_MIPS:
|
||||
delete context_.ctx_mips;
|
||||
break;
|
||||
|
@ -1142,6 +1290,11 @@ bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
|
|||
return_value = true;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
|
||||
return_value = true;
|
||||
break;
|
||||
|
||||
case MD_CONTEXT_MIPS:
|
||||
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
|
||||
return_value = true;
|
||||
|
@ -1423,6 +1576,31 @@ void MinidumpContext::Print() {
|
|||
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_ARM64: {
|
||||
const MDRawContextARM64* context_arm64 = GetContextARM64();
|
||||
printf("MDRawContextARM64\n");
|
||||
printf(" context_flags = 0x%llx\n",
|
||||
context_arm64->context_flags);
|
||||
for (unsigned int ireg_index = 0;
|
||||
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
|
||||
++ireg_index) {
|
||||
printf(" iregs[%2d] = 0x%llx\n",
|
||||
ireg_index, context_arm64->iregs[ireg_index]);
|
||||
}
|
||||
printf(" cpsr = 0x%x\n", context_arm64->cpsr);
|
||||
printf(" float_save.fpsr = 0x%x\n", context_arm64->float_save.fpsr);
|
||||
printf(" float_save.fpcr = 0x%x\n", context_arm64->float_save.fpcr);
|
||||
|
||||
for (unsigned int freg_index = 0;
|
||||
freg_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
|
||||
++freg_index) {
|
||||
uint128_struct fp_value = context_arm64->float_save.regs[freg_index];
|
||||
printf(" float_save.regs[%2d] = 0x%llx%llx\n",
|
||||
freg_index, fp_value.high, fp_value.low);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_MIPS: {
|
||||
const MDRawContextMIPS* context_mips = GetContextMIPS();
|
||||
|
@ -3530,6 +3708,10 @@ string MinidumpSystemInfo::GetCPU() {
|
|||
cpu = "arm";
|
||||
break;
|
||||
|
||||
case MD_CPU_ARCHITECTURE_ARM64:
|
||||
cpu = "arm64";
|
||||
break;
|
||||
|
||||
default:
|
||||
BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
|
||||
HexString(system_info_.processor_architecture);
|
||||
|
@ -4265,6 +4447,9 @@ bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) {
|
|||
case MD_CPU_ARCHITECTURE_ARM:
|
||||
*context_cpu_flags = MD_CONTEXT_ARM;
|
||||
break;
|
||||
case MD_CPU_ARCHITECTURE_ARM64:
|
||||
*context_cpu_flags = MD_CONTEXT_ARM64;
|
||||
break;
|
||||
case MD_CPU_ARCHITECTURE_IA64:
|
||||
*context_cpu_flags = MD_CONTEXT_IA64;
|
||||
break;
|
||||
|
|
|
@ -505,6 +505,11 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
|
|||
break;
|
||||
}
|
||||
|
||||
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||
info->cpu = "arm64";
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CPU_ARCHITECTURE_MIPS: {
|
||||
info->cpu = "mips";
|
||||
break;
|
||||
|
@ -668,7 +673,9 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
|||
default:
|
||||
// arm and ppc overlap
|
||||
if (raw_system_info->processor_architecture ==
|
||||
MD_CPU_ARCHITECTURE_ARM) {
|
||||
MD_CPU_ARCHITECTURE_ARM ||
|
||||
raw_system_info->processor_architecture ==
|
||||
MD_CPU_ARCHITECTURE_ARM64) {
|
||||
switch (exception_flags) {
|
||||
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
||||
reason.append("EXC_ARM_DA_ALIGN");
|
||||
|
@ -708,7 +715,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
|||
case MD_EXCEPTION_MAC_BAD_INSTRUCTION:
|
||||
reason = "EXC_BAD_INSTRUCTION / ";
|
||||
switch (raw_system_info->processor_architecture) {
|
||||
case MD_CPU_ARCHITECTURE_ARM: {
|
||||
case MD_CPU_ARCHITECTURE_ARM:
|
||||
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||
switch (exception_flags) {
|
||||
case MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED:
|
||||
reason.append("EXC_ARM_UNDEFINED");
|
||||
|
@ -887,7 +895,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
|||
case MD_EXCEPTION_MAC_BREAKPOINT:
|
||||
reason = "EXC_BREAKPOINT / ";
|
||||
switch (raw_system_info->processor_architecture) {
|
||||
case MD_CPU_ARCHITECTURE_ARM: {
|
||||
case MD_CPU_ARCHITECTURE_ARM:
|
||||
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||
switch (exception_flags) {
|
||||
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
||||
reason.append("EXC_ARM_DA_ALIGN");
|
||||
|
|
|
@ -72,6 +72,7 @@ using google_breakpad::StackFrameSPARC;
|
|||
using google_breakpad::StackFrameX86;
|
||||
using google_breakpad::StackFrameAMD64;
|
||||
using google_breakpad::StackFrameARM;
|
||||
using google_breakpad::StackFrameARM64;
|
||||
using google_breakpad::StackFrameMIPS;
|
||||
|
||||
// Separator character for machine readable output.
|
||||
|
@ -272,6 +273,144 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
|||
sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
|
||||
sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
|
||||
} else if (cpu == "arm64") {
|
||||
const StackFrameARM64 *frame_arm64 =
|
||||
reinterpret_cast<const StackFrameARM64*>(frame);
|
||||
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
|
||||
sequence =
|
||||
PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
|
||||
sequence =
|
||||
PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
|
||||
sequence =
|
||||
PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
|
||||
sequence =
|
||||
PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
|
||||
sequence =
|
||||
PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
|
||||
sequence =
|
||||
PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
|
||||
sequence =
|
||||
PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
|
||||
sequence =
|
||||
PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
|
||||
sequence =
|
||||
PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
|
||||
sequence =
|
||||
PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
|
||||
sequence =
|
||||
PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
|
||||
sequence =
|
||||
PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
|
||||
sequence =
|
||||
PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
|
||||
sequence =
|
||||
PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
|
||||
sequence =
|
||||
PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
|
||||
sequence =
|
||||
PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
|
||||
sequence =
|
||||
PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
|
||||
sequence =
|
||||
PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
|
||||
sequence =
|
||||
PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
|
||||
sequence =
|
||||
PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
|
||||
sequence =
|
||||
PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
|
||||
sequence =
|
||||
PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
|
||||
sequence =
|
||||
PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
|
||||
sequence =
|
||||
PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
|
||||
sequence =
|
||||
PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
|
||||
sequence =
|
||||
PrintRegister64("x25", frame_arm64->context.iregs[25], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
|
||||
sequence =
|
||||
PrintRegister64("x26", frame_arm64->context.iregs[26], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
|
||||
sequence =
|
||||
PrintRegister64("x27", frame_arm64->context.iregs[27], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
|
||||
sequence =
|
||||
PrintRegister64("x28", frame_arm64->context.iregs[28], sequence);
|
||||
}
|
||||
|
||||
// Registers with a dedicated or conventional purpose.
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
|
||||
sequence =
|
||||
PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
|
||||
sequence =
|
||||
PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
|
||||
sequence =
|
||||
PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
|
||||
}
|
||||
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
|
||||
sequence =
|
||||
PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
|
||||
}
|
||||
} else if (cpu == "mips") {
|
||||
const StackFrameMIPS* frame_mips =
|
||||
reinterpret_cast<const StackFrameMIPS*>(frame);
|
||||
|
@ -328,7 +467,7 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
|||
sequence = PrintRegister64("s7",
|
||||
frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
|
||||
sequence);
|
||||
}
|
||||
}
|
||||
printf("\n Found by: %s\n", frame->trust_description().c_str());
|
||||
}
|
||||
}
|
||||
|
|
79
src/processor/stack_frame_cpu.cc
Normal file
79
src/processor/stack_frame_cpu.cc
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2013 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// stack_frame_cpu.h: CPU-specific StackFrame extensions.
|
||||
//
|
||||
// See google_breakpad/processor/stack_frame_cpu.h for documentation.
|
||||
//
|
||||
// Author: Colin Blundell
|
||||
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X0;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X1;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X2;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X3;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X4;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X5;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X6;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X7;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X8;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X9;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X10;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X11;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X12;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X13;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X14;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X15;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X16;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X17;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X18;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X19;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X20;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X21;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X22;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X23;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X24;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X25;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X26;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X27;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X28;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X29;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X30;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X31;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_X32;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_FP;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_LR;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_SP;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_PC;
|
||||
const uint64_t StackFrameARM64::CONTEXT_VALID_ALL;
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -53,6 +53,7 @@
|
|||
#include "processor/stackwalker_x86.h"
|
||||
#include "processor/stackwalker_amd64.h"
|
||||
#include "processor/stackwalker_arm.h"
|
||||
#include "processor/stackwalker_arm64.h"
|
||||
#include "processor/stackwalker_mips.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
@ -239,6 +240,7 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
|
|||
break;
|
||||
|
||||
case MD_CONTEXT_ARM:
|
||||
{
|
||||
int fp_register = -1;
|
||||
if (system_info->os_short == "ios")
|
||||
fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
|
||||
|
@ -247,6 +249,14 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
|
|||
fp_register, memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
}
|
||||
|
||||
case MD_CONTEXT_ARM64:
|
||||
cpu_stackwalker = new StackwalkerARM64(system_info,
|
||||
context->GetContextARM64(),
|
||||
memory, modules,
|
||||
frame_symbolizer);
|
||||
break;
|
||||
}
|
||||
|
||||
BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
|
||||
|
|
209
src/processor/stackwalker_arm64.cc
Normal file
209
src/processor/stackwalker_arm64.cc
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Copyright (c) 2013 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.
|
||||
|
||||
// stackwalker_arm64.cc: arm64-specific stackwalker.
|
||||
//
|
||||
// See stackwalker_arm64.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy, Colin Blundell
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/stackwalker_arm64.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
StackwalkerARM64::StackwalkerARM64(const SystemInfo* system_info,
|
||||
const MDRawContextARM64* context,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* resolver_helper)
|
||||
: Stackwalker(system_info, memory, modules, resolver_helper),
|
||||
context_(context),
|
||||
context_frame_validity_(StackFrameARM64::CONTEXT_VALID_ALL) { }
|
||||
|
||||
|
||||
StackFrame* StackwalkerARM64::GetContextFrame() {
|
||||
if (!context_) {
|
||||
BPLOG(ERROR) << "Can't get context frame without context";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
// The instruction pointer is stored directly in a register (x32), so pull it
|
||||
// straight out of the CPU context structure.
|
||||
frame->context = *context_;
|
||||
frame->context_validity = context_frame_validity_;
|
||||
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
|
||||
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC];
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByCFIFrameInfo(
|
||||
const vector<StackFrame*> &frames,
|
||||
CFIFrameInfo* cfi_frame_info) {
|
||||
// Obtaining the stack frame from CFI info is not yet supported for ARM64.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByStackScan(
|
||||
const vector<StackFrame*> &frames) {
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
uint64_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
|
||||
uint64_t caller_sp, caller_pc;
|
||||
|
||||
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc,
|
||||
frames.size() == 1 /* is_context_frame */)) {
|
||||
// No plausible return address was found.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ScanForReturnAddress found a reasonable return address. Advance
|
||||
// %sp to the location above the one where the return address was
|
||||
// found.
|
||||
caller_sp += 8;
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
frame->trust = StackFrame::FRAME_TRUST_SCAN;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] = caller_pc;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
|
||||
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrameARM64* StackwalkerARM64::GetCallerByFramePointer(
|
||||
const vector<StackFrame*> &frames) {
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
|
||||
uint64_t last_fp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP];
|
||||
|
||||
uint64_t caller_fp = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x"
|
||||
<< std::hex << last_fp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t caller_lr = 0;
|
||||
if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 8, &caller_lr)) {
|
||||
BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 8: 0x"
|
||||
<< std::hex << (last_fp + 8);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint64_t caller_sp = last_fp ? last_fp + 16 :
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
|
||||
|
||||
// Create a new stack frame (ownership will be transferred to the caller)
|
||||
// and fill it in.
|
||||
StackFrameARM64* frame = new StackFrameARM64();
|
||||
|
||||
frame->trust = StackFrame::FRAME_TRUST_FP;
|
||||
frame->context = last_frame->context;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] = caller_fp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] =
|
||||
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_LR];
|
||||
frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] = caller_lr;
|
||||
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_LR |
|
||||
StackFrameARM64::CONTEXT_VALID_FP |
|
||||
StackFrameARM64::CONTEXT_VALID_SP;
|
||||
return frame;
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerARM64::GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const vector<StackFrame*> &frames = *stack->frames();
|
||||
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
|
||||
scoped_ptr<StackFrameARM64> frame;
|
||||
|
||||
// See if there is DWARF call frame information covering this address.
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(
|
||||
frame_symbolizer_->FindCFIFrameInfo(last_frame));
|
||||
if (cfi_frame_info.get())
|
||||
frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
||||
|
||||
// If CFI failed, or there wasn't CFI available, fall back to frame pointer.
|
||||
if (!frame.get())
|
||||
frame.reset(GetCallerByFramePointer(frames));
|
||||
|
||||
// If everything failed, fall back to stack scanning.
|
||||
if (stack_scan_allowed && !frame.get())
|
||||
frame.reset(GetCallerByStackScan(frames));
|
||||
|
||||
// If nothing worked, tell the caller.
|
||||
if (!frame.get())
|
||||
return NULL;
|
||||
|
||||
// An instruction address of zero marks the end of the stack.
|
||||
if (frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] == 0)
|
||||
return NULL;
|
||||
|
||||
// If the new stack pointer is at a lower address than the old, then
|
||||
// that's clearly incorrect. Treat this as end-of-stack to enforce
|
||||
// progress and avoid infinite loops.
|
||||
if (frame->context.iregs[MD_CONTEXT_ARM64_REG_SP]
|
||||
< last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP])
|
||||
return NULL;
|
||||
|
||||
// The new frame's context's PC is the return address, which is one
|
||||
// instruction past the instruction that caused us to arrive at the callee.
|
||||
// ARM64 instructions have a uniform 4-byte encoding, so subtracting 4 off
|
||||
// the return address gets back to the beginning of the call instruction.
|
||||
// Callers that require the exact return address value may access
|
||||
// frame->context.iregs[MD_CONTEXT_ARM64_REG_PC].
|
||||
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] - 4;
|
||||
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
102
src/processor/stackwalker_arm64.h
Normal file
102
src/processor/stackwalker_arm64.h
Normal file
|
@ -0,0 +1,102 @@
|
|||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright (c) 2013 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.
|
||||
|
||||
// stackwalker_arm64.h: arm64-specific stackwalker.
|
||||
//
|
||||
// Provides stack frames given arm64 register context and a memory region
|
||||
// corresponding to an arm64 stack.
|
||||
//
|
||||
// Author: Mark Mentovai, Ted Mielczarek, Colin Blundell
|
||||
|
||||
|
||||
#ifndef PROCESSOR_STACKWALKER_ARM64_H__
|
||||
#define PROCESSOR_STACKWALKER_ARM64_H__
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CodeModules;
|
||||
|
||||
class StackwalkerARM64 : public Stackwalker {
|
||||
public:
|
||||
// context is an arm64 context object that gives access to arm64-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.
|
||||
StackwalkerARM64(const SystemInfo* system_info,
|
||||
const MDRawContextARM64* context,
|
||||
MemoryRegion* memory,
|
||||
const CodeModules* modules,
|
||||
StackFrameSymbolizer* frame_symbolizer);
|
||||
|
||||
// Change the context validity mask of the frame returned by
|
||||
// GetContextFrame to VALID. This is only for use by unit tests; the
|
||||
// default behavior is correct for all application code.
|
||||
void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; }
|
||||
|
||||
private:
|
||||
// Implementation of Stackwalker, using arm64 context and stack conventions.
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(const CallStack* stack,
|
||||
bool stack_scan_allowed);
|
||||
|
||||
// Use cfi_frame_info (derived from STACK CFI records) to construct
|
||||
// the frame that called frames.back(). The caller takes ownership
|
||||
// of the returned frame. Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByCFIFrameInfo(const vector<StackFrame*> &frames,
|
||||
CFIFrameInfo* cfi_frame_info);
|
||||
|
||||
// Use the frame pointer. The caller takes ownership of the returned frame.
|
||||
// Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByFramePointer(const vector<StackFrame*> &frames);
|
||||
|
||||
// Scan the stack for plausible return addresses. The caller takes ownership
|
||||
// of the returned frame. Return NULL on failure.
|
||||
StackFrameARM64* GetCallerByStackScan(const vector<StackFrame*> &frames);
|
||||
|
||||
// Stores the CPU context corresponding to the youngest stack frame, to
|
||||
// be returned by GetContextFrame.
|
||||
const MDRawContextARM64* context_;
|
||||
|
||||
// Validity mask for youngest stack frame. This is always
|
||||
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
|
||||
// unit tests.
|
||||
uint64_t context_frame_validity_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // PROCESSOR_STACKWALKER_ARM64_H__
|
536
src/processor/stackwalker_arm64_unittest.cc
Normal file
536
src/processor/stackwalker_arm64_unittest.cc
Normal file
|
@ -0,0 +1,536 @@
|
|||
// Copyright (c) 2010, 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.
|
||||
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// stackwalker_arm64_unittest.cc: Unit tests for StackwalkerARM64 class.
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/test_assembler.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/stackwalker_unittest_utils.h"
|
||||
#include "processor/stackwalker_arm64.h"
|
||||
#include "processor/windows_frame_info.h"
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CallStack;
|
||||
using google_breakpad::CodeModule;
|
||||
using google_breakpad::StackFrameSymbolizer;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::StackFrameARM64;
|
||||
using google_breakpad::Stackwalker;
|
||||
using google_breakpad::StackwalkerARM64;
|
||||
using google_breakpad::SystemInfo;
|
||||
using google_breakpad::WindowsFrameInfo;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using std::vector;
|
||||
using testing::_;
|
||||
using testing::AnyNumber;
|
||||
using testing::Return;
|
||||
using testing::SetArgumentPointee;
|
||||
using testing::Test;
|
||||
|
||||
class StackwalkerARM64Fixture {
|
||||
public:
|
||||
StackwalkerARM64Fixture()
|
||||
: stack_section(kLittleEndian),
|
||||
// Give the two modules reasonable standard locations and names
|
||||
// for tests to play with.
|
||||
module1(0x40000000, 0x10000, "module1", "version1"),
|
||||
module2(0x50000000, 0x10000, "module2", "version2") {
|
||||
// Identify the system as an iOS system, since that is the only platform
|
||||
// for which ARM64 support is currently enabled.
|
||||
system_info.os = "iOS";
|
||||
system_info.os_short = "ios";
|
||||
system_info.cpu = "arm64";
|
||||
system_info.cpu_info = "";
|
||||
|
||||
// Put distinctive values in the raw CPU context.
|
||||
BrandContext(&raw_context);
|
||||
|
||||
// Create some modules with some stock debugging information.
|
||||
modules.Add(&module1);
|
||||
modules.Add(&module2);
|
||||
|
||||
// By default, none of the modules have symbol info; call
|
||||
// SetModuleSymbols to override this.
|
||||
EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _, _))
|
||||
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
|
||||
|
||||
// Avoid GMOCK WARNING "Uninteresting mock function call - returning
|
||||
// directly" for FreeSymbolData().
|
||||
EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
|
||||
|
||||
// Reset max_frames_scanned since it's static.
|
||||
Stackwalker::set_max_frames_scanned(1024);
|
||||
}
|
||||
|
||||
// Set the Breakpad symbol information that supplier should return for
|
||||
// MODULE to INFO.
|
||||
void SetModuleSymbols(MockCodeModule *module, const string &info) {
|
||||
size_t buffer_size;
|
||||
char *buffer = supplier.CopySymbolDataAndOwnTheCopy(info, &buffer_size);
|
||||
EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
|
||||
SetArgumentPointee<4>(buffer_size),
|
||||
Return(MockSymbolSupplier::FOUND)));
|
||||
}
|
||||
|
||||
// Populate stack_region with the contents of stack_section. Use
|
||||
// stack_section.start() as the region's starting address.
|
||||
void RegionFromSection() {
|
||||
string contents;
|
||||
ASSERT_TRUE(stack_section.GetContents(&contents));
|
||||
stack_region.Init(stack_section.start().Value(), contents);
|
||||
}
|
||||
|
||||
// Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
|
||||
void BrandContext(MDRawContextARM64 *raw_context) {
|
||||
uint8_t x = 173;
|
||||
for (size_t i = 0; i < sizeof(*raw_context); i++)
|
||||
reinterpret_cast<uint8_t *>(raw_context)[i] = (x += 17);
|
||||
}
|
||||
|
||||
SystemInfo system_info;
|
||||
MDRawContextARM64 raw_context;
|
||||
Section stack_section;
|
||||
MockMemoryRegion stack_region;
|
||||
MockCodeModule module1;
|
||||
MockCodeModule module2;
|
||||
MockCodeModules modules;
|
||||
MockSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
CallStack call_stack;
|
||||
const vector<StackFrame *> *frames;
|
||||
};
|
||||
|
||||
class SanityCheck: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
TEST_F(SanityCheck, NoResolver) {
|
||||
// Since the context's frame pointer is garbage, the stack walk will end after
|
||||
// the first frame.
|
||||
StackFrameSymbolizer frame_symbolizer(NULL, NULL);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
// This should succeed even without a resolver or supplier.
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
// Check that the values from the original raw context made it
|
||||
// through to the context in the stack frame.
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
|
||||
}
|
||||
|
||||
class GetContextFrame: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
// The stackwalker should be able to produce the context frame even
|
||||
// without stack memory present.
|
||||
TEST_F(GetContextFrame, NoStackMemory) {
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, NULL, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
// Check that the values from the original raw context made it
|
||||
// through to the context in the stack frame.
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
|
||||
}
|
||||
|
||||
class GetCallerFrame: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
TEST_F(GetCallerFrame, ScanWithoutSymbols) {
|
||||
// When the stack walker resorts to scanning the stack,
|
||||
// only addresses located within loaded modules are
|
||||
// considered valid return addresses.
|
||||
// Force scanning through three frames to ensure that the
|
||||
// stack pointer is set properly in scan-recovered frames.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0xF0000000) // more junk
|
||||
.D64(0x0000000D)
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(3U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
|
||||
StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame2->context_validity);
|
||||
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
}
|
||||
|
||||
TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
|
||||
// During stack scanning, if a potential return address
|
||||
// is located within a loaded module that has symbols,
|
||||
// it is only considered a valid return address if it
|
||||
// lies within a function's bounds.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address = 0x50000200;
|
||||
Label frame1_sp;
|
||||
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.D64(0x40001000) // a couple of plausible addresses
|
||||
.D64(0x5000F000) // that are not within functions
|
||||
|
||||
.D64(return_address) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40000200;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
SetModuleSymbols(&module1,
|
||||
// The youngest frame's function.
|
||||
"FUNC 100 400 10 monotreme\n");
|
||||
SetModuleSymbols(&module2,
|
||||
// The calling frame's function.
|
||||
"FUNC 100 400 10 marsupial\n");
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(0U, modules_without_symbols.size());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(2U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
EXPECT_EQ("monotreme", frame0->function_name);
|
||||
EXPECT_EQ(0x40000100ULL, frame0->function_base);
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
EXPECT_EQ("marsupial", frame1->function_name);
|
||||
EXPECT_EQ(0x50000100ULL, frame1->function_base);
|
||||
}
|
||||
|
||||
TEST_F(GetCallerFrame, ScanFirstFrame) {
|
||||
// If the stackwalker resorts to stack scanning, it will scan much
|
||||
// farther to find the caller of the context frame.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(32, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.Append(96, 0) // more space
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(32, 0) // space
|
||||
|
||||
.D64(0xF0000000) // more junk
|
||||
.D64(0x0000000D)
|
||||
|
||||
.Append(256, 0) // more space
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// (won't be found)
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(2U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
}
|
||||
|
||||
// Test that set_max_frames_scanned prevents using stack scanning
|
||||
// to find caller frames.
|
||||
TEST_F(GetCallerFrame, ScanningNotAllowed) {
|
||||
// When the stack walker resorts to scanning the stack,
|
||||
// only addresses located within loaded modules are
|
||||
// considered valid return addresses.
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0x40090000) // junk that's not
|
||||
.D64(0x60000000) // a return address
|
||||
|
||||
.D64(return_address1) // actual return address
|
||||
// frame 1
|
||||
.Mark(&frame1_sp)
|
||||
.Append(16, 0) // space
|
||||
|
||||
.D64(0xF0000000) // more junk
|
||||
.D64(0x0000000D)
|
||||
|
||||
.D64(return_address2) // actual return address
|
||||
// frame 2
|
||||
.Mark(&frame2_sp)
|
||||
.Append(64, 0); // end of stack
|
||||
RegionFromSection();
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&frame_symbolizer);
|
||||
Stackwalker::set_max_frames_scanned(0);
|
||||
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(1U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
}
|
||||
|
||||
class GetFramesByFramePointer: public StackwalkerARM64Fixture, public Test { };
|
||||
|
||||
TEST_F(GetFramesByFramePointer, OnlyFramePointer) {
|
||||
stack_section.start() = 0x80000000;
|
||||
uint64_t return_address1 = 0x50000100;
|
||||
uint64_t return_address2 = 0x50000900;
|
||||
Label frame1_sp, frame2_sp;
|
||||
Label frame1_fp, frame2_fp;
|
||||
stack_section
|
||||
// frame 0
|
||||
.Append(64, 0) // Whatever values on the stack.
|
||||
.D64(0x0000000D) // junk that's not
|
||||
.D64(0xF0000000) // a return address.
|
||||
|
||||
.Mark(&frame1_fp) // Next fp will point to the next value.
|
||||
.D64(frame2_fp) // Save current frame pointer.
|
||||
.D64(return_address2) // Save current link register.
|
||||
.Mark(&frame1_sp)
|
||||
|
||||
// frame 1
|
||||
.Append(64, 0) // Whatever values on the stack.
|
||||
.D64(0x0000000D) // junk that's not
|
||||
.D64(0xF0000000) // a return address.
|
||||
|
||||
.Mark(&frame2_fp)
|
||||
.D64(0)
|
||||
.D64(0)
|
||||
.Mark(&frame2_sp)
|
||||
|
||||
// frame 2
|
||||
.Append(64, 0) // Whatever values on the stack.
|
||||
.D64(0x0000000D) // junk that's not
|
||||
.D64(0xF0000000); // a return address.
|
||||
RegionFromSection();
|
||||
|
||||
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = return_address1;
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = frame1_fp.Value();
|
||||
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
|
||||
StackwalkerARM64 walker(&system_info, &raw_context,
|
||||
&stack_region, &modules, &frame_symbolizer);
|
||||
|
||||
vector<const CodeModule*> modules_without_symbols;
|
||||
vector<const CodeModule*> modules_with_corrupt_symbols;
|
||||
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
|
||||
&modules_with_corrupt_symbols));
|
||||
ASSERT_EQ(2U, modules_without_symbols.size());
|
||||
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
|
||||
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
|
||||
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(3U, frames->size());
|
||||
|
||||
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
|
||||
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
|
||||
frame0->context_validity);
|
||||
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
|
||||
|
||||
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_LR |
|
||||
StackFrameARM64::CONTEXT_VALID_FP |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
|
||||
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
EXPECT_EQ(frame2_fp.Value(),
|
||||
frame1->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
|
||||
|
||||
StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
|
||||
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust);
|
||||
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
|
||||
StackFrameARM64::CONTEXT_VALID_LR |
|
||||
StackFrameARM64::CONTEXT_VALID_FP |
|
||||
StackFrameARM64::CONTEXT_VALID_SP),
|
||||
frame2->context_validity);
|
||||
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
|
||||
EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
|
||||
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
|
||||
EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
|
||||
}
|
Loading…
Reference in a new issue