mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-23 03:55:33 +00:00
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
28
Makefile.am
28
Makefile.am
|
@ -187,12 +187,15 @@ src_libbreakpad_a_SOURCES = \
|
||||||
src/processor/windows_frame_info.h \
|
src/processor/windows_frame_info.h \
|
||||||
src/processor/source_line_resolver_base_types.h \
|
src/processor/source_line_resolver_base_types.h \
|
||||||
src/processor/source_line_resolver_base.cc \
|
src/processor/source_line_resolver_base.cc \
|
||||||
|
src/processor/stack_frame_cpu.cc \
|
||||||
src/processor/stack_frame_symbolizer.cc \
|
src/processor/stack_frame_symbolizer.cc \
|
||||||
src/processor/stackwalker.cc \
|
src/processor/stackwalker.cc \
|
||||||
src/processor/stackwalker_amd64.cc \
|
src/processor/stackwalker_amd64.cc \
|
||||||
src/processor/stackwalker_amd64.h \
|
src/processor/stackwalker_amd64.h \
|
||||||
src/processor/stackwalker_arm.cc \
|
src/processor/stackwalker_arm.cc \
|
||||||
src/processor/stackwalker_arm.h \
|
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.cc \
|
||||||
src/processor/stackwalker_address_list.h \
|
src/processor/stackwalker_address_list.h \
|
||||||
src/processor/stackwalker_mips.cc \
|
src/processor/stackwalker_mips.cc \
|
||||||
|
@ -293,6 +296,7 @@ check_PROGRAMS += \
|
||||||
src/processor/range_map_unittest \
|
src/processor/range_map_unittest \
|
||||||
src/processor/stackwalker_amd64_unittest \
|
src/processor/stackwalker_amd64_unittest \
|
||||||
src/processor/stackwalker_arm_unittest \
|
src/processor/stackwalker_arm_unittest \
|
||||||
|
src/processor/stackwalker_arm64_unittest \
|
||||||
src/processor/stackwalker_address_list_unittest \
|
src/processor/stackwalker_address_list_unittest \
|
||||||
src/processor/stackwalker_mips_unittest \
|
src/processor/stackwalker_mips_unittest \
|
||||||
src/processor/stackwalker_x86_unittest \
|
src/processor/stackwalker_x86_unittest \
|
||||||
|
@ -636,10 +640,12 @@ src_processor_exploitability_unittest_LDADD = \
|
||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
src/processor/simple_symbol_supplier.o \
|
src/processor/simple_symbol_supplier.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
src/processor/stackwalker_address_list.o \
|
src/processor/stackwalker_address_list.o \
|
||||||
src/processor/stackwalker_mips.o \
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.o \
|
src/processor/stackwalker_ppc.o \
|
||||||
|
@ -728,10 +734,12 @@ src_processor_minidump_processor_unittest_LDADD = \
|
||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
src/processor/process_state.o \
|
src/processor/process_state.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
src/processor/stackwalker_address_list.o \
|
src/processor/stackwalker_address_list.o \
|
||||||
src/processor/stackwalker_mips.o \
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.o \
|
src/processor/stackwalker_ppc.o \
|
||||||
|
@ -856,10 +864,12 @@ src_processor_stackwalker_selftest_LDADD = \
|
||||||
src/processor/minidump.o \
|
src/processor/minidump.o \
|
||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
src/processor/stackwalker_address_list.o \
|
src/processor/stackwalker_address_list.o \
|
||||||
src/processor/stackwalker_mips.o \
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.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/gtest \
|
||||||
-I$(top_srcdir)/src/testing
|
-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_processor_stackwalker_address_list_unittest_SOURCES = \
|
||||||
src/common/test_assembler.cc \
|
src/common/test_assembler.cc \
|
||||||
src/processor/stackwalker_address_list_unittest.cc \
|
src/processor/stackwalker_address_list_unittest.cc \
|
||||||
|
@ -1012,10 +1038,12 @@ src_processor_minidump_stackwalk_LDADD = \
|
||||||
src/processor/process_state.o \
|
src/processor/process_state.o \
|
||||||
src/processor/simple_symbol_supplier.o \
|
src/processor/simple_symbol_supplier.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
src/processor/stackwalker_address_list.o \
|
src/processor/stackwalker_address_list.o \
|
||||||
src/processor/stackwalker_mips.o \
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.o \
|
src/processor/stackwalker_ppc.o \
|
||||||
|
|
4017
Makefile.in
4017
Makefile.in
File diff suppressed because it is too large
Load diff
615
aclocal.m4
vendored
615
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. */
|
/* Define to 1 if you have the <unistd.h> header file. */
|
||||||
#undef HAVE_UNISTD_H
|
#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 */
|
/* Name of package */
|
||||||
#undef PACKAGE
|
#undef PACKAGE
|
||||||
|
|
||||||
|
|
|
@ -187,6 +187,7 @@ class MinidumpContext : public MinidumpStream {
|
||||||
// NULL.
|
// NULL.
|
||||||
const MDRawContextAMD64* GetContextAMD64() const;
|
const MDRawContextAMD64* GetContextAMD64() const;
|
||||||
const MDRawContextARM* GetContextARM() const;
|
const MDRawContextARM* GetContextARM() const;
|
||||||
|
const MDRawContextARM64* GetContextARM64() const;
|
||||||
const MDRawContextMIPS* GetContextMIPS() const;
|
const MDRawContextMIPS* GetContextMIPS() const;
|
||||||
const MDRawContextPPC* GetContextPPC() const;
|
const MDRawContextPPC* GetContextPPC() const;
|
||||||
const MDRawContextPPC64* GetContextPPC64() const;
|
const MDRawContextPPC64* GetContextPPC64() const;
|
||||||
|
@ -210,6 +211,7 @@ class MinidumpContext : public MinidumpStream {
|
||||||
// so variables can NOT be named as sparc
|
// so variables can NOT be named as sparc
|
||||||
MDRawContextSPARC* ctx_sparc;
|
MDRawContextSPARC* ctx_sparc;
|
||||||
MDRawContextARM* arm;
|
MDRawContextARM* arm;
|
||||||
|
MDRawContextARM64* arm64;
|
||||||
MDRawContextMIPS* ctx_mips;
|
MDRawContextMIPS* ctx_mips;
|
||||||
} context_;
|
} context_;
|
||||||
|
|
||||||
|
|
|
@ -270,6 +270,70 @@ struct StackFrameARM : public StackFrame {
|
||||||
int context_validity;
|
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 {
|
struct StackFrameMIPS : public StackFrame {
|
||||||
// MIPS callee save registers for o32 ABI (32bit registers) are:
|
// MIPS callee save registers for o32 ABI (32bit registers) are:
|
||||||
// 1. $s0-$s7,
|
// 1. $s0-$s7,
|
||||||
|
|
|
@ -73,6 +73,30 @@ using std::ifstream;
|
||||||
using std::numeric_limits;
|
using std::numeric_limits;
|
||||||
using std::vector;
|
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
|
// Swapping routines
|
||||||
|
@ -361,6 +385,23 @@ MinidumpContext::~MinidumpContext() {
|
||||||
bool MinidumpContext::Read(uint32_t expected_size) {
|
bool MinidumpContext::Read(uint32_t expected_size) {
|
||||||
valid_ = false;
|
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();
|
FreeContext();
|
||||||
|
|
||||||
// First, figure out what type of CPU this context structure is for.
|
// 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) {
|
if (cpu_type != MD_CONTEXT_AMD64) {
|
||||||
// TODO: fall through to switch below?
|
// 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=550
|
||||||
// http://code.google.com/p/google-breakpad/issues/detail?id=224
|
|
||||||
BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
|
BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -483,6 +523,21 @@ bool MinidumpContext::Read(uint32_t expected_size) {
|
||||||
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
|
||||||
scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64());
|
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
|
// Set the context_flags member, which has already been read, and
|
||||||
// read the rest of the structure beginning with the first member
|
// 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_.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 {
|
} else {
|
||||||
uint32_t context_flags;
|
uint32_t context_flags;
|
||||||
if (!minidump_->ReadBytes(&context_flags, sizeof(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:
|
case MD_CONTEXT_ARM:
|
||||||
*ip = context_.arm->iregs[MD_CONTEXT_ARM_REG_PC];
|
*ip = context_.arm->iregs[MD_CONTEXT_ARM_REG_PC];
|
||||||
break;
|
break;
|
||||||
|
case MD_CONTEXT_ARM64:
|
||||||
|
*ip = context_.arm64->iregs[MD_CONTEXT_ARM64_REG_PC];
|
||||||
|
break;
|
||||||
case MD_CONTEXT_PPC:
|
case MD_CONTEXT_PPC:
|
||||||
*ip = context_.ppc->srr0;
|
*ip = context_.ppc->srr0;
|
||||||
break;
|
break;
|
||||||
|
@ -1033,6 +1168,15 @@ const MDRawContextARM* MinidumpContext::GetContextARM() const {
|
||||||
return context_.arm;
|
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 {
|
const MDRawContextMIPS* MinidumpContext::GetContextMIPS() const {
|
||||||
if (GetContextCPU() != MD_CONTEXT_MIPS) {
|
if (GetContextCPU() != MD_CONTEXT_MIPS) {
|
||||||
BPLOG(ERROR) << "MinidumpContext cannot get MIPS context";
|
BPLOG(ERROR) << "MinidumpContext cannot get MIPS context";
|
||||||
|
@ -1068,6 +1212,10 @@ void MinidumpContext::FreeContext() {
|
||||||
delete context_.arm;
|
delete context_.arm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MD_CONTEXT_ARM64:
|
||||||
|
delete context_.arm64;
|
||||||
|
break;
|
||||||
|
|
||||||
case MD_CONTEXT_MIPS:
|
case MD_CONTEXT_MIPS:
|
||||||
delete context_.ctx_mips;
|
delete context_.ctx_mips;
|
||||||
break;
|
break;
|
||||||
|
@ -1142,6 +1290,11 @@ bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
|
||||||
return_value = true;
|
return_value = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MD_CONTEXT_ARM64:
|
||||||
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
|
||||||
|
return_value = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case MD_CONTEXT_MIPS:
|
case MD_CONTEXT_MIPS:
|
||||||
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
|
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
|
||||||
return_value = true;
|
return_value = true;
|
||||||
|
@ -1424,6 +1577,31 @@ void MinidumpContext::Print() {
|
||||||
break;
|
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: {
|
case MD_CONTEXT_MIPS: {
|
||||||
const MDRawContextMIPS* context_mips = GetContextMIPS();
|
const MDRawContextMIPS* context_mips = GetContextMIPS();
|
||||||
printf("MDRawContextMIPS\n");
|
printf("MDRawContextMIPS\n");
|
||||||
|
@ -3530,6 +3708,10 @@ string MinidumpSystemInfo::GetCPU() {
|
||||||
cpu = "arm";
|
cpu = "arm";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MD_CPU_ARCHITECTURE_ARM64:
|
||||||
|
cpu = "arm64";
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
|
BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
|
||||||
HexString(system_info_.processor_architecture);
|
HexString(system_info_.processor_architecture);
|
||||||
|
@ -4265,6 +4447,9 @@ bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) {
|
||||||
case MD_CPU_ARCHITECTURE_ARM:
|
case MD_CPU_ARCHITECTURE_ARM:
|
||||||
*context_cpu_flags = MD_CONTEXT_ARM;
|
*context_cpu_flags = MD_CONTEXT_ARM;
|
||||||
break;
|
break;
|
||||||
|
case MD_CPU_ARCHITECTURE_ARM64:
|
||||||
|
*context_cpu_flags = MD_CONTEXT_ARM64;
|
||||||
|
break;
|
||||||
case MD_CPU_ARCHITECTURE_IA64:
|
case MD_CPU_ARCHITECTURE_IA64:
|
||||||
*context_cpu_flags = MD_CONTEXT_IA64;
|
*context_cpu_flags = MD_CONTEXT_IA64;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -505,6 +505,11 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||||
|
info->cpu = "arm64";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case MD_CPU_ARCHITECTURE_MIPS: {
|
case MD_CPU_ARCHITECTURE_MIPS: {
|
||||||
info->cpu = "mips";
|
info->cpu = "mips";
|
||||||
break;
|
break;
|
||||||
|
@ -668,7 +673,9 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
||||||
default:
|
default:
|
||||||
// arm and ppc overlap
|
// arm and ppc overlap
|
||||||
if (raw_system_info->processor_architecture ==
|
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) {
|
switch (exception_flags) {
|
||||||
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
||||||
reason.append("EXC_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:
|
case MD_EXCEPTION_MAC_BAD_INSTRUCTION:
|
||||||
reason = "EXC_BAD_INSTRUCTION / ";
|
reason = "EXC_BAD_INSTRUCTION / ";
|
||||||
switch (raw_system_info->processor_architecture) {
|
switch (raw_system_info->processor_architecture) {
|
||||||
case MD_CPU_ARCHITECTURE_ARM: {
|
case MD_CPU_ARCHITECTURE_ARM:
|
||||||
|
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||||
switch (exception_flags) {
|
switch (exception_flags) {
|
||||||
case MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED:
|
case MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED:
|
||||||
reason.append("EXC_ARM_UNDEFINED");
|
reason.append("EXC_ARM_UNDEFINED");
|
||||||
|
@ -887,7 +895,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
|
||||||
case MD_EXCEPTION_MAC_BREAKPOINT:
|
case MD_EXCEPTION_MAC_BREAKPOINT:
|
||||||
reason = "EXC_BREAKPOINT / ";
|
reason = "EXC_BREAKPOINT / ";
|
||||||
switch (raw_system_info->processor_architecture) {
|
switch (raw_system_info->processor_architecture) {
|
||||||
case MD_CPU_ARCHITECTURE_ARM: {
|
case MD_CPU_ARCHITECTURE_ARM:
|
||||||
|
case MD_CPU_ARCHITECTURE_ARM64: {
|
||||||
switch (exception_flags) {
|
switch (exception_flags) {
|
||||||
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
|
||||||
reason.append("EXC_ARM_DA_ALIGN");
|
reason.append("EXC_ARM_DA_ALIGN");
|
||||||
|
|
|
@ -72,6 +72,7 @@ using google_breakpad::StackFrameSPARC;
|
||||||
using google_breakpad::StackFrameX86;
|
using google_breakpad::StackFrameX86;
|
||||||
using google_breakpad::StackFrameAMD64;
|
using google_breakpad::StackFrameAMD64;
|
||||||
using google_breakpad::StackFrameARM;
|
using google_breakpad::StackFrameARM;
|
||||||
|
using google_breakpad::StackFrameARM64;
|
||||||
using google_breakpad::StackFrameMIPS;
|
using google_breakpad::StackFrameMIPS;
|
||||||
|
|
||||||
// Separator character for machine readable output.
|
// 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);
|
sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
|
||||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
|
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
|
||||||
sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
|
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") {
|
} else if (cpu == "mips") {
|
||||||
const StackFrameMIPS* frame_mips =
|
const StackFrameMIPS* frame_mips =
|
||||||
reinterpret_cast<const StackFrameMIPS*>(frame);
|
reinterpret_cast<const StackFrameMIPS*>(frame);
|
||||||
|
|
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_x86.h"
|
||||||
#include "processor/stackwalker_amd64.h"
|
#include "processor/stackwalker_amd64.h"
|
||||||
#include "processor/stackwalker_arm.h"
|
#include "processor/stackwalker_arm.h"
|
||||||
|
#include "processor/stackwalker_arm64.h"
|
||||||
#include "processor/stackwalker_mips.h"
|
#include "processor/stackwalker_mips.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
@ -239,6 +240,7 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MD_CONTEXT_ARM:
|
case MD_CONTEXT_ARM:
|
||||||
|
{
|
||||||
int fp_register = -1;
|
int fp_register = -1;
|
||||||
if (system_info->os_short == "ios")
|
if (system_info->os_short == "ios")
|
||||||
fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
|
fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
|
||||||
|
@ -249,6 +251,14 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
|
||||||
break;
|
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) <<
|
BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
|
||||||
", can't choose a stackwalker "
|
", can't choose a stackwalker "
|
||||||
"implementation";
|
"implementation";
|
||||||
|
|
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