bug 548113 - Sync to Breakpad revision 554

--HG--
extra : rebase_source : d2fec8ccdc5e042a806f257b2c80631373549b3b
This commit is contained in:
Ted Mielczarek 2010-03-17 11:57:25 -04:00
Родитель 90aa8f9de7
Коммит ce696988e7
117 изменённых файлов: 43798 добавлений и 22355 удалений

Просмотреть файл

@ -1,6 +1,6 @@
## Process this file with automake to produce Makefile.in
# Copyright (c) 2006, Google Inc.
# Copyright (c) 2010, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@ -33,6 +33,8 @@
# This allows #includes to be relative to src/
AM_CPPFLAGS = -I$(top_srcdir)/src
# Specify include paths for ac macros
ACLOCAL_AMFLAGS = -I m4
## Documentation
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
@ -47,7 +49,19 @@ dist_doc_DATA = \
## Libraries
lib_LTLIBRARIES = src/libbreakpad.la
lib_LTLIBRARIES = src/libbreakpad.la src/client/linux/libbreakpad_client.la
src_client_linux_libbreakpad_client_la_SOURCES = \
src/client/linux/crash_generation/crash_generation_client.cc \
src/client/linux/handler/exception_handler.cc \
src/client/linux/minidump_writer/linux_dumper.cc \
src/client/linux/minidump_writer/minidump_writer.cc \
src/client/minidump_file_writer.cc \
src/common/convert_UTF.c \
src/common/md5.c \
src/common/string_conversion.cc \
src/common/linux/file_id.cc \
src/common/linux/guid_creator.cc
src_libbreakpad_la_SOURCES = \
src/google_breakpad/common/breakpad_types.h \
@ -74,6 +88,8 @@ src_libbreakpad_la_SOURCES = \
src/processor/basic_code_modules.h \
src/processor/basic_source_line_resolver.cc \
src/processor/call_stack.cc \
src/processor/cfi_frame_info.cc \
src/processor/cfi_frame_info.h \
src/processor/contained_range_map-inl.h \
src/processor/contained_range_map.h \
src/processor/linked_ptr.h \
@ -91,7 +107,7 @@ src_libbreakpad_la_SOURCES = \
src/processor/scoped_ptr.h \
src/processor/simple_symbol_supplier.cc \
src/processor/simple_symbol_supplier.h \
src/processor/stack_frame_info.h \
src/processor/windows_frame_info.h \
src/processor/stackwalker.cc \
src/processor/stackwalker_amd64.cc \
src/processor/stackwalker_amd64.h \
@ -107,20 +123,27 @@ src_libbreakpad_la_SOURCES = \
## Programs
bin_PROGRAMS = \
src/client/linux/linux_dumper_unittest_helper \
src/processor/minidump_dump \
src/processor/minidump_stackwalk
## Tests
check_PROGRAMS = \
src/client/linux/linux_client_unittest \
src/processor/address_map_unittest \
src/processor/basic_source_line_resolver_unittest \
src/processor/cfi_frame_info_unittest \
src/processor/contained_range_map_unittest \
src/processor/minidump_processor_unittest \
src/processor/minidump_unittest \
src/processor/pathname_stripper_unittest \
src/processor/postfix_evaluator_unittest \
src/processor/range_map_unittest
src/processor/range_map_unittest \
src/processor/stackwalker_amd64_unittest \
src/processor/stackwalker_arm_unittest \
src/processor/stackwalker_x86_unittest \
src/processor/synth_minidump_unittest \
src/processor/test_assembler_unittest
if SELFTEST
check_PROGRAMS += \
@ -135,6 +158,40 @@ check_SCRIPTS = \
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
TESTS_ENVIRONMENT =
src_client_linux_linux_dumper_unittest_helper_SOURCES = \
src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS)
src_client_linux_linux_dumper_unittest_helper_LDFLAGS=$(PTHREAD_CFLAGS)
src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC)
src_client_linux_linux_client_unittest_SOURCES = \
src/client/linux/handler/exception_handler_unittest.cc \
src/client/linux/minidump_writer/directory_reader_unittest.cc \
src/client/linux/minidump_writer/line_reader_unittest.cc \
src/client/linux/minidump_writer/linux_dumper_unittest.cc \
src/client/linux/minidump_writer/minidump_writer_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_client_linux_linux_client_unittest_CPPFLAGS = \
-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_client_linux_linux_client_unittest_LDADD = \
src/client/linux/handler/exception_handler.lo \
src/client/linux/crash_generation/crash_generation_client.lo \
src/client/linux/minidump_writer/linux_dumper.lo \
src/client/linux/minidump_writer/minidump_writer.lo \
src/client/minidump_file_writer.lo \
src/common/convert_UTF.lo \
src/common/md5.lo \
src/common/linux/file_id.lo \
src/common/linux/guid_creator.lo \
src/common/string_conversion.lo
src_client_linux_linux_client_unittest_DEPENDENCIES = src/client/linux/linux_dumper_unittest_helper src/client/linux/libbreakpad_client.la
src_processor_address_map_unittest_SOURCES = \
src/processor/address_map_unittest.cc
src_processor_address_map_unittest_LDADD = \
@ -145,9 +202,25 @@ src_processor_basic_source_line_resolver_unittest_SOURCES = \
src/processor/basic_source_line_resolver_unittest.cc
src_processor_basic_source_line_resolver_unittest_LDADD = \
src/processor/basic_source_line_resolver.lo \
src/processor/cfi_frame_info.lo \
src/processor/pathname_stripper.lo \
src/processor/logging.lo
src_processor_cfi_frame_info_unittest_SOURCES = \
src/processor/cfi_frame_info_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_cfi_frame_info_unittest_LDADD = \
src/processor/cfi_frame_info.lo \
src/processor/logging.lo \
src/processor/pathname_stripper.lo
src_processor_cfi_frame_info_unittest_CPPFLAGS = \
-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_contained_range_map_unittest_SOURCES = \
src/processor/contained_range_map_unittest.cc
src_processor_contained_range_map_unittest_LDADD = \
@ -167,6 +240,7 @@ src_processor_minidump_processor_unittest_LDADD = \
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/call_stack.lo \
src/processor/cfi_frame_info.lo \
src/processor/logging.lo \
src/processor/minidump_processor.lo \
src/processor/minidump.lo \
@ -181,7 +255,10 @@ src_processor_minidump_processor_unittest_LDADD = \
src_processor_minidump_unittest_SOURCES = \
src/processor/minidump_unittest.cc \
src/processor/synth_minidump.cc \
src/processor/test_assembler.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_minidump_unittest_CPPFLAGS = \
-I$(top_srcdir)/src/testing/include \
@ -227,6 +304,76 @@ src_processor_stackwalker_selftest_LDADD = \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo
src_processor_stackwalker_amd64_unittest_SOURCES = \
src/processor/stackwalker_amd64_unittest.cc \
src/processor/test_assembler.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_stackwalker_amd64_unittest_LDADD = \
src/libbreakpad.la
src_processor_stackwalker_amd64_unittest_CPPFLAGS = \
-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_arm_unittest_SOURCES = \
src/processor/stackwalker_arm_unittest.cc \
src/processor/test_assembler.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_stackwalker_arm_unittest_LDADD = \
src/libbreakpad.la
src_processor_stackwalker_arm_unittest_CPPFLAGS = \
-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_x86_unittest_SOURCES = \
src/processor/stackwalker_x86_unittest.cc \
src/processor/test_assembler.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_stackwalker_x86_unittest_LDADD = \
src/libbreakpad.la
src_processor_stackwalker_x86_unittest_CPPFLAGS = \
-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_synth_minidump_unittest_SOURCES = \
src/processor/synth_minidump_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc \
src/processor/synth_minidump.cc \
src/processor/synth_minidump.h \
src/processor/test_assembler.cc \
src/processor/test_assembler.h
src_processor_synth_minidump_unittest_CPPFLAGS = \
-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_test_assembler_unittest_SOURCES = \
src/processor/test_assembler_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc \
src/processor/test_assembler.cc \
src/processor/test_assembler.h
src_processor_test_assembler_unittest_CPPFLAGS = \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
## Non-installables
noinst_PROGRAMS =
noinst_SCRIPTS = $(check_SCRIPTS)
@ -245,6 +392,7 @@ src_processor_minidump_stackwalk_LDADD = \
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/call_stack.lo \
src/processor/cfi_frame_info.lo \
src/processor/logging.lo \
src/processor/minidump.lo \
src/processor/minidump_processor.lo \
@ -399,3 +547,4 @@ EXTRA_DIST = \
## Additional rules
libtool: $(LIBTOOL_DEPS)
$(SHELL) ./config.status --recheck

9651
toolkit/crashreporter/google-breakpad/aclocal.m4 поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

23175
toolkit/crashreporter/google-breakpad/configure поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -35,7 +35,7 @@ dnl Sanity check: the argument is just a file that should exist.
AC_CONFIG_SRCDIR(README)
AC_CONFIG_AUX_DIR(autotools)
AM_INIT_AUTOMAKE(subdir-objects tar-ustar)
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
AM_CONFIG_HEADER(src/config.h)
AC_PROG_CC
@ -46,6 +46,26 @@ AC_PROG_LIBTOOL
AC_SUBST(LIBTOOL_DEPS)
AC_HEADER_STDC
m4_include(m4/ax_pthread.m4)
AX_PTHREAD
AC_ARG_ENABLE(m32,
AS_HELP_STRING([--enable-m32],
[Compile/build with -m32]
[(default is no)]),
[case "${enableval}" in
yes)
CFLAGS=$(CFLAGS) -m32
usem32=true
;;
no)
usem32=false
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --enable-m32)
;;
esac],
[usem32=false])
AC_ARG_ENABLE(selftest,
AS_HELP_STRING([--enable-selftest],

Просмотреть файл

@ -0,0 +1,283 @@
# ===========================================================================
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
#
# 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 3 of the License, 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, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_SAVE
AC_LANG_C
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case "${host_cpu}-${host_os}" in
*solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
*-darwin*)
acx_pthread_flags="-pthread $acx_pthread_flags"
;;
esac
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_TRY_LINK([#include <pthread.h>
static void routine(void* a) {a=0;}
static void* start_routine(void* a) {return a;}],
[pthread_t th; pthread_attr_t attr;
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_create(&th,0,start_routine,0);
pthread_cleanup_pop(0); ],
[ax_pthread_ok=yes])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_MSG_CHECKING([for joinable pthread attribute])
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
[attr_name=$attr; break])
done
AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case "${host_cpu}-${host_os}" in
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with xlc_r or cc_r
if test x"$GCC" != xyes; then
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
else
PTHREAD_CC=$CC
fi
else
PTHREAD_CC="$CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_RESTORE
])dnl AX_PTHREAD

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -27,6 +27,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
@ -44,7 +45,6 @@ CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
{
int fds[2];
sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
struct kernel_msghdr msg;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -80,6 +80,9 @@
#include <ucontext.h>
#include <unistd.h>
#include <algorithm>
#include <vector>
#include "common/linux/linux_libc_support.h"
#include "common/linux/linux_syscall_support.h"
#include "common/linux/memory.h"
@ -88,7 +91,7 @@
// A wrapper for the tgkill syscall: send a signal to a specific thread.
static int tgkill(pid_t tgid, pid_t tid, int sig) {
syscall(__NR_tgkill, tgid, tid, sig);
return syscall(__NR_tgkill, tgid, tid, sig);
return 0;
}
@ -145,7 +148,6 @@ void ExceptionHandler::Init(const std::string &dump_path,
const int server_fd)
{
crash_handler_ = NULL;
if (0 <= server_fd)
crash_generation_client_
.reset(CrashGenerationClient::TryCreate(server_fd));
@ -209,7 +211,11 @@ void ExceptionHandler::UninstallHandlers() {
sigaction(old_handlers_[i].first, action, NULL);
delete action;
}
pthread_mutex_lock(&handler_stack_mutex_);
std::vector<ExceptionHandler*>::iterator handler =
std::find(handler_stack_->begin(), handler_stack_->end(), this);
handler_stack_->erase(handler);
pthread_mutex_unlock(&handler_stack_mutex_);
old_handlers_.clear();
}
@ -231,12 +237,15 @@ void ExceptionHandler::UpdateNextID() {
}
}
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
// crash_handler_ = callback;
// }
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
// static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// All the exception signals are blocked at this point.
pthread_mutex_lock(&handler_stack_mutex_);
if (!handler_stack_->size()) {
@ -288,18 +297,25 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
// Allow ourselves to be dumped.
sys_prctl(PR_SET_DUMPABLE, 1);
CrashContext context;
memcpy(&context.siginfo, info, sizeof(siginfo_t));
memcpy(&context.context, uc, sizeof(struct ucontext));
memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs,
sizeof(context.float_state));
#if !defined(__ARM_EABI__)
// FP state is not part of user ABI on ARM Linux.
struct ucontext *uc_ptr = (struct ucontext*)uc;
if (uc_ptr->uc_mcontext.fpregs) {
memcpy(&context.float_state,
uc_ptr->uc_mcontext.fpregs,
sizeof(context.float_state));
}
#endif
context.tid = sys_gettid();
if (crash_handler_ && crash_handler_(&context, sizeof(context),
callback_context_))
return true;
if (crash_handler_ != NULL) {
if (crash_handler_(&context, sizeof(context),
callback_context_)) {
return true;
}
}
return GenerateDump(&context);
}
@ -364,6 +380,7 @@ bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
}
bool ExceptionHandler::WriteMinidump() {
#if !defined(__ARM_EABI__)
// Allow ourselves to be dumped.
sys_prctl(PR_SET_DUMPABLE, 1);
@ -378,6 +395,9 @@ bool ExceptionHandler::WriteMinidump() {
bool success = GenerateDump(&context);
UpdateNextID();
return success;
#else
return false;
#endif // !defined(__ARM_EABI__)
}
} // namespace google_breakpad

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -34,6 +34,7 @@
#include <string>
#include <signal.h>
#include <stdio.h>
#include "client/linux/crash_generation/crash_generation_client.h"
#include "processor/scoped_ptr.h"
@ -42,6 +43,8 @@ struct sigaction;
namespace google_breakpad {
class ExceptionHandler;
// ExceptionHandler
//
// ExceptionHandler can write a minidump file when an exception occurs,
@ -163,7 +166,10 @@ class ExceptionHandler {
siginfo_t siginfo;
pid_t tid; // the crashing thread.
struct ucontext context;
#if !defined(__ARM_EABI__)
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
struct _libc_fpstate float_state;
#endif
};
// Returns whether out-of-process dump generation is used or not.

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -36,7 +36,7 @@
#include <sys/socket.h>
#include <sys/uio.h>
#include "client/linux/handler//exception_handler.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/linux_libc_support.h"
@ -112,8 +112,8 @@ TEST(ExceptionHandlerTest, ChildCrash) {
ASSERT_TRUE(pfd.revents & POLLIN);
uint32_t len;
ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len));
ASSERT_LT(len, 2048);
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
ASSERT_LT(len, (uint32_t)2048);
char* filename = reinterpret_cast<char*>(malloc(len + 1));
ASSERT_EQ(read(fds[0], filename, len), len);
filename[len] = 0;
@ -137,12 +137,10 @@ CrashHandler(const void* crash_context, size_t crash_context_size,
const int fd = (intptr_t) context;
int fds[2];
pipe(fds);
struct kernel_msghdr msg = {0};
struct kernel_iovec iov;
iov.iov_base = const_cast<void*>(crash_context);
iov.iov_len = crash_context_size;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char cmsg[kControlMsgSize];
@ -183,11 +181,10 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
const pid_t child = fork();
if (child == 0) {
close(fds[0]);
ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true);
ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true);
handler.set_crash_handler(CrashHandler);
*reinterpret_cast<int*>(NULL) = 0;
}
close(fds[1]);
struct msghdr msg = {0};
struct iovec iov;

Просмотреть файл

@ -69,9 +69,9 @@ TEST(LineReaderTest, OneLineTerminated) {
LineReader reader(fd);
const char *line;
unsigned len;
unsigned int len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, 1);
ASSERT_EQ(len, (unsigned int)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@ -90,7 +90,7 @@ TEST(LineReaderTest, OneLine) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, 1);
ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@ -109,13 +109,13 @@ TEST(LineReaderTest, TwoLinesTerminated) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, 1);
ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, 1);
ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
@ -134,13 +134,13 @@ TEST(LineReaderTest, TwoLines) {
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, 1);
ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'a');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(len, 1);
ASSERT_EQ(len, (unsigned)1);
ASSERT_EQ(line[0], 'b');
ASSERT_EQ(line[1], 0);
reader.PopLine(len);

Просмотреть файл

@ -85,7 +85,7 @@ namespace google_breakpad {
LinuxDumper::LinuxDumper(int pid)
: pid_(pid),
threads_suspened_(false),
threads_suspended_(false),
threads_(&allocator_, 8),
mappings_(&allocator_) {
}
@ -96,22 +96,22 @@ bool LinuxDumper::Init() {
}
bool LinuxDumper::ThreadsSuspend() {
if (threads_suspened_)
if (threads_suspended_)
return true;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= SuspendThread(threads_[i]);
threads_suspened_ = true;
threads_suspended_ = true;
return good;
}
bool LinuxDumper::ThreadsResume() {
if (!threads_suspened_)
if (!threads_suspended_)
return false;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= ResumeThread(threads_[i]);
threads_suspened_ = false;
threads_suspended_ = false;
return good;
}
@ -312,9 +312,9 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
}
// Read thread info from /proc/$pid/status.
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible,
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
// these members are set to -1. Returns true iff all three members are
// availible.
// available.
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
assert(info != NULL);
char status_path[80];
@ -371,6 +371,8 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
#elif defined(__x86_64)
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
#elif defined(__ARM_EABI__)
memcpy(&stack_pointer, &info->regs.uregs[R13], sizeof(info->regs.uregs[R13]));
#else
#error "This code hasn't been ported to your platform yet."
#endif
@ -387,13 +389,9 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
// unwind. So we just grab, up to, 32k of stack.
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
uintptr_t int_stack_pointer) {
#if defined(__i386) || defined(__x86_64)
static const bool stack_grows_down = true;
static const uintptr_t page_size = 4096;
#else
#error "This code has not been ported to your platform yet."
#endif
// Move the stack pointer to the bottom of the page that it's in.
const uintptr_t page_size = getpagesize();
uint8_t* const stack_pointer =
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
@ -403,26 +401,19 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
const MappingInfo* mapping = FindMapping(stack_pointer);
if (!mapping)
return false;
if (stack_grows_down) {
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
const ptrdiff_t distance_to_end =
static_cast<ptrdiff_t>(mapping->size) - offset;
*stack_len = distance_to_end > kStackToCapture ?
kStackToCapture : distance_to_end;
*stack = stack_pointer;
} else {
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
*stack_len = offset > kStackToCapture ? kStackToCapture : offset;
*stack = stack_pointer - *stack_len;
}
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
const ptrdiff_t distance_to_end =
static_cast<ptrdiff_t>(mapping->size) - offset;
*stack_len = distance_to_end > kStackToCapture ?
kStackToCapture : distance_to_end;
*stack = stack_pointer;
return true;
}
// static
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) {
unsigned long tmp;
unsigned long tmp = 55;
size_t done = 0;
static const size_t word_size = sizeof(tmp);
uint8_t* const local = (uint8_t*) dest;
@ -430,8 +421,9 @@ void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
while (done < length) {
const size_t l = length - done > word_size ? word_size : length - done;
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1)
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
tmp = 0;
}
memcpy(local + done, &tmp, l);
done += l;
}

Просмотреть файл

@ -44,7 +44,7 @@ namespace google_breakpad {
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
#if defined(__i386)
#if defined(__i386) || defined(__ARM_EABI__)
typedef Elf32_auxv_t elf_aux_entry;
#elif defined(__x86_64__)
typedef Elf64_auxv_t elf_aux_entry;
@ -64,16 +64,20 @@ struct ThreadInfo {
const void* stack; // pointer to the stack area
size_t stack_len; // length of the stack to copy
user_regs_struct regs;
user_fpregs_struct fpregs;
#if defined(__i386)
user_fpxregs_struct fpxregs;
#endif
#if defined(__i386) || defined(__x86_64)
user_regs_struct regs;
user_fpregs_struct fpregs;
static const unsigned kNumDebugRegisters = 8;
debugreg_t dregs[8];
#if defined(__i386)
user_fpxregs_struct fpxregs;
#endif // defined(__i386)
#elif defined(__ARM_EABI__)
// Mimicking how strace does this(see syscall.c, search for GETREGS)
struct user_regs regs;
struct user_fpregs fpregs;
#endif
};
@ -141,7 +145,7 @@ class LinuxDumper {
mutable PageAllocator allocator_;
bool threads_suspened_;
bool threads_suspended_;
wasteful_vector<pid_t> threads_; // the ids of all the threads
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
};

Просмотреть файл

@ -29,10 +29,13 @@
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/file_id.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/memory.h"
using namespace google_breakpad;
@ -67,7 +70,7 @@ TEST(LinuxDumperTest, ThreadList) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_GE(dumper.threads().size(), 1);
ASSERT_GE(dumper.threads().size(), (size_t)1);
bool found = false;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
if (dumper.threads()[i] == getpid()) {
@ -77,6 +80,55 @@ TEST(LinuxDumperTest, ThreadList) {
}
}
TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
static const int kNumberOfThreadsInHelperProgram = 5;
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
pid_t child_pid = fork();
if (child_pid == 0) {
// Set the number of threads
execl("src/client/linux/linux_dumper_unittest_helper",
"linux_dumper_unittest_helper",
kNumberOfThreadsArgument,
NULL);
// Kill if we get here.
printf("Errno from exec: %d", errno);
FAIL() << "Exec failed: " << strerror(errno);
exit(0);
}
// The sleep is flaky, but prevents us from reading
// the child process before all threads have been created.
sleep(1);
LinuxDumper dumper(child_pid);
EXPECT_TRUE(dumper.Init());
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
EXPECT_TRUE(dumper.ThreadsSuspend());
ThreadInfo one_thread;
for(size_t i = 0; i < dumper.threads().size(); ++i) {
EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread));
// We know the threads are in a function which has allocated exactly
// one word off the stack to store its thread id.
#if defined(__ARM_EABI__)
void* process_tid_location = (void *)(one_thread.regs.uregs[11] - 8);
#elif defined(__i386)
void* process_tid_location = (void *)(one_thread.regs.ebp - 4);
#elif defined(__x86_64)
void* process_tid_location = (void *)(one_thread.regs.rbp - 4);
#else
#error Platform not supported!
#endif
pid_t one_thread_id;
dumper.CopyFromProcess(&one_thread_id,
dumper.threads()[i],
process_tid_location,
4);
EXPECT_EQ(dumper.threads()[i], one_thread_id);
}
kill(child_pid, SIGKILL);
}
TEST(LinuxDumperTest, BuildProcPath) {
const pid_t pid = getpid();
LinuxDumper dumper(pid);
@ -106,28 +158,29 @@ TEST(LinuxDumperTest, BuildProcPath) {
#endif
}
#if !defined(__ARM_EABI__)
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
LinuxDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
if (linux_gate_loc) {
bool found_linux_gate = false;
ASSERT_TRUE(linux_gate_loc);
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
const MappingInfo* mapping;
for (unsigned i = 0; i < mappings.size(); ++i) {
mapping = mappings[i];
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
break;
}
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
const MappingInfo* mapping;
for (unsigned i = 0; i < mappings.size(); ++i) {
mapping = mappings[i];
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
break;
}
EXPECT_TRUE(found_linux_gate);
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
EXPECT_TRUE(found_linux_gate);
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
#endif
TEST(LinuxDumperTest, FileIDsMatch) {
// Calculate the File ID of our binary using both

Просмотреть файл

@ -0,0 +1,64 @@
// 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.
//
// Helper program for the linux_dumper class, which creates a bunch of
// threads. The first word of each thread's stack is set to the thread
// id.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
#pragma GCC optimize ("O0")
void *thread_function(void *data) __attribute__((noinline, optimize("O2")));
void *thread_function(void *data) {
pid_t thread_id = syscall(SYS_gettid);
while (true) ;
asm("");
}
int main(int argc, char *argv[]) {
int num_threads = atoi(argv[1]);
if (num_threads < 1) {
fprintf(stderr, "ERROR: number of threads is 0");
return 1;
}
pthread_t threads[num_threads];
pthread_attr_t thread_attributes;
pthread_attr_init(&thread_attributes);
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
for (int i = 1; i < num_threads; i++) {
pthread_create(&threads[i], &thread_attributes, &thread_function, NULL);
}
thread_function(NULL);
return 0;
}

Просмотреть файл

@ -61,7 +61,7 @@
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "client/linux/minidump_writer//linux_dumper.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/linux_syscall_support.h"
@ -307,6 +307,54 @@ static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
}
#elif defined(__ARMEL__)
typedef MDRawContextARM RawContextCPU;
static void CPUFillFromThreadInfo(MDRawContextARM *out,
const google_breakpad::ThreadInfo &info) {
out->context_flags = MD_CONTEXT_ARM_FULL;
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
out->iregs[i] = info.regs.uregs[i];
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
out->cpsr = 0;
out->float_save.fpscr = info.fpregs.fpsr |
(static_cast<u_int64_t>(info.fpregs.fpcr) << 32);
//TODO: sort this out, actually collect floating point registers
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
}
static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
const struct _libc_fpstate* fpregs) {
out->context_flags = MD_CONTEXT_ARM_FULL;
out->iregs[0] = uc->uc_mcontext.arm_r0;
out->iregs[1] = uc->uc_mcontext.arm_r1;
out->iregs[2] = uc->uc_mcontext.arm_r2;
out->iregs[3] = uc->uc_mcontext.arm_r3;
out->iregs[4] = uc->uc_mcontext.arm_r4;
out->iregs[5] = uc->uc_mcontext.arm_r5;
out->iregs[6] = uc->uc_mcontext.arm_r6;
out->iregs[7] = uc->uc_mcontext.arm_r7;
out->iregs[8] = uc->uc_mcontext.arm_r8;
out->iregs[9] = uc->uc_mcontext.arm_r9;
out->iregs[10] = uc->uc_mcontext.arm_r10;
out->iregs[11] = uc->uc_mcontext.arm_fp;
out->iregs[12] = uc->uc_mcontext.arm_ip;
out->iregs[13] = uc->uc_mcontext.arm_sp;
out->iregs[14] = uc->uc_mcontext.arm_lr;
out->iregs[15] = uc->uc_mcontext.arm_pc;
out->cpsr = uc->uc_mcontext.arm_cpsr;
//TODO: fix this after fixing ExceptionHandler
out->float_save.fpscr = 0;
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
}
#else
#error "This code has not been ported to your platform yet."
#endif
@ -321,7 +369,12 @@ class MinidumpWriter {
: filename_(filename),
siginfo_(&context->siginfo),
ucontext_(&context->context),
#if !defined(__ARM_EABI__)
float_state_(&context->float_state),
#else
//TODO: fix this after fixing ExceptionHandler
float_state_(NULL),
#endif
crashing_tid_(context->tid),
dumper_(crashing_pid) {
}
@ -612,6 +665,10 @@ class MinidumpWriter {
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.gregs[REG_RSP];
}
#elif defined(__ARM_EABI__)
uintptr_t GetStackPointer() {
return ucontext_->uc_mcontext.arm_sp;
}
#else
#error "This code has not been ported to your platform yet."
#endif
@ -644,6 +701,8 @@ class MinidumpWriter {
MD_CPU_ARCHITECTURE_X86;
#elif defined(__x86_64)
MD_CPU_ARCHITECTURE_AMD64;
#elif defined(__arm__)
MD_CPU_ARCHITECTURE_ARM;
#else
#error "Unknown CPU arch"
#endif

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without

Двоичный файл не отображается.

Просмотреть файл

@ -612,6 +612,12 @@ NSString *const kDefaultServerType = @"google";
float emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
[emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
// Localize the placeholder text.
[[commentsEntryField_ cell]
setPlaceholderString:NSLocalizedString(@"commentsPlaceholder", @"")];
[[emailEntryField_ cell]
setPlaceholderString:NSLocalizedString(@"emailPlaceholder", @"")];
// Localize the privacy policy label, and keep it right-aligned to the arrow.
[privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
float privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit];
@ -896,8 +902,8 @@ doCommandBySelector:(SEL)commandSelector {
forKey:@BREAKPAD_VERSION];
[socorroDictionary_ setObject:@"ProductName"
forKey:@BREAKPAD_PRODUCT];
[socorroDictionary_ setObject:@"ProductName"
forKey:@BREAKPAD_PRODUCT];
[socorroDictionary_ setObject:@"Email"
forKey:@BREAKPAD_EMAIL];
}
- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType {

Просмотреть файл

@ -205,9 +205,8 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
}
handler_stack_->push_back(this);
if (handler_types & HANDLER_EXCEPTION) {
if (handler_types & HANDLER_EXCEPTION)
previous_filter_ = SetUnhandledExceptionFilter(HandleException);
}
#if _MSC_VER >= 1400 // MSVC 2005/8
if (handler_types & HANDLER_INVALID_PARAMETER)

Просмотреть файл

@ -144,6 +144,32 @@ inline uint64 ByteReader::ReadAddress(const char* buffer) const {
return (this->*address_reader_)(buffer);
}
inline void ByteReader::SetCFIDataBase(uint64 section_base,
const char *buffer_base) {
section_base_ = section_base;
buffer_base_ = buffer_base;
have_section_base_ = true;
}
inline void ByteReader::SetTextBase(uint64 text_base) {
text_base_ = text_base;
have_text_base_ = true;
}
inline void ByteReader::SetDataBase(uint64 data_base) {
data_base_ = data_base;
have_data_base_ = true;
}
inline void ByteReader::SetFunctionBase(uint64 function_base) {
function_base_ = function_base;
have_function_base_ = true;
}
inline void ByteReader::ClearFunctionBase() {
have_function_base_ = false;
}
} // namespace dwarf2reader
#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright 2006 Google Inc. All Rights Reserved.
// 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
@ -27,6 +27,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <stdlib.h>
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/bytereader.h"
@ -35,8 +36,9 @@ namespace dwarf2reader {
ByteReader::ByteReader(enum Endianness endian)
:offset_reader_(NULL), address_reader_(NULL), endian_(endian),
address_size_(0), offset_size_(0)
{ }
address_size_(0), offset_size_(0),
have_section_base_(), have_text_base_(), have_data_base_(),
have_function_base_() { }
ByteReader::~ByteReader() { }
@ -60,4 +62,184 @@ void ByteReader::SetAddressSize(uint8 size) {
}
}
uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
const uint64 initial_length = ReadFourBytes(start);
start += 4;
// In DWARF2/3, if the initial length is all 1 bits, then the offset
// size is 8 and we need to read the next 8 bytes for the real length.
if (initial_length == 0xffffffff) {
SetOffsetSize(8);
*len = 12;
return ReadOffset(start);
} else {
SetOffsetSize(4);
*len = 4;
}
return initial_length;
}
bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const {
if (encoding == DW_EH_PE_omit) return true;
if (encoding == DW_EH_PE_aligned) return true;
if ((encoding & 0x7) > DW_EH_PE_udata8)
return false;
if ((encoding & 0x70) > DW_EH_PE_funcrel)
return false;
return true;
}
bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
switch (encoding & 0x70) {
case DW_EH_PE_absptr: return true;
case DW_EH_PE_pcrel: return have_section_base_;
case DW_EH_PE_textrel: return have_text_base_;
case DW_EH_PE_datarel: return have_data_base_;
case DW_EH_PE_funcrel: return have_function_base_;
default: return false;
}
}
uint64 ByteReader::ReadEncodedPointer(const char *buffer,
DwarfPointerEncoding encoding,
size_t *len) const {
// UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
// see it here.
assert(encoding != DW_EH_PE_omit);
// The Linux Standards Base 4.0 does not make this clear, but the
// GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
// agree that aligned pointers are always absolute, machine-sized,
// machine-signed pointers.
if (encoding == DW_EH_PE_aligned) {
assert(have_section_base_);
// We don't need to align BUFFER in *our* address space. Rather, we
// need to find the next position in our buffer that would be aligned
// when the .eh_frame section the buffer contains is loaded into the
// program's memory. So align assuming that buffer_base_ gets loaded at
// address section_base_, where section_base_ itself may or may not be
// aligned.
// First, find the offset to START from the closest prior aligned
// address.
size_t skew = section_base_ & (AddressSize() - 1);
// Now find the offset from that aligned address to buffer.
size_t offset = skew + (buffer - buffer_base_);
// Round up to the next boundary.
size_t aligned = (offset + AddressSize() - 1) & -AddressSize();
// Convert back to a pointer.
const char *aligned_buffer = buffer_base_ + (aligned - skew);
// Finally, store the length and actually fetch the pointer.
*len = aligned_buffer - buffer + AddressSize();
return ReadAddress(aligned_buffer);
}
// Extract the value first, ignoring whether it's a pointer or an
// offset relative to some base.
uint64 offset;
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
// DW_EH_PE_absptr is weird, as it is used as a meaningful value for
// both the high and low nybble of encoding bytes. When it appears in
// the high nybble, it means that the pointer is absolute, not an
// offset from some base address. When it appears in the low nybble,
// as here, it means that the pointer is stored as a normal
// machine-sized and machine-signed address. A low nybble of
// DW_EH_PE_absptr does not imply that the pointer is absolute; it is
// correct for us to treat the value as an offset from a base address
// if the upper nybble is not DW_EH_PE_absptr.
offset = ReadAddress(buffer);
*len = AddressSize();
break;
case DW_EH_PE_uleb128:
offset = ReadUnsignedLEB128(buffer, len);
break;
case DW_EH_PE_udata2:
offset = ReadTwoBytes(buffer);
*len = 2;
break;
case DW_EH_PE_udata4:
offset = ReadFourBytes(buffer);
*len = 4;
break;
case DW_EH_PE_udata8:
offset = ReadEightBytes(buffer);
*len = 8;
break;
case DW_EH_PE_sleb128:
offset = ReadSignedLEB128(buffer, len);
break;
case DW_EH_PE_sdata2:
offset = ReadTwoBytes(buffer);
// Sign-extend from 16 bits.
offset = (offset ^ 0x8000) - 0x8000;
*len = 2;
break;
case DW_EH_PE_sdata4:
offset = ReadFourBytes(buffer);
// Sign-extend from 32 bits.
offset = (offset ^ 0x80000000ULL) - 0x80000000ULL;
*len = 4;
break;
case DW_EH_PE_sdata8:
// No need to sign-extend; this is the full width of our type.
offset = ReadEightBytes(buffer);
*len = 8;
break;
default:
abort();
}
// Find the appropriate base address.
uint64 base;
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
base = 0;
break;
case DW_EH_PE_pcrel:
assert(have_section_base_);
base = section_base_ + (buffer - buffer_base_);
break;
case DW_EH_PE_textrel:
assert(have_text_base_);
base = text_base_;
break;
case DW_EH_PE_datarel:
assert(have_data_base_);
base = data_base_;
break;
case DW_EH_PE_funcrel:
assert(have_function_base_);
base = function_base_;
break;
default:
abort();
}
uint64 pointer = base + offset;
// Remove inappropriate upper bits.
if (AddressSize() == 4)
pointer = pointer & 0xffffffff;
else
assert(AddressSize() == sizeof(uint64));
return pointer;
}
} // namespace dwarf2reader

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright 2006 Google Inc. All Rights Reserved.
// -*- mode: C++ -*-
// 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
@ -31,6 +33,7 @@
#include <string>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
namespace dwarf2reader {
@ -41,69 +44,238 @@ enum Endianness {
ENDIANNESS_LITTLE
};
// Class that knows how to read both big endian and little endian
// numbers, for use in DWARF2/3 reader.
// Takes an endianness argument.
// To read addresses and offsets, SetAddressSize and SetOffsetSize
// must be called first.
// A ByteReader knows how to read single- and multi-byte values of
// various endiannesses, sizes, and encodings, as used in DWARF
// debugging information and Linux C++ exception handling data.
class ByteReader {
public:
explicit ByteReader(enum Endianness endian);
// Construct a ByteReader capable of reading one-, two-, four-, and
// eight-byte values according to ENDIANNESS, absolute machine-sized
// addresses, DWARF-style "initial length" values, signed and
// unsigned LEB128 numbers, and Linux C++ exception handling data's
// encoded pointers.
explicit ByteReader(enum Endianness endianness);
virtual ~ByteReader();
// Set the address size to SIZE, which sets up the ReadAddress member
// so that it works.
void SetAddressSize(uint8 size);
// Set the offset size to SIZE, which sets up the ReadOffset member
// so that it works.
void SetOffsetSize(uint8 size);
// Return the current offset size
uint8 OffsetSize() const { return offset_size_; }
// Return the current address size
uint8 AddressSize() const { return address_size_; }
// Read a single byte from BUFFER and return it as an unsigned 8 bit
// number.
uint8 ReadOneByte(const char* buffer) const;
// Read two bytes from BUFFER and return it as an unsigned 16 bit
// number.
// Read two bytes from BUFFER and return them as an unsigned 16 bit
// number, using this ByteReader's endianness.
uint16 ReadTwoBytes(const char* buffer) const;
// Read four bytes from BUFFER and return it as an unsigned 32 bit
// number. This function returns a uint64 so that it is compatible
// with ReadAddress and ReadOffset. The number it returns will
// never be outside the range of an unsigned 32 bit integer.
// Read four bytes from BUFFER and return them as an unsigned 32 bit
// number, using this ByteReader's endianness. This function returns
// a uint64 so that it is compatible with ReadAddress and
// ReadOffset. The number it returns will never be outside the range
// of an unsigned 32 bit integer.
uint64 ReadFourBytes(const char* buffer) const;
// Read eight bytes from BUFFER and return it as an unsigned 64 bit
// number
// Read eight bytes from BUFFER and return them as an unsigned 64
// bit number, using this ByteReader's endianness.
uint64 ReadEightBytes(const char* buffer) const;
// Read an unsigned LEB128 (Little Endian Base 128) number from
// BUFFER and return it as an unsigned 64 bit integer. LEN is set
// to the length read. Everybody seems to reinvent LEB128 as a
// variable size integer encoding, DWARF has had it for a long time.
// BUFFER and return it as an unsigned 64 bit integer. Set LEN to
// the number of bytes read.
//
// The unsigned LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between 0 and 0x7f, then its unsigned LEB128
// representation is a single byte whose value is N.
//
// - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
// 0x80, followed by the unsigned LEB128 representation of N /
// 128, rounded towards negative infinity.
//
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const;
// Read a signed LEB128 number from BUFFER and return it as an
// signed 64 bit integer. LEN is set to the length read.
// signed 64 bit integer. Set LEN to the number of bytes read.
//
// The signed LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between -0x40 and 0x3f, then its signed LEB128
// representation is a single byte whose value is N in two's
// complement.
//
// - Otherwise, its signed LEB128 representation is (N & 0x7f) |
// 0x80, followed by the signed LEB128 representation of N / 128,
// rounded towards negative infinity.
//
// In other words, we break VALUE into groups of seven bits, put
// them in little-endian order, and then write them as eight-bit
// bytes with the high bit on all but the last.
int64 ReadSignedLEB128(const char* buffer, size_t* len) const;
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 define offsets as either 4 or 8 bytes,
// generally depending on the amount of DWARF2/3 info present.
uint64 ReadOffset(const char* buffer) const;
// Indicate that addresses on this architecture are SIZE bytes long. SIZE
// must be either 4 or 8. (DWARF allows addresses to be any number of
// bytes in length from 1 to 255, but we only support 32- and 64-bit
// addresses at the moment.) You must call this before using the
// ReadAddress member function.
//
// For data in a .debug_info section, or something that .debug_info
// refers to like line number or macro data, the compilation unit
// header's address_size field indicates the address size to use. Call
// frame information doesn't indicate its address size (a shortcoming of
// the spec); you must supply the appropriate size based on the
// architecture of the target machine.
void SetAddressSize(uint8 size);
// Return the current address size, in bytes. This is either 4,
// indicating 32-bit addresses, or 8, indicating 64-bit addresses.
uint8 AddressSize() const { return address_size_; }
// Read an address from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 allow addresses to be any size from 0-255
// bytes currently. Internally we support 4 and 8 byte addresses,
// and will CHECK on anything else.
// integer, respecting this ByteReader's endianness and address size. You
// must call SetAddressSize before calling this function.
uint64 ReadAddress(const char* buffer) const;
// DWARF actually defines two slightly different formats: 32-bit DWARF
// and 64-bit DWARF. This is *not* related to the size of registers or
// addresses on the target machine; it refers only to the size of section
// offsets and data lengths appearing in the DWARF data. One only needs
// 64-bit DWARF when the debugging data itself is larger than 4GiB.
// 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the
// debugging data itself is very large.
//
// DWARF information identifies itself as 32-bit or 64-bit DWARF: each
// compilation unit and call frame information entry begins with an
// "initial length" field, which, in addition to giving the length of the
// data, also indicates the size of section offsets and lengths appearing
// in that data. The ReadInitialLength member function, below, reads an
// initial length and sets the ByteReader's offset size as a side effect.
// Thus, in the normal process of reading DWARF data, the appropriate
// offset size is set automatically. So, you should only need to call
// SetOffsetSize if you are using the same ByteReader to jump from the
// midst of one block of DWARF data into another.
// Read a DWARF "initial length" field from START, and return it as
// an unsigned 64 bit integer, respecting this ByteReader's
// endianness. Set *LEN to the length of the initial length in
// bytes, either four or twelve. As a side effect, set this
// ByteReader's offset size to either 4 (if we see a 32-bit DWARF
// initial length) or 8 (if we see a 64-bit DWARF initial length).
//
// A DWARF initial length is either:
//
// - a byte count stored as an unsigned 32-bit value less than
// 0xffffff00, indicating that the data whose length is being
// measured uses the 32-bit DWARF format, or
//
// - The 32-bit value 0xffffffff, followed by a 64-bit byte count,
// indicating that the data whose length is being measured uses
// the 64-bit DWARF format.
uint64 ReadInitialLength(const char* start, size_t* len);
// Read an offset from BUFFER and return it as an unsigned 64 bit
// integer, respecting the ByteReader's endianness. In 32-bit DWARF, the
// offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes
// long. You must call ReadInitialLength or SetOffsetSize before calling
// this function; see the comments above for details.
uint64 ReadOffset(const char* buffer) const;
// Return the current offset size, in bytes.
// A return value of 4 indicates that we are reading 32-bit DWARF.
// A return value of 8 indicates that we are reading 64-bit DWARF.
uint8 OffsetSize() const { return offset_size_; }
// Indicate that section offsets and lengths are SIZE bytes long. SIZE
// must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF).
// Usually, you should not call this function yourself; instead, let a
// call to ReadInitialLength establish the data's offset size
// automatically.
void SetOffsetSize(uint8 size);
// The Linux C++ ABI uses a variant of DWARF call frame information
// for exception handling. This data is included in the program's
// address space as the ".eh_frame" section, and intepreted at
// runtime to walk the stack, find exception handlers, and run
// cleanup code. The format is mostly the same as DWARF CFI, with
// some adjustments made to provide the additional
// exception-handling data, and to make the data easier to work with
// in memory --- for example, to allow it to be placed in read-only
// memory even when describing position-independent code.
//
// In particular, exception handling data can select a number of
// different encodings for pointers that appear in the data, as
// described by the DwarfPointerEncoding enum. There are actually
// four axes(!) to the encoding:
//
// - The pointer size: pointers can be 2, 4, or 8 bytes long, or use
// the DWARF LEB128 encoding.
//
// - The pointer's signedness: pointers can be signed or unsigned.
//
// - The pointer's base address: the data stored in the exception
// handling data can be the actual address (that is, an absolute
// pointer), or relative to one of a number of different base
// addreses --- including that of the encoded pointer itself, for
// a form of "pc-relative" addressing.
//
// - The pointer may be indirect: it may be the address where the
// true pointer is stored. (This is used to refer to things via
// global offset table entries, program linkage table entries, or
// other tricks used in position-independent code.)
//
// There are also two options that fall outside that matrix
// altogether: the pointer may be omitted, or it may have padding to
// align it on an appropriate address boundary. (That last option
// may seem like it should be just another axis, but it is not.)
// Indicate that the exception handling data is loaded starting at
// SECTION_BASE, and that the start of its buffer in our own memory
// is BUFFER_BASE. This allows us to find the address that a given
// byte in our buffer would have when loaded into the program the
// data describes. We need this to resolve DW_EH_PE_pcrel pointers.
void SetCFIDataBase(uint64 section_base, const char *buffer_base);
// Indicate that the base address of the program's ".text" section
// is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers.
void SetTextBase(uint64 text_base);
// Indicate that the base address for DW_EH_PE_datarel pointers is
// DATA_BASE. The proper value depends on the ABI; it is usually the
// address of the global offset table, held in a designated register in
// position-independent code. You will need to look at the startup code
// for the target system to be sure. I tried; my eyes bled.
void SetDataBase(uint64 data_base);
// Indicate that the base address for the FDE we are processing is
// FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel
// pointers. (This encoding does not seem to be used by the GNU
// toolchain.)
void SetFunctionBase(uint64 function_base);
// Indicate that we are no longer processing any FDE, so any use of
// a DW_EH_PE_funcrel encoding is an error.
void ClearFunctionBase();
// Return true if ENCODING is a valid pointer encoding.
bool ValidEncoding(DwarfPointerEncoding encoding) const;
// Return true if we have all the information we need to read a
// pointer that uses ENCODING. This checks that the appropriate
// SetFooBase function for ENCODING has been called.
bool UsableEncoding(DwarfPointerEncoding encoding) const;
// Read an encoded pointer from BUFFER using ENCODING; return the
// absolute address it represents, and set *LEN to the pointer's
// length in bytes, including any padding for aligned pointers.
//
// This function calls 'abort' if ENCODING is invalid or refers to a
// base address this reader hasn't been given, so you should check
// with ValidEncoding and UsableEncoding first if you would rather
// die in a more helpful way.
uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding,
size_t *len) const;
private:
// Function pointer type for our address and offset readers.
@ -125,6 +297,12 @@ class ByteReader {
Endianness endian_;
uint8 address_size_;
uint8 offset_size_;
// Base addresses for Linux C++ exception handling data's encoded pointers.
bool have_section_base_, have_text_base_, have_data_base_;
bool have_function_base_;
uint64 section_base_, text_base_, data_base_, function_base_;
const char *buffer_base_;
};
} // namespace dwarf2reader

Просмотреть файл

@ -0,0 +1,697 @@
// 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>
// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/cfi_assembler.h"
using dwarf2reader::ByteReader;
using dwarf2reader::DwarfPointerEncoding;
using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using google_breakpad::CFISection;
using google_breakpad::TestAssembler::Label;
using google_breakpad::TestAssembler::kBigEndian;
using google_breakpad::TestAssembler::kLittleEndian;
using google_breakpad::TestAssembler::Section;
using std::string;
using testing::Test;
struct ReaderFixture {
string contents;
size_t pointer_size;
};
class Reader: public ReaderFixture, public Test { };
class ReaderDeathTest: public ReaderFixture, public Test { };
TEST_F(Reader, SimpleConstructor) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
CFISection section(kBigEndian, 4);
section
.D8(0xc0)
.D16(0xcf0d)
.D32(0x96fdd219)
.D64(0xbbf55fef0825f117ULL)
.ULEB128(0xa0927048ba8121afULL)
.LEB128(-0x4f337badf4483f83LL)
.D32(0xfec319c9);
ASSERT_TRUE(section.GetContents(&contents));
const char *data = contents.data();
EXPECT_EQ(0xc0U, reader.ReadOneByte(data));
EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1));
EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3));
EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7));
size_t leb128_size;
EXPECT_EQ(0xa0927048ba8121afULL,
reader.ReadUnsignedLEB128(data + 15, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(-0x4f337badf4483f83LL,
reader.ReadSignedLEB128(data + 25, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35));
}
TEST_F(Reader, ValidEncodings) {
ByteReader reader(ENDIANNESS_LITTLE);
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0)));
}
TEST_F(ReaderDeathTest, DW_EH_PE_omit) {
static const char data[1] = { 42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit,
&pointer_size),
"encoding != DW_EH_PE_omit");
}
TEST_F(Reader, DW_EH_PE_absptr4) {
static const char data[] = { 0x27, 0x57, 0xea, 0x40 };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x40ea5727U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_absptr8) {
static const char data[] = {
0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x010598c240ea5727ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_uleb128) {
static const char data[] = { 0x81, 0x84, 0x4c };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x130201U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata2) {
static const char data[] = { 0xf4, 0x8d };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(0xf48dU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata4) {
static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
EXPECT_EQ(0xa5628f8b,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr8) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x8fed199f69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr4) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sleb128) {
static const char data[] = { 0x42, 0xff, 0xfb, 0x73 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(-0x030201U & 0xffffffff,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata2) {
static const char data[] = { 0xb9, 0xbf };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffffffbfb9ULL,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata4) {
static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffadc2b8f2ULL,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata8) {
static const char data[] = {
0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x87269b0ce0795766ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_pcrel) {
static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel
| dwarf2reader::DW_EH_PE_absptr);
reader.SetCFIDataBase(0x89951377, data);
EXPECT_EQ(0x89951377 + 3 + 0x14c8c402,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_textrel) {
static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
reader.SetTextBase(0xb91beaf0);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel
| dwarf2reader::DW_EH_PE_sdata2);
EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_datarel) {
static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
reader.SetDataBase(0xbef308bd25ce74f0ULL);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel
| dwarf2reader::DW_EH_PE_sleb128);
EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL,
reader.ReadEncodedPointer(data + 2, encoding, &pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_funcrel) {
static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
reader.SetFunctionBase(0x823c3520);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel
| dwarf2reader::DW_EH_PE_udata2);
EXPECT_EQ(0x823c3520 + 0xd148,
reader.ReadEncodedPointer(data + 5, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST(UsableBase, CFI) {
static const char data[1] = { 0x42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetCFIDataBase(0xb31cbd20, data);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Text) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetTextBase(0xa899ccb9);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Data) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetDataBase(0xf7b10bcd);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Function) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, ClearFunction) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
reader.ClearFunctionBase();
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
struct AlignedFixture {
AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); }
static const char data[10];
ByteReader reader;
size_t pointer_size;
};
const char AlignedFixture::data[10] = {
0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b
};
class Aligned: public AlignedFixture, public Test { };
TEST_F(Aligned, DW_EH_PE_aligned0) {
reader.SetCFIDataBase(0xb440305c, data);
EXPECT_EQ(0xfe6e93d8U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned1) {
reader.SetCFIDataBase(0xb440305d, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned2) {
reader.SetCFIDataBase(0xb440305e, data);
EXPECT_EQ(0x93d834d5U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned3) {
reader.SetCFIDataBase(0xb440305f, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned11) {
reader.SetCFIDataBase(0xb4403061, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned30) {
reader.SetCFIDataBase(0xb4403063, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned23) {
reader.SetCFIDataBase(0xb4403062, data);
EXPECT_EQ(0x1cd3ac2bU,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned03) {
reader.SetCFIDataBase(0xb4403064, data);
EXPECT_EQ(0x34d51cd3U,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}

Просмотреть файл

@ -0,0 +1,196 @@
// 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>
// cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
// See cfi_assembler.h for details.
#include <cassert>
#include <stdlib.h>
#include "common/dwarf/cfi_assembler.h"
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
u_int8_t version,
const string &augmentation,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = false;
if (dwarf64) {
D32(0xffffffff);
D64(entry_length_->length);
entry_length_->start = Here();
// Write the CIE distinguished value. In .debug_frame sections, it's
// ~0; in .eh_frame sections, it's zero.
D64(eh_frame_ ? 0 : ~(u_int64_t)0);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
// Write the CIE distinguished value. In .debug_frame sections, it's
// ~0; in .eh_frame sections, it's zero.
D32(eh_frame_ ? 0 : ~(u_int32_t)0);
}
D8(version);
AppendCString(augmentation);
ULEB128(code_alignment_factor);
LEB128(data_alignment_factor);
if (version == 1)
D8(return_address_register);
else
ULEB128(return_address_register);
return *this;
}
CFISection &CFISection::FDEHeader(Label cie_pointer,
u_int64_t initial_location,
u_int64_t address_range,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = true;
fde_start_address_ = initial_location;
if (dwarf64) {
D32(0xffffffff);
D64(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D64(Here() - cie_pointer);
else
D64(cie_pointer);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D32(Here() - cie_pointer);
else
D32(cie_pointer);
}
EncodedPointer(initial_location);
// The FDE length in an .eh_frame section uses the same encoding as the
// initial location, but ignores the base address (selected by the upper
// nybble of the encoding), as it's a length, not an address that can be
// made relative.
EncodedPointer(address_range,
DwarfPointerEncoding(pointer_encoding_ & 0x0f));
return *this;
}
CFISection &CFISection::FinishEntry() {
assert(entry_length_);
Align(address_size_, dwarf2reader::DW_CFA_nop);
entry_length_->length = Here() - entry_length_->start;
delete entry_length_;
entry_length_ = NULL;
in_fde_ = false;
return *this;
}
CFISection &CFISection::EncodedPointer(u_int64_t address,
DwarfPointerEncoding encoding,
const EncodedPointerBases &bases) {
// Omitted data is extremely easy to emit.
if (encoding == dwarf2reader::DW_EH_PE_omit)
return *this;
// If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume
// that ADDRESS is the address at which the pointer is stored --- in
// other words, that bit has no effect on how we write the pointer.
encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect);
// Find the base address to which this pointer is relative. The upper
// nybble of the encoding specifies this.
u_int64_t base;
switch (encoding & 0xf0) {
case dwarf2reader::DW_EH_PE_absptr: base = 0; break;
case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break;
case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break;
case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break;
case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break;
case dwarf2reader::DW_EH_PE_aligned: base = 0; break;
default: abort();
};
// Make ADDRESS relative. Yes, this is appropriate even for "absptr"
// values; see gcc/unwind-pe.h.
address -= base;
// Align the pointer, if required.
if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned)
Align(AddressSize());
// Append ADDRESS to this section in the appropriate form. For the
// fixed-width forms, we don't need to differentiate between signed and
// unsigned encodings, because ADDRESS has already been extended to 64
// bits before it was passed to us.
switch (encoding & 0x0f) {
case dwarf2reader::DW_EH_PE_absptr:
Address(address);
break;
case dwarf2reader::DW_EH_PE_uleb128:
ULEB128(address);
break;
case dwarf2reader::DW_EH_PE_sleb128:
LEB128(address);
break;
case dwarf2reader::DW_EH_PE_udata2:
case dwarf2reader::DW_EH_PE_sdata2:
D16(address);
break;
case dwarf2reader::DW_EH_PE_udata4:
case dwarf2reader::DW_EH_PE_sdata4:
D32(address);
break;
case dwarf2reader::DW_EH_PE_udata8:
case dwarf2reader::DW_EH_PE_sdata8:
D64(address);
break;
default:
abort();
}
return *this;
};
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,256 @@
// -*- mode: C++ -*-
// 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>
// cfi-assembler.h: Define CFISection, a class for creating properly
// (and improperly) formatted DWARF CFI data for unit tests.
#ifndef PROCESSOR_CFI_ASSEMBLER_H_
#define PROCESSOR_CFI_ASSEMBLER_H_
#include <string>
#include "common/dwarf/dwarf2enums.h"
#include "google_breakpad/common/breakpad_types.h"
#include "processor/test_assembler.h"
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
using google_breakpad::TestAssembler::Endianness;
using google_breakpad::TestAssembler::Label;
using google_breakpad::TestAssembler::Section;
using std::string;
class CFISection: public Section {
public:
// CFI augmentation strings beginning with 'z', defined by the
// Linux/IA-64 C++ ABI, can specify interesting encodings for
// addresses appearing in FDE headers and call frame instructions (and
// for additional fields whose presence the augmentation string
// specifies). In particular, pointers can be specified to be relative
// to various base address: the start of the .text section, the
// location holding the address itself, and so on. These allow the
// frame data to be position-independent even when they live in
// write-protected pages. These variants are specified at the
// following two URLs:
//
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
//
// CFISection leaves the production of well-formed 'z'-augmented CIEs and
// FDEs to the user, but does provide EncodedPointer, to emit
// properly-encoded addresses for a given pointer encoding.
// EncodedPointer uses an instance of this structure to find the base
// addresses it should use; you can establish a default for all encoded
// pointers appended to this section with SetEncodedPointerBases.
struct EncodedPointerBases {
EncodedPointerBases() : cfi(), text(), data() { }
// The starting address of this CFI section in memory, for
// DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
// that has is loaded into the program's address space.
u_int64_t cfi;
// The starting address of this file's .text section, for DW_EH_PE_textrel.
u_int64_t text;
// The starting address of this file's .got or .eh_frame_hdr section,
// for DW_EH_PE_datarel.
u_int64_t data;
};
// Create a CFISection whose endianness is ENDIANNESS, and where
// machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is
// true, use the .eh_frame format, as described by the Linux
// Standards Base Core Specification, instead of the DWARF CFI
// format.
CFISection(Endianness endianness, size_t address_size,
bool eh_frame = false)
: Section(endianness), address_size_(address_size), eh_frame_(eh_frame),
pointer_encoding_(dwarf2reader::DW_EH_PE_absptr),
encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) {
// The 'start', 'Here', and 'Mark' members of a CFISection all refer
// to section offsets.
start() = 0;
}
// Return this CFISection's address size.
size_t AddressSize() const { return address_size_; }
// Return true if this CFISection uses the .eh_frame format, or
// false if it contains ordinary DWARF CFI data.
bool ContainsEHFrame() const { return eh_frame_; }
// Use ENCODING for pointers in calls to FDEHeader and EncodedPointer.
void SetPointerEncoding(DwarfPointerEncoding encoding) {
pointer_encoding_ = encoding;
}
// Use the addresses in BASES as the base addresses for encoded
// pointers in subsequent calls to FDEHeader or EncodedPointer.
// This function makes a copy of BASES.
void SetEncodedPointerBases(const EncodedPointerBases &bases) {
encoded_pointer_bases_ = bases;
}
// Append a Common Information Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// Before calling this function, you will typically want to use Mark
// or Here to make a label to pass to FDEHeader that refers to this
// CIE's position in the section.
CFISection &CIEHeader(u_int64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
u_int8_t version = 3,
const string &augmentation = "",
bool dwarf64 = false);
// Append a Frame Description Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// This function doesn't support entries that are longer than
// 0xffffff00 bytes. (The "initial length" is always a 32-bit
// value.) Nor does it support .debug_frame sections longer than
// 0xffffff00 bytes.
CFISection &FDEHeader(Label cie_pointer,
u_int64_t initial_location,
u_int64_t address_range,
bool dwarf64 = false);
// Note the current position as the end of the last CIE or FDE we
// started, after padding with DW_CFA_nops for alignment. This
// defines the label representing the entry's length, cited in the
// entry's header. Return a reference to this section.
CFISection &FinishEntry();
// Append the contents of BLOCK as a DW_FORM_block value: an
// unsigned LEB128 length, followed by that many bytes of data.
CFISection &Block(const string &block) {
ULEB128(block.size());
Append(block);
return *this;
}
// Append ADDRESS to this section, in the appropriate size and
// endianness. Return a reference to this section.
CFISection &Address(u_int64_t address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
CFISection &Address(Label address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
// Append ADDRESS to this section, using ENCODING and BASES. ENCODING
// defaults to this section's default encoding, established by
// SetPointerEncoding. BASES defaults to this section's bases, set by
// SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the
// encoding, assume that ADDRESS is where the true address is stored.
// Return a reference to this section.
//
// (C++ doesn't let me use default arguments here, because I want to
// refer to members of *this in the default argument expression.)
CFISection &EncodedPointer(u_int64_t address) {
return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
}
CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding) {
return EncodedPointer(address, encoding, encoded_pointer_bases_);
}
CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding,
const EncodedPointerBases &bases);
// Restate some member functions, to keep chaining working nicely.
CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
CFISection &D8(u_int8_t v) { Section::D8(v); return *this; }
CFISection &D16(u_int16_t v) { Section::D16(v); return *this; }
CFISection &D16(Label v) { Section::D16(v); return *this; }
CFISection &D32(u_int32_t v) { Section::D32(v); return *this; }
CFISection &D32(const Label &v) { Section::D32(v); return *this; }
CFISection &D64(u_int64_t v) { Section::D64(v); return *this; }
CFISection &D64(const Label &v) { Section::D64(v); return *this; }
CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
CFISection &ULEB128(u_int64_t v) { Section::ULEB128(v); return *this; }
private:
// A length value that we've appended to the section, but is not yet
// known. LENGTH is the appended value; START is a label referring
// to the start of the data whose length was cited.
struct PendingLength {
Label length;
Label start;
};
// The size of a machine address for the data in this section.
size_t address_size_;
// If true, we are generating a Linux .eh_frame section, instead of
// a standard DWARF .debug_frame section.
bool eh_frame_;
// The encoding to use for FDE pointers.
DwarfPointerEncoding pointer_encoding_;
// The base addresses to use when emitting encoded pointers.
EncodedPointerBases encoded_pointer_bases_;
// The length value for the current entry.
//
// Oddly, this must be dynamically allocated. Labels never get new
// values; they only acquire constraints on the value they already
// have, or assert if you assign them something incompatible. So
// each header needs truly fresh Label objects to cite in their
// headers and track their positions. The alternative is explicit
// destructor invocation and a placement new. Ick.
PendingLength *entry_length_;
// True if we are currently emitting an FDE --- that is, we have
// called FDEHeader but have not yet called FinishEntry.
bool in_fde_;
// If in_fde_ is true, this is its starting address. We use this for
// emitting DW_EH_PE_funcrel pointers.
u_int64_t fde_start_address_;
};
} // namespace google_breakpad
#endif // PROCESSOR_CFI_ASSEMBLER_H_

Просмотреть файл

@ -0,0 +1,186 @@
// 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>
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
// See dwarf2diehandler.h for details.
#include <cassert>
#include "common/dwarf/dwarf2diehandler.h"
namespace dwarf2reader {
DIEDispatcher::~DIEDispatcher() {
while (!die_handlers_.empty()) {
HandlerStack &entry = die_handlers_.top();
if (entry.handler_ != root_handler_)
delete entry.handler_;
die_handlers_.pop();
}
}
bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version) {
return root_handler_->StartCompilationUnit(offset, address_size,
offset_size, cu_length,
dwarf_version);
}
bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) {
// The stack entry for the parent of this DIE, if there is one.
HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top();
// Does this call indicate that we're done receiving the parent's
// attributes' values? If so, call its EndAttributes member function.
if (parent && parent->handler_ && !parent->reported_attributes_end_) {
parent->reported_attributes_end_ = true;
if (!parent->handler_->EndAttributes()) {
// Finish off this handler now. and edit *PARENT to indicate that
// we don't want to visit any of the children.
parent->handler_->Finish();
if (parent->handler_ != root_handler_)
delete parent->handler_;
parent->handler_ = NULL;
return false;
}
}
// Find a handler for this DIE.
DIEHandler *handler;
if (parent) {
if (parent->handler_)
// Ask the parent to find a handler.
handler = parent->handler_->FindChildHandler(offset, tag, attrs);
else
// No parent handler means we're not interested in any of our
// children.
handler = NULL;
} else {
// This is the root DIE. For a non-root DIE, the parent's handler
// decides whether to visit it, but the root DIE has no parent
// handler, so we have a special method on the root DIE handler
// itself to decide.
if (root_handler_->StartRootDIE(offset, tag, attrs))
handler = root_handler_;
else
handler = NULL;
}
// Push a handler stack entry for this new handler. As an
// optimization, we don't push NULL-handler entries on top of other
// NULL-handler entries; we just let the oldest such entry stand for
// the whole subtree.
if (handler || !parent || parent->handler_) {
HandlerStack entry;
entry.offset_ = offset;
entry.handler_ = handler;
entry.reported_attributes_end_ = false;
die_handlers_.push(entry);
}
return handler != NULL;
}
void DIEDispatcher::EndDIE(uint64 offset) {
assert(!die_handlers_.empty());
HandlerStack *entry = &die_handlers_.top();
if (entry->handler_) {
// This entry had better be the handler for this DIE.
assert(entry->offset_ == offset);
// If a DIE has no children, this EndDIE call indicates that we're
// done receiving its attributes' values.
if (!entry->reported_attributes_end_)
entry->handler_->EndAttributes(); // Ignore return value: no children.
entry->handler_->Finish();
if (entry->handler_ != root_handler_)
delete entry->handler_;
} else {
// If this DIE is within a tree we're ignoring, then don't pop the
// handler stack: that entry stands for the whole tree.
if (entry->offset_ != offset)
return;
}
die_handlers_.pop();
}
void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeUnsigned(attr, form, data);
}
void DIEDispatcher::ProcessAttributeSigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeSigned(attr, form, data);
}
void DIEDispatcher::ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeReference(attr, form, data);
}
void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeBuffer(attr, form, data, len);
}
void DIEDispatcher::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeString(attr, form, data);
}
} // namespace dwarf2reader

Просмотреть файл

@ -0,0 +1,358 @@
// -*- mode: c++ -*-
// 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>
// dwarf2reader::CompilationUnit is a simple and direct parser for
// DWARF data, but its handler interface is not convenient to use. In
// particular:
//
// - CompilationUnit calls Dwarf2Handler's member functions to report
// every attribute's value, regardless of what sort of DIE it is.
// As a result, the ProcessAttributeX functions end up looking like
// this:
//
// switch (parent_die_tag) {
// case DW_TAG_x:
// switch (attribute_name) {
// case DW_AT_y:
// handle attribute y of DIE type x
// ...
// } break;
// ...
// }
//
// In C++ it's much nicer to use virtual function dispatch to find
// the right code for a given case than to switch on the DIE tag
// like this.
//
// - Processing different kinds of DIEs requires different sets of
// data: lexical block DIEs have start and end addresses, but struct
// type DIEs don't. It would be nice to be able to have separate
// handler classes for separate kinds of DIEs, each with the members
// appropriate to its role, instead of having one handler class that
// needs to hold data for every DIE type.
//
// - There should be a separate instance of the appropriate handler
// class for each DIE, instead of a single object with tables
// tracking all the dies in the compilation unit.
//
// - It's not convenient to take some action after all a DIE's
// attributes have been seen, but before visiting any of its
// children. The only indication you have that a DIE's attribute
// list is complete is that you get either a StartDIE or an EndDIE
// call.
//
// - It's not convenient to make use of the tree structure of the
// DIEs. Skipping all the children of a given die requires
// maintaining state and returning false from StartDIE until we get
// an EndDIE call with the appropriate offset.
//
// This interface tries to take care of all that. (You're shocked, I'm sure.)
//
// Using the classes here, you provide an initial handler for the root
// DIE of the compilation unit. Each handler receives its DIE's
// attributes, and provides fresh handler objects for children of
// interest, if any. The three classes are:
//
// - DIEHandler: the base class for your DIE-type-specific handler
// classes.
//
// - RootDIEHandler: derived from DIEHandler, the base class for your
// root DIE handler class.
//
// - DIEDispatcher: derived from Dwarf2Handler, an instance of this
// invokes your DIE-type-specific handler objects.
//
// In detail:
//
// - Define handler classes specialized for the DIE types you're
// interested in. These handler classes must inherit from
// DIEHandler. Thus:
//
// class My_DW_TAG_X_Handler: public DIEHandler { ... };
// class My_DW_TAG_Y_Handler: public DIEHandler { ... };
//
// DIEHandler subclasses needn't correspond exactly to single DIE
// types, as shown here; the point is that you can have several
// different classes appropriate to different kinds of DIEs.
//
// - In particular, define a handler class for the compilation
// unit's root DIE, that inherits from RootDIEHandler:
//
// class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... };
//
// RootDIEHandler inherits from DIEHandler, adding a few additional
// member functions for examining the compilation unit as a whole,
// and other quirks of rootness.
//
// - Then, create a DIEDispatcher instance, passing it an instance of
// your root DIE handler class, and use that DIEDispatcher as the
// dwarf2reader::CompilationUnit's handler:
//
// My_DW_TAG_compile_unit_Handler root_die_handler(...);
// DIEDispatcher die_dispatcher(&root_die_handler);
// CompilationUnit reader(sections, offset, bytereader, &die_dispatcher);
//
// Here, 'die_dispatcher' acts as a shim between 'reader' and the
// various DIE-specific handlers you have defined.
//
// - When you call reader.Start(), die_dispatcher behaves as follows,
// starting with your root die handler and the compilation unit's
// root DIE:
//
// - It calls the handler's ProcessAttributeX member functions for
// each of the DIE's attributes.
//
// - It calls the handler's EndAttributes member function. This
// should return true if any of the DIE's children should be
// visited, in which case:
//
// - For each of the DIE's children, die_dispatcher calls the
// DIE's handler's FindChildHandler member function. If that
// returns a pointer to a DIEHandler instance, then
// die_dispatcher uses that handler to process the child, using
// this procedure recursively. Alternatively, if
// FindChildHandler returns NULL, die_dispatcher ignores that
// child and its descendants.
//
// - When die_dispatcher has finished processing all the DIE's
// children, it invokes the handler's Finish() member function,
// and destroys the handler. (As a special case, it doesn't
// destroy the root DIE handler.)
//
// This allows the code for handling a particular kind of DIE to be
// gathered together in a single class, makes it easy to skip all the
// children or individual children of a particular DIE, and provides
// appropriate parental context for each die.
#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
#include <stack>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/dwarf2reader.h"
namespace dwarf2reader {
// A base class for handlers for specific DIE types. The series of
// calls made on a DIE handler is as follows:
//
// - for each attribute of the DIE:
// - ProcessAttributeX()
// - EndAttributes()
// - if that returned true, then for each child:
// - FindChildHandler()
// - if that returns a non-NULL pointer to a new handler:
// - recurse, with the new handler and the child die
// - Finish()
// - destruction
class DIEHandler {
public:
DIEHandler() { }
virtual ~DIEHandler() { }
// When we visit a DIE, we first use these member functions to
// report the DIE's attributes and their values. These have the
// same restrictions as the corresponding member functions of
// dwarf2reader::Dwarf2Handler.
//
// Since DWARF does not specify in what order attributes must
// appear, avoid making decisions in these functions that would be
// affected by the presence of other attributes. The EndAttributes
// function is a more appropriate place for such work, as all the
// DIE's attributes have been seen at that point.
//
// The default definitions ignore the values they are passed.
virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) { }
virtual void ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) { }
virtual void ProcessAttributeReference(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) { }
virtual void ProcessAttributeBuffer(enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len) { }
virtual void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) { }
// Once we have reported all the DIE's attributes' values, we call
// this member function. If it returns false, we skip all the DIE's
// children. If it returns true, we call FindChildHandler on each
// child. If that returns a handler object, we use that to visit
// the child; otherwise, we skip the child.
//
// This is a good place to make decisions that depend on more than
// one attribute. DWARF does not specify in what order attributes
// must appear, so only when the EndAttributes function is called
// does the handler have a complete picture of the DIE's attributes.
//
// The default definition elects to ignore the DIE's children.
// You'll need to override this if you override FindChildHandler,
// but at least the default behavior isn't to pass the children to
// FindChildHandler, which then ignores them all.
virtual bool EndAttributes() { return false; }
// If EndAttributes returns true to indicate that some of the DIE's
// children might be of interest, then we apply this function to
// each of the DIE's children. If it returns a handler object, then
// we use that to visit the child DIE. If it returns NULL, we skip
// that child DIE (and all its descendants).
//
// OFFSET is the offset of the child; TAG indicates what kind of DIE
// it is; and ATTRS is the list of attributes the DIE will have, and
// their forms (their values are not provided).
//
// The default definition skips all children.
virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs) {
return NULL;
}
// When we are done processing a DIE, we call this member function.
// This happens after the EndAttributes call, all FindChildHandler
// calls (if any), and all operations on the children themselves (if
// any). We call Finish on every handler --- even if EndAttributes
// returns false.
virtual void Finish() { };
};
// A subclass of DIEHandler, with additional kludges for handling the
// compilation unit's root die.
class RootDIEHandler: public DIEHandler {
public:
RootDIEHandler() { }
virtual ~RootDIEHandler() { }
// We pass the values reported via Dwarf2Handler::StartCompilationUnit
// to this member function, and skip the entire compilation unit if it
// returns false. So the root DIE handler is actually also
// responsible for handling the compilation unit metadata.
// The default definition always visits the compilation unit.
virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version) { return true; }
// For the root DIE handler only, we pass the offset, tag and
// attributes of the compilation unit's root DIE. This is the only
// way the root DIE handler can find the root DIE's tag. If this
// function returns true, we will visit the root DIE using the usual
// DIEHandler methods; otherwise, we skip the entire compilation
// unit.
//
// The default definition elects to visit the root DIE.
virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) { return true; }
};
class DIEDispatcher: public Dwarf2Handler {
public:
// Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for
// the compilation unit's root die, as described for the DIEHandler
// class.
DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { }
// Destroying a DIEDispatcher destroys all active handler objects
// except the root handler.
~DIEDispatcher();
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
bool StartDIE(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs);
void ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeSigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data);
void ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len);
void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data);
void EndDIE(uint64 offset);
private:
// The type of a handler stack entry. This includes some fields
// which don't really need to be on the stack --- they could just be
// single data members of DIEDispatcher --- but putting them here
// makes it easier to see that the code is correct.
struct HandlerStack {
// The offset of the DIE for this handler stack entry.
uint64 offset_;
// The handler object interested in this DIE's attributes and
// children. If NULL, we're not interested in either.
DIEHandler *handler_;
// Have we reported the end of this DIE's attributes to the handler?
bool reported_attributes_end_;
};
// Stack of DIE attribute handlers. At StartDIE(D), the top of the
// stack is the handler of D's parent, whom we may ask for a handler
// for D itself. At EndDIE(D), the top of the stack is D's handler.
// Special cases:
//
// - Before we've seen the compilation unit's root DIE, the stack is
// empty; we'll call root_handler_'s special member functions, and
// perhaps push root_handler_ on the stack to look at the root's
// immediate children.
//
// - When we decide to ignore a subtree, we only push an entry on
// the stack for the root of the tree being ignored, rather than
// pushing lots of stack entries with handler_ set to NULL.
stack<HandlerStack> die_handlers_;
// The root handler. We don't push it on die_handlers_ until we
// actually get the StartDIE call for the root.
RootDIEHandler *root_handler_;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__

Просмотреть файл

@ -0,0 +1,560 @@
// -*- mode: c++ -*-
// 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>
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
#include "breakpad_googletest_includes.h"
#include "common/dwarf/dwarf2diehandler.h"
using ::testing::_;
using ::testing::ContainerEq;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrEq;
using dwarf2reader::AttributeList;
using dwarf2reader::DIEDispatcher;
using dwarf2reader::DIEHandler;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfTag;
using dwarf2reader::RootDIEHandler;
class MockDIEHandler: public DIEHandler {
public:
MOCK_METHOD3(ProcessAttributeUnsigned,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD3(ProcessAttributeSigned,
void(DwarfAttribute, DwarfForm, int64));
MOCK_METHOD3(ProcessAttributeReference,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD4(ProcessAttributeBuffer,
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
const AttributeList &));
MOCK_METHOD0(Finish, void());
};
class MockRootDIEHandler: public RootDIEHandler {
public:
MOCK_METHOD3(ProcessAttributeUnsigned,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD3(ProcessAttributeSigned,
void(DwarfAttribute, DwarfForm, int64));
MOCK_METHOD3(ProcessAttributeReference,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD4(ProcessAttributeBuffer,
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
const AttributeList &));
MOCK_METHOD0(Finish, void());
MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
MOCK_METHOD3(StartRootDIE, bool(uint64, DwarfTag, const AttributeList &));
};
// If the handler elects to skip the compilation unit, the dispatcher
// should tell the reader so.
TEST(Dwarf2DIEHandler, SkipCompilationUnit) {
Sequence s;
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66))
.InSequence(s)
.WillOnce(Return(false));
EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66));
}
// If the handler elects to skip the root DIE, the dispatcher should
// tell the reader so.
TEST(Dwarf2DIEHandler, SkipRootDIE) {
Sequence s;
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList mock_attribute_list;
mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string));
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6,
ContainerEq(mock_attribute_list)))
.InSequence(s)
.WillOnce(Return(false));
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,
0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c));
EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6,
mock_attribute_list));
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
// If the handler elects to skip the root DIE's children, the
// dispatcher should tell the reader so --- and avoid deleting the
// root handler.
TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList mock_attribute_list;
{
InSequence s;
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0,
0x09f8bf0767f91675LL, 0xdb))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6,
ContainerEq(mock_attribute_list)))
.WillOnce(Return(true));
// Please don't tell me about my children.
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(false));
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL,
0x26, 0xa0,
0x09f8bf0767f91675LL, 0xdb));
EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6,
mock_attribute_list));
EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
(DwarfTag) 0xc3a17bba,
mock_attribute_list));
die_dispatcher.EndDIE(0x435150ceedccda18LL);
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
// The dispatcher should pass attribute values through to the die
// handler accurately.
TEST(Dwarf2DIEHandler, PassAttributeValues) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList mock_attribute_list;
mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string));
const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca,
0xcf, 0xa8, 0x84, 0xa7, 0x18 };
string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d";
// Set expectations.
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc,
0x2ecb4dc778a80f21LL, 0x66))
.WillOnce(Return(true));
// We'll like the root DIE.
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c,
ContainerEq(mock_attribute_list)))
.WillOnce(Return(true));
// Expect some attribute values.
EXPECT_CALL(mock_root_handler,
ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed,
(DwarfForm) 0x424f1468,
0xa592571997facda1ULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeSigned((DwarfAttribute) 0x43694dc9,
(DwarfForm) 0xf6f78901L,
0x92602a4e3bf1f446LL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeReference((DwarfAttribute) 0x4033e8cL,
(DwarfForm) 0xf66fbe0bL,
0x50fddef44734fdecULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af,
(DwarfForm) 0xe99a539a,
buffer, sizeof(buffer)))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeString((DwarfAttribute) 0x310ed065,
(DwarfForm) 0x15762fec,
StrEq(str)))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _))
.Times(0);
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
// Drive the dispatcher.
// Report the CU header.
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66));
// Report the root DIE.
EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL,
(DwarfTag) 0x9829445c,
mock_attribute_list));
// Report some attribute values.
die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x1cc0bfed,
(DwarfForm) 0x424f1468,
0xa592571997facda1ULL);
die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x43694dc9,
(DwarfForm) 0xf6f78901,
0x92602a4e3bf1f446LL);
die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x4033e8c,
(DwarfForm) 0xf66fbe0b,
0x50fddef44734fdecULL);
die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x25d7e0af,
(DwarfForm) 0xe99a539a,
buffer, sizeof(buffer));
die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x310ed065,
(DwarfForm) 0x15762fec,
str);
// Finish the root DIE (and thus the CU).
die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
}
TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
MockRootDIEHandler mock_root_handler;
MockDIEHandler *mock_child1_handler = new(MockDIEHandler);
MockDIEHandler *mock_child3_handler = new(MockDIEHandler);
DIEDispatcher die_dispatcher(&mock_root_handler);
AttributeList root_attribute_list;
root_attribute_list.push_back(make_pair((DwarfAttribute) 0xb01185df,
(DwarfForm) 0xbc97cee8));
AttributeList child1_attribute_list;
child1_attribute_list.push_back(make_pair((DwarfAttribute) 0x41014e43,
(DwarfForm) 0x63155f4c));
AttributeList grandchild1_attribute_list;
grandchild1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf72f823c,
(DwarfForm) 0x0ff6a201));
AttributeList greatgrandchild1_attribute_list;
greatgrandchild1_attribute_list
.push_back(make_pair((DwarfAttribute) 0xbe66e5f0, (DwarfForm) 0xb4b24ff7));
AttributeList child2_attribute_list;
child1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf22df14c,
(DwarfForm) 0x20676e7d));
AttributeList child3_attribute_list;
child3_attribute_list.push_back(make_pair((DwarfAttribute) 0xe8bf1201,
(DwarfForm) 0x53a5b7a8));
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
0x47dd3c764275a216LL, 0xa5))
.WillOnce(Return(true));
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59,
ContainerEq(root_attribute_list)))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
ProcessAttributeSigned((DwarfAttribute) 0xf779a642,
(DwarfForm) 0x2cb63027,
0x18e744661769d08fLL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
// First child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c,
ContainerEq(child1_attribute_list)))
.WillOnce(Return(mock_child1_handler));
{
EXPECT_CALL(*mock_child1_handler,
ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65,
(DwarfForm) 0xe4f64c41,
0x1b04e5444a55fe67LL))
.WillOnce(Return());
EXPECT_CALL(*mock_child1_handler, EndAttributes())
.WillOnce(Return(false));
// Skip first grandchild DIE and first great-grandchild DIE.
EXPECT_CALL(*mock_child1_handler, Finish())
.WillOnce(Return());
}
// Second child DIE. Root handler will decline to return a handler
// for this child.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b,
ContainerEq(child2_attribute_list)))
.WillOnce(Return((DIEHandler *) NULL));
// Third child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e,
ContainerEq(child3_attribute_list)))
.WillOnce(Return(mock_child3_handler));
{
EXPECT_CALL(*mock_child3_handler,
ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL))
.WillOnce(Return());
EXPECT_CALL(*mock_child3_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(*mock_child3_handler, Finish())
.WillOnce(Return());
}
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
}
// Drive the dispatcher.
// Report the CU header.
EXPECT_TRUE(die_dispatcher
.StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
0x47dd3c764275a216LL, 0xa5));
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL,
(DwarfTag) 0xf5d60c59,
root_attribute_list));
die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
(DwarfAttribute) 0xf779a642,
(DwarfForm) 0x2cb63027,
0x18e744661769d08fLL);
// First child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c,
child1_attribute_list));
die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
(DwarfAttribute) 0xa6fd6f65,
(DwarfForm) 0xe4f64c41,
0x1b04e5444a55fe67LL);
// First grandchild DIE. Will be skipped.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
(DwarfTag) 0x22f05a15,
grandchild1_attribute_list));
// First great-grandchild DIE. Will be skipped without being
// mentioned to any handler.
{
EXPECT_FALSE(die_dispatcher
.StartDIE(0xb3076285d25cac25LL,
(DwarfTag) 0xcff4061b,
greatgrandchild1_attribute_list));
die_dispatcher.EndDIE(0xb3076285d25cac25LL);
}
die_dispatcher.EndDIE(0xd68de1ee0bd29419LL);
}
die_dispatcher.EndDIE(0x149f644f8116fe8cLL);
}
// Second child DIE. Root handler will decline to find a handler for it.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b,
child2_attribute_list));
die_dispatcher.EndDIE(0x97412be24875de9dLL);
}
// Third child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e,
child3_attribute_list));
die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL);
die_dispatcher.EndDIE(0x753c964c8ab538aeLL);
}
// Finish the root DIE (and thus the CU).
die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL);
}
}
// The DIEDispatcher destructor is supposed to delete all handlers on
// the stack, except for the root.
TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
MockRootDIEHandler mock_root_handler;
MockDIEHandler *mock_child_handler = new(MockDIEHandler);
MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler);
AttributeList empty_attribute_list;
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
0x76d392ff393ddda2LL, 0xbf))
.WillOnce(Return(true));
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361,
ContainerEq(empty_attribute_list)))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
// Child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0,
ContainerEq(empty_attribute_list)))
.WillOnce(Return(mock_child_handler));
{
EXPECT_CALL(*mock_child_handler, EndAttributes())
.WillOnce(Return(true));
// Grandchild DIE.
EXPECT_CALL(*mock_child_handler,
FindChildHandler(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007,
ContainerEq(empty_attribute_list)))
.WillOnce(Return(mock_grandchild_handler));
{
EXPECT_CALL(*mock_grandchild_handler,
ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL))
.WillOnce(Return());
// At this point, we abandon the traversal, so none of the
// usual stuff should get called.
EXPECT_CALL(*mock_grandchild_handler, EndAttributes())
.Times(0);
EXPECT_CALL(*mock_grandchild_handler, Finish())
.Times(0);
}
EXPECT_CALL(*mock_child_handler, Finish())
.Times(0);
}
EXPECT_CALL(mock_root_handler, Finish())
.Times(0);
}
}
// The dispatcher.
DIEDispatcher die_dispatcher(&mock_root_handler);
// Report the CU header.
EXPECT_TRUE(die_dispatcher
.StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
0x76d392ff393ddda2LL, 0xbf));
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL,
(DwarfTag) 0x98980361,
empty_attribute_list));
// Child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0,
empty_attribute_list));
// Grandchild DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007,
empty_attribute_list));
die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL);
// Stop the traversal abruptly, so that there will still be
// handlers on the stack when the dispatcher is destructed.
// No EndDIE call...
}
// No EndDIE call...
}
// No EndDIE call...
}
}

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright 2006 Google Inc. All Rights Reserved.
// -*- mode: c++ -*-
// 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
@ -486,5 +488,126 @@ enum DwarfOpcode {
DW_OP_GNU_push_tls_address =0xe0
};
// Source languages. These are values for DW_AT_language.
enum DwarfLanguage
{
DW_LANG_none =0x0000,
DW_LANG_C89 =0x0001,
DW_LANG_C =0x0002,
DW_LANG_Ada83 =0x0003,
DW_LANG_C_plus_plus =0x0004,
DW_LANG_Cobol74 =0x0005,
DW_LANG_Cobol85 =0x0006,
DW_LANG_Fortran77 =0x0007,
DW_LANG_Fortran90 =0x0008,
DW_LANG_Pascal83 =0x0009,
DW_LANG_Modula2 =0x000a,
DW_LANG_Java =0x000b,
DW_LANG_C99 =0x000c,
DW_LANG_Ada95 =0x000d,
DW_LANG_Fortran95 =0x000e,
DW_LANG_PLI =0x000f,
DW_LANG_ObjC =0x0010,
DW_LANG_ObjC_plus_plus =0x0011,
DW_LANG_UPC =0x0012,
DW_LANG_D =0x0013,
// Implementation-defined language code range.
DW_LANG_lo_user = 0x8000,
DW_LANG_hi_user = 0xffff,
// Extensions.
// MIPS assembly language. The GNU toolchain uses this for all
// assembly languages, since there's no generic DW_LANG_ value for that.
// See include/dwarf2.h in the binutils, gdb, or gcc source trees.
DW_LANG_Mips_Assembler =0x8001,
DW_LANG_Upc =0x8765 // Unified Parallel C
};
// Inline codes. These are values for DW_AT_inline.
enum DwarfInline {
DW_INL_not_inlined =0x0,
DW_INL_inlined =0x1,
DW_INL_declared_not_inlined =0x2,
DW_INL_declared_inlined =0x3,
};
// Call Frame Info instructions.
enum DwarfCFI
{
DW_CFA_advance_loc = 0x40,
DW_CFA_offset = 0x80,
DW_CFA_restore = 0xc0,
DW_CFA_nop = 0x00,
DW_CFA_set_loc = 0x01,
DW_CFA_advance_loc1 = 0x02,
DW_CFA_advance_loc2 = 0x03,
DW_CFA_advance_loc4 = 0x04,
DW_CFA_offset_extended = 0x05,
DW_CFA_restore_extended = 0x06,
DW_CFA_undefined = 0x07,
DW_CFA_same_value = 0x08,
DW_CFA_register = 0x09,
DW_CFA_remember_state = 0x0a,
DW_CFA_restore_state = 0x0b,
DW_CFA_def_cfa = 0x0c,
DW_CFA_def_cfa_register = 0x0d,
DW_CFA_def_cfa_offset = 0x0e,
DW_CFA_def_cfa_expression = 0x0f,
DW_CFA_expression = 0x10,
DW_CFA_offset_extended_sf = 0x11,
DW_CFA_def_cfa_sf = 0x12,
DW_CFA_def_cfa_offset_sf = 0x13,
DW_CFA_val_offset = 0x14,
DW_CFA_val_offset_sf = 0x15,
DW_CFA_val_expression = 0x16,
// Opcodes in this range are reserved for user extensions.
DW_CFA_lo_user = 0x1c,
DW_CFA_hi_user = 0x3f,
// SGI/MIPS specific.
DW_CFA_MIPS_advance_loc8 = 0x1d,
// GNU extensions.
DW_CFA_GNU_window_save = 0x2d,
DW_CFA_GNU_args_size = 0x2e,
DW_CFA_GNU_negative_offset_extended = 0x2f
};
// Exception handling frame description pointer formats, as described
// by the Linux Standard Base Core Specification 4.0, section 11.5,
// DWARF Extensions.
enum DwarfPointerEncoding
{
DW_EH_PE_absptr = 0x00,
DW_EH_PE_omit = 0xff,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_textrel = 0x20,
DW_EH_PE_datarel = 0x30,
DW_EH_PE_funcrel = 0x40,
DW_EH_PE_aligned = 0x50,
// The GNU toolchain sources define this enum value as well,
// simply to help classify the lower nybble values into signed and
// unsigned groups.
DW_EH_PE_signed = 0x08,
// This is not documented in LSB 4.0, but it is used in both the
// Linux and OS X toolchains. It can be added to any other
// encoding (except DW_EH_PE_aligned), and indicates that the
// encoded value represents the address at which the true address
// is stored, not the true address itself.
DW_EH_PE_indirect = 0x80
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_DWARF2ENUMS_H__

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright 2006 Google Inc. All Rights Reserved.
// -*- mode: C++ -*-
// 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
@ -26,6 +28,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// This file contains definitions related to the DWARF2/3 reader and
// it's handler interfaces.
// The DWARF2/3 specification can be found at
@ -42,6 +46,7 @@
#include <utility>
#include <vector>
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/types.h"
@ -49,7 +54,6 @@ using namespace std;
namespace dwarf2reader {
struct LineStateMachine;
class ByteReader;
class Dwarf2Handler;
class LineInfoHandler;
@ -167,12 +171,13 @@ class LineInfoHandler {
uint64 length) { }
// Called when the line info reader has a new line, address pair
// ready for us. ADDRESS is the address of the code, FILE_NUM is
// the file number containing the code, LINE_NUM is the line number in
// that file for the code, and COLUMN_NUM is the column number the code
// starts at, if we know it (0 otherwise).
virtual void AddLine(uint64 address, uint32 file_num, uint32 line_num,
uint32 column_num) { }
// ready for us. ADDRESS is the address of the code, LENGTH is the
// length of its machine code in bytes, FILE_NUM is the file number
// containing the code, LINE_NUM is the line number in that file for
// the code, and COLUMN_NUM is the column number the code starts at,
// if we know it (0 otherwise).
virtual void AddLine(uint64 address, uint64 length,
uint32 file_num, uint32 line_num, uint32 column_num) { }
};
// The base of DWARF2/3 debug info is a DIE (Debugging Information
@ -358,6 +363,16 @@ class Dwarf2Handler {
enum DwarfForm form,
int64 data) { }
// Called when we have an attribute whose value is a reference to
// another DIE. The attribute belongs to the DIE at OFFSET from the
// beginning of the .debug_info section. Its name is ATTR, its form
// is FORM, and the offset of the DIE being referred to from the
// beginning of the .debug_info section is DATA.
virtual void ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) { }
// Called when we have an attribute with a buffer of data to give to our
// handler. The attribute is for the DIE at OFFSET from the beginning of the
// .debug_info section. Its name is ATTR, its form is FORM, DATA points to
@ -387,6 +402,640 @@ class Dwarf2Handler {
};
// This class is a reader for DWARF's Call Frame Information. CFI
// describes how to unwind stack frames --- even for functions that do
// not follow fixed conventions for saving registers, whose frame size
// varies as they execute, etc.
//
// CFI describes, at each machine instruction, how to compute the
// stack frame's base address, how to find the return address, and
// where to find the saved values of the caller's registers (if the
// callee has stashed them somewhere to free up the registers for its
// own use).
//
// For example, suppose we have a function whose machine code looks
// like this (imagine an assembly language that looks like C, for a
// machine with 32-bit registers, and a stack that grows towards lower
// addresses):
//
// func: ; entry point; return address at sp
// func+0: sp = sp - 16 ; allocate space for stack frame
// func+1: sp[12] = r0 ; save r0 at sp+12
// ... ; other code, not frame-related
// func+10: sp -= 4; *sp = x ; push some x on the stack
// ... ; other code, not frame-related
// func+20: r0 = sp[16] ; restore saved r0
// func+21: sp += 20 ; pop whole stack frame
// func+22: pc = *sp; sp += 4 ; pop return address and jump to it
//
// DWARF CFI is (a very compressed representation of) a table with a
// row for each machine instruction address and a column for each
// register showing how to restore it, if possible.
//
// A special column named "CFA", for "Canonical Frame Address", tells how
// to compute the base address of the frame; registers' entries may
// refer to the CFA in describing where the registers are saved.
//
// Another special column, named "RA", represents the return address.
//
// For example, here is a complete (uncompressed) table describing the
// function above:
//
// insn cfa r0 r1 ... ra
// =======================================
// func+0: sp cfa[0]
// func+1: sp+16 cfa[0]
// func+2: sp+16 cfa[-4] cfa[0]
// func+11: sp+20 cfa[-4] cfa[0]
// func+21: sp+20 cfa[0]
// func+22: sp cfa[0]
//
// Some things to note here:
//
// - Each row describes the state of affairs *before* executing the
// instruction at the given address. Thus, the row for func+0
// describes the state before we allocate the stack frame. In the
// next row, the formula for computing the CFA has changed,
// reflecting that allocation.
//
// - The other entries are written in terms of the CFA; this allows
// them to remain unchanged as the stack pointer gets bumped around.
// For example, the rule for recovering the return address (the "ra"
// column) remains unchanged throughout the function, even as the
// stack pointer takes on three different offsets from the return
// address.
//
// - Although we haven't shown it, most calling conventions designate
// "callee-saves" and "caller-saves" registers. The callee must
// preserve the values of callee-saves registers; if it uses them,
// it must save their original values somewhere, and restore them
// before it returns. In contrast, the callee is free to trash
// caller-saves registers; if the callee uses these, it will
// probably not bother to save them anywhere, and the CFI will
// probably mark their values as "unrecoverable".
//
// (However, since the caller cannot assume the callee was going to
// save them, caller-saves registers are probably dead in the caller
// anyway, so compilers usually don't generate CFA for caller-saves
// registers.)
//
// - Exactly where the CFA points is a matter of convention that
// depends on the architecture and ABI in use. In the example, the
// CFA is the value the stack pointer had upon entry to the
// function, pointing at the saved return address. But on the x86,
// the call frame information generated by GCC follows the
// convention that the CFA is the address *after* the saved return
// address.
//
// But by definition, the CFA remains constant throughout the
// lifetime of the frame. This makes it a useful value for other
// columns to refer to. It is also gives debuggers a useful handle
// for identifying a frame.
//
// If you look at the table above, you'll notice that a given entry is
// often the same as the one immediately above it: most instructions
// change only one or two aspects of the stack frame, if they affect
// it at all. The DWARF format takes advantage of this fact, and
// reduces the size of the data by mentioning only the addresses and
// columns at which changes take place. So for the above, DWARF CFI
// data would only actually mention the following:
//
// insn cfa r0 r1 ... ra
// =======================================
// func+0: sp cfa[0]
// func+1: sp+16
// func+2: cfa[-4]
// func+11: sp+20
// func+21: r0
// func+22: sp
//
// In fact, this is the way the parser reports CFI to the consumer: as
// a series of statements of the form, "At address X, column Y changed
// to Z," and related conventions for describing the initial state.
//
// Naturally, it would be impractical to have to scan the entire
// program's CFI, noting changes as we go, just to recover the
// unwinding rules in effect at one particular instruction. To avoid
// this, CFI data is grouped into "entries", each of which covers a
// specified range of addresses and begins with a complete statement
// of the rules for all recoverable registers at that starting
// address. Each entry typically covers a single function.
//
// Thus, to compute the contents of a given row of the table --- that
// is, rules for recovering the CFA, RA, and registers at a given
// instruction --- the consumer should find the entry that covers that
// instruction's address, start with the initial state supplied at the
// beginning of the entry, and work forward until it has processed all
// the changes up to and including those for the present instruction.
//
// There are seven kinds of rules that can appear in an entry of the
// table:
//
// - "undefined": The given register is not preserved by the callee;
// its value cannot be recovered.
//
// - "same value": This register has the same value it did in the callee.
//
// - offset(N): The register is saved at offset N from the CFA.
//
// - val_offset(N): The value the register had in the caller is the
// CFA plus offset N. (This is usually only useful for describing
// the stack pointer.)
//
// - register(R): The register's value was saved in another register R.
//
// - expression(E): Evaluating the DWARF expression E using the
// current frame's registers' values yields the address at which the
// register was saved.
//
// - val_expression(E): Evaluating the DWARF expression E using the
// current frame's registers' values yields the value the register
// had in the caller.
class CallFrameInfo {
public:
// The different kinds of entries one finds in CFI. Used internally,
// and for error reporting.
enum EntryKind { kUnknown, kCIE, kFDE, kTerminator };
// The handler class to which the parser hands the parsed call frame
// information. Defined below.
class Handler;
// A reporter class, which CallFrameInfo uses to report errors
// encountered while parsing call frame information. Defined below.
class Reporter;
// Create a DWARF CFI parser. BUFFER points to the contents of the
// .debug_frame section to parse; BUFFER_LENGTH is its length in bytes.
// REPORTER is an error reporter the parser should use to report
// problems. READER is a ByteReader instance that has the endianness and
// address size set properly. Report the data we find to HANDLER.
//
// This class can also parse Linux C++ exception handling data, as found
// in '.eh_frame' sections. This data is a variant of DWARF CFI that is
// placed in loadable segments so that it is present in the program's
// address space, and is interpreted by the C++ runtime to search the
// call stack for a handler interested in the exception being thrown,
// actually pop the frames, and find cleanup code to run.
//
// There are two differences between the call frame information described
// in the DWARF standard and the exception handling data Linux places in
// the .eh_frame section:
//
// - Exception handling data uses uses a different format for call frame
// information entry headers. The distinguished CIE id, the way FDEs
// refer to their CIEs, and the way the end of the series of entries is
// determined are all slightly different.
//
// If the constructor's EH_FRAME argument is true, then the
// CallFrameInfo parses the entry headers as Linux C++ exception
// handling data. If EH_FRAME is false or omitted, the CallFrameInfo
// parses standard DWARF call frame information.
//
// - Linux C++ exception handling data uses CIE augmentation strings
// beginning with 'z' to specify the presence of additional data after
// the CIE and FDE headers and special encodings used for addresses in
// frame description entries.
//
// CallFrameInfo can handle 'z' augmentations in either DWARF CFI or
// exception handling data if you have supplied READER with the base
// addresses needed to interpret the pointer encodings that 'z'
// augmentations can specify. See the ByteReader interface for details
// about the base addresses. See the CallFrameInfo::Handler interface
// for details about the additional information one might find in
// 'z'-augmented data.
//
// Thus:
//
// - If you are parsing standard DWARF CFI, as found in a .debug_frame
// section, you should pass false for the EH_FRAME argument, or omit
// it, and you need not worry about providing READER with the
// additional base addresses.
//
// - If you want to parse Linux C++ exception handling data from a
// .eh_frame section, you should pass EH_FRAME as true, and call
// READER's Set*Base member functions before calling our Start method.
//
// - If you want to parse DWARF CFI that uses the 'z' augmentations
// (although I don't think any toolchain ever emits such data), you
// could pass false for EH_FRAME, but call READER's Set*Base members.
//
// The extensions the Linux C++ ABI makes to DWARF for exception
// handling are described here, rather poorly:
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
//
// The mechanics of C++ exception handling, personality routines,
// and language-specific data areas are described here, rather nicely:
// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
CallFrameInfo(const char *buffer, size_t buffer_length,
ByteReader *reader, Handler *handler, Reporter *reporter,
bool eh_frame = false)
: buffer_(buffer), buffer_length_(buffer_length),
reader_(reader), handler_(handler), reporter_(reporter),
eh_frame_(eh_frame) { }
~CallFrameInfo() { }
// Parse the entries in BUFFER, reporting what we find to HANDLER.
// Return true if we reach the end of the section successfully, or
// false if we encounter an error.
bool Start();
// Return the textual name of KIND. For error reporting.
static const char *KindName(EntryKind kind);
private:
struct CIE;
// A CFI entry, either an FDE or a CIE.
struct Entry {
// The starting offset of the entry in the section, for error
// reporting.
size_t offset;
// The start of this entry in the buffer.
const char *start;
// Which kind of entry this is.
//
// We want to be able to use this for error reporting even while we're
// in the midst of parsing. Error reporting code may assume that kind,
// offset, and start fields are valid, although kind may be kUnknown.
EntryKind kind;
// The end of this entry's common prologue (initial length and id), and
// the start of this entry's kind-specific fields.
const char *fields;
// The start of this entry's instructions.
const char *instructions;
// The address past the entry's last byte in the buffer. (Note that
// since offset points to the entry's initial length field, and the
// length field is the number of bytes after that field, this is not
// simply buffer_ + offset + length.)
const char *end;
// For both DWARF CFI and .eh_frame sections, this is the CIE id in a
// CIE, and the offset of the associated CIE in an FDE.
uint64 id;
// The CIE that applies to this entry, if we've parsed it. If this is a
// CIE, then this field points to this structure.
CIE *cie;
};
// A common information entry (CIE).
struct CIE: public Entry {
uint8 version; // CFI data version number
string augmentation; // vendor format extension markers
uint64 code_alignment_factor; // scale for code address adjustments
int data_alignment_factor; // scale for stack pointer adjustments
unsigned return_address_register; // which register holds the return addr
// True if this CIE includes Linux C++ ABI 'z' augmentation data.
bool has_z_augmentation;
// Parsed 'z' augmentation data. These are meaningful only if
// has_z_augmentation is true.
bool has_z_lsda; // The 'z' augmentation included 'L'.
bool has_z_personality; // The 'z' augmentation included 'P'.
bool has_z_signal_frame; // The 'z' augmentation included 'S'.
// If has_z_lsda is true, this is the encoding to be used for language-
// specific data area pointers in FDEs.
DwarfPointerEncoding lsda_encoding;
// If has_z_personality is true, this is the encoding used for the
// personality routine pointer in the augmentation data.
DwarfPointerEncoding personality_encoding;
// If has_z_personality is true, this is the address of the personality
// routine --- or, if personality_encoding & DW_EH_PE_indirect, the
// address where the personality routine's address is stored.
uint64 personality_address;
// This is the encoding used for addresses in the FDE header and
// in DW_CFA_set_loc instructions. This is always valid, whether
// or not we saw a 'z' augmentation string; its default value is
// DW_EH_PE_absptr, which is what normal DWARF CFI uses.
DwarfPointerEncoding pointer_encoding;
};
// A frame description entry (FDE).
struct FDE: public Entry {
uint64 address; // start address of described code
uint64 size; // size of described code, in bytes
// If cie->has_z_lsda is true, then this is the language-specific data
// area's address --- or its address's address, if cie->lsda_encoding
// has the DW_EH_PE_indirect bit set.
uint64 lsda_address;
};
// Internal use.
class Rule;
class UndefinedRule;
class SameValueRule;
class OffsetRule;
class ValOffsetRule;
class RegisterRule;
class ExpressionRule;
class ValExpressionRule;
class RuleMap;
class State;
// Parse the initial length and id of a CFI entry, either a CIE, an FDE,
// or a .eh_frame end-of-data mark. CURSOR points to the beginning of the
// data to parse. On success, populate ENTRY as appropriate, and return
// true. On failure, report the problem, and return false. Even if we
// return false, set ENTRY->end to the first byte after the entry if we
// were able to figure that out, or NULL if we weren't.
bool ReadEntryPrologue(const char *cursor, Entry *entry);
// Parse the fields of a CIE after the entry prologue, including any 'z'
// augmentation data. Assume that the 'Entry' fields of CIE are
// populated; use CIE->fields and CIE->end as the start and limit for
// parsing. On success, populate the rest of *CIE, and return true; on
// failure, report the problem and return false.
bool ReadCIEFields(CIE *cie);
// Parse the fields of an FDE after the entry prologue, including any 'z'
// augmentation data. Assume that the 'Entry' fields of *FDE are
// initialized; use FDE->fields and FDE->end as the start and limit for
// parsing. Assume that FDE->cie is fully initialized. On success,
// populate the rest of *FDE, and return true; on failure, report the
// problem and return false.
bool ReadFDEFields(FDE *fde);
// Report that ENTRY is incomplete, and return false. This is just a
// trivial wrapper for invoking reporter_->Incomplete; it provides a
// little brevity.
bool ReportIncomplete(Entry *entry);
// Return true if ENCODING has the DW_EH_PE_indirect bit set.
static bool IsIndirectEncoding(DwarfPointerEncoding encoding) {
return encoding & DW_EH_PE_indirect;
}
// The contents of the DWARF .debug_info section we're parsing.
const char *buffer_;
size_t buffer_length_;
// For reading multi-byte values with the appropriate endianness.
ByteReader *reader_;
// The handler to which we should report the data we find.
Handler *handler_;
// For reporting problems in the info we're parsing.
Reporter *reporter_;
// True if we are processing .eh_frame-format data.
bool eh_frame_;
};
// The handler class for CallFrameInfo. The a CFI parser calls the
// member functions of a handler object to report the data it finds.
class CallFrameInfo::Handler {
public:
// The pseudo-register number for the canonical frame address.
enum { kCFARegister = -1 };
Handler() { }
virtual ~Handler() { }
// The parser has found CFI for the machine code at ADDRESS,
// extending for LENGTH bytes. OFFSET is the offset of the frame
// description entry in the section, for use in error messages.
// VERSION is the version number of the CFI format. AUGMENTATION is
// a string describing any producer-specific extensions present in
// the data. RETURN_ADDRESS is the number of the register that holds
// the address to which the function should return.
//
// Entry should return true to process this CFI, or false to skip to
// the next entry.
//
// The parser invokes Entry for each Frame Description Entry (FDE)
// it finds. The parser doesn't report Common Information Entries
// to the handler explicitly; instead, if the handler elects to
// process a given FDE, the parser reiterates the appropriate CIE's
// contents at the beginning of the FDE's rules.
virtual bool Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address) = 0;
// When the Entry function returns true, the parser calls these
// handler functions repeatedly to describe the rules for recovering
// registers at each instruction in the given range of machine code.
// Immediately after a call to Entry, the handler should assume that
// the rule for each callee-saves register is "unchanged" --- that
// is, that the register still has the value it had in the caller.
//
// If a *Rule function returns true, we continue processing this entry's
// instructions. If a *Rule function returns false, we stop evaluating
// instructions, and skip to the next entry. Either way, we call End
// before going on to the next entry.
//
// In all of these functions, if the REG parameter is kCFARegister, then
// the rule describes how to find the canonical frame address.
// kCFARegister may be passed as a BASE_REGISTER argument, meaning that
// the canonical frame address should be used as the base address for the
// computation. All other REG values will be positive.
// At ADDRESS, register REG's value is not recoverable.
virtual bool UndefinedRule(uint64 address, int reg) = 0;
// At ADDRESS, register REG's value is the same as that it had in
// the caller.
virtual bool SameValueRule(uint64 address, int reg) = 0;
// At ADDRESS, register REG has been saved at offset OFFSET from
// BASE_REGISTER.
virtual bool OffsetRule(uint64 address, int reg,
int base_register, long offset) = 0;
// At ADDRESS, the caller's value of register REG is the current
// value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an
// address at which the register's value is saved.)
virtual bool ValOffsetRule(uint64 address, int reg,
int base_register, long offset) = 0;
// At ADDRESS, register REG has been saved in BASE_REGISTER. This differs
// from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that
// BASE_REGISTER is the "home" for REG's saved value: if you want to
// assign to a variable whose home is REG in the calling frame, you
// should put the value in BASE_REGISTER.
virtual bool RegisterRule(uint64 address, int reg, int base_register) = 0;
// At ADDRESS, the DWARF expression EXPRESSION yields the address at
// which REG was saved.
virtual bool ExpressionRule(uint64 address, int reg,
const string &expression) = 0;
// At ADDRESS, the DWARF expression EXPRESSION yields the caller's
// value for REG. (This rule doesn't provide an address at which the
// register's value is saved.)
virtual bool ValExpressionRule(uint64 address, int reg,
const string &expression) = 0;
// Indicate that the rules for the address range reported by the
// last call to Entry are complete. End should return true if
// everything is okay, or false if an error has occurred and parsing
// should stop.
virtual bool End() = 0;
// Handler functions for Linux C++ exception handling data. These are
// only called if the data includes 'z' augmentation strings.
// The Linux C++ ABI uses an extension of the DWARF CFI format to
// walk the stack to propagate exceptions from the throw to the
// appropriate catch, and do the appropriate cleanups along the way.
// CFI entries used for exception handling have two additional data
// associated with them:
//
// - The "language-specific data area" describes which exception
// types the function has 'catch' clauses for, and indicates how
// to go about re-entering the function at the appropriate catch
// clause. If the exception is not caught, it describes the
// destructors that must run before the frame is popped.
//
// - The "personality routine" is responsible for interpreting the
// language-specific data area's contents, and deciding whether
// the exception should continue to propagate down the stack,
// perhaps after doing some cleanup for this frame, or whether the
// exception will be caught here.
//
// In principle, the language-specific data area is opaque to
// everybody but the personality routine. In practice, these values
// may be useful or interesting to readers with extra context, and
// we have to at least skip them anyway, so we might as well report
// them to the handler.
// This entry's exception handling personality routine's address is
// ADDRESS. If INDIRECT is true, then ADDRESS is the address at
// which the routine's address is stored. The default definition for
// this handler function simply returns true, allowing parsing of
// the entry to continue.
virtual bool PersonalityRoutine(uint64 address, bool indirect) {
return true;
}
// This entry's language-specific data area (LSDA) is located at
// ADDRESS. If INDIRECT is true, then ADDRESS is the address at
// which the area's address is stored. The default definition for
// this handler function simply returns true, allowing parsing of
// the entry to continue.
virtual bool LanguageSpecificDataArea(uint64 address, bool indirect) {
return true;
}
// This entry describes a signal trampoline --- this frame is the
// caller of a signal handler. The default definition for this
// handler function simply returns true, allowing parsing of the
// entry to continue.
//
// The best description of the rationale for and meaning of signal
// trampoline CFI entries seems to be in the GCC bug database:
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26208
virtual bool SignalHandler() { return true; }
};
// The CallFrameInfo class makes calls on an instance of this class to
// report errors or warn about problems in the data it is parsing. The
// default definitions of these methods print a message to stderr, but
// you can make a derived class that overrides them.
class CallFrameInfo::Reporter {
public:
// Create an error reporter which attributes troubles to the section
// named SECTION in FILENAME.
//
// Normally SECTION would be .debug_frame, but the Mac puts CFI data
// in a Mach-O section named __debug_frame. If we support
// Linux-style exception handling data, we could be reading an
// .eh_frame section.
Reporter(const string &filename,
const string &section = ".debug_frame")
: filename_(filename), section_(section) { }
virtual ~Reporter() { }
// The CFI entry at OFFSET ends too early to be well-formed. KIND
// indicates what kind of entry it is; KIND can be kUnknown if we
// haven't parsed enough of the entry to tell yet.
virtual void Incomplete(uint64 offset, CallFrameInfo::EntryKind kind);
// The .eh_frame data has a four-byte zero at OFFSET where the next
// entry's length would be; this is a terminator. However, the buffer
// length as given to the CallFrameInfo constructor says there should be
// more data.
virtual void EarlyEHTerminator(uint64 offset);
// The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the
// section is not that large.
virtual void CIEPointerOutOfRange(uint64 offset, uint64 cie_offset);
// The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry
// there is not a CIE.
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
// The FDE at OFFSET refers to a CIE with version number VERSION,
// which we don't recognize. We cannot parse DWARF CFI if it uses
// a version number we don't recognize.
virtual void UnrecognizedVersion(uint64 offset, int version);
// The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION,
// which we don't recognize. We cannot parse DWARF CFI if it uses
// augmentations we don't recognize.
virtual void UnrecognizedAugmentation(uint64 offset,
const string &augmentation);
// The pointer encoding ENCODING, specified by the CIE at OFFSET, is not
// a valid encoding.
virtual void InvalidPointerEncoding(uint64 offset, uint8 encoding);
// The pointer encoding ENCODING, specified by the CIE at OFFSET, depends
// on a base address which has not been supplied.
virtual void UnusablePointerEncoding(uint64 offset, uint8 encoding);
// The CIE at OFFSET contains a DW_CFA_restore instruction at
// INSN_OFFSET, which may not appear in a CIE.
virtual void RestoreInCIE(uint64 offset, uint64 insn_offset);
// The entry at OFFSET, of kind KIND, has an unrecognized
// instruction at INSN_OFFSET.
virtual void BadInstruction(uint64 offset, CallFrameInfo::EntryKind kind,
uint64 insn_offset);
// The instruction at INSN_OFFSET in the entry at OFFSET, of kind
// KIND, establishes a rule that cites the CFA, but we have not
// established a CFA rule yet.
virtual void NoCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
uint64 insn_offset);
// The instruction at INSN_OFFSET in the entry at OFFSET, of kind
// KIND, is a DW_CFA_restore_state instruction, but the stack of
// saved states is empty.
virtual void EmptyStateStack(uint64 offset, CallFrameInfo::EntryKind kind,
uint64 insn_offset);
// The DW_CFA_remember_state instruction at INSN_OFFSET in the entry
// at OFFSET, of kind KIND, would restore a state that has no CFA
// rule, whereas the current state does have a CFA rule. This is
// bogus input, which the CallFrameInfo::Handler interface doesn't
// (and shouldn't) have any way to report.
virtual void ClearingCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
uint64 insn_offset);
protected:
// The name of the file whose CFI we're reading.
string filename_;
// The name of the CFI section in that file.
string section_;
};
} // namespace dwarf2reader

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright 2006 Google Inc. All Rights Reserved.
// 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
@ -42,25 +42,6 @@
namespace dwarf2reader {
// Given an offset value, its form, and the base offset of the
// compilation unit containing this value, return an absolute offset
// within the .debug_info section.
uint64 GetAbsoluteOffset(uint64 offset,
enum DwarfForm form,
uint64 compilation_unit_base) {
switch (form) {
case DW_FORM_ref1:
case DW_FORM_ref2:
case DW_FORM_ref4:
case DW_FORM_ref8:
case DW_FORM_ref_udata:
return offset + compilation_unit_base;
case DW_FORM_ref_addr:
default:
return offset;
}
}
CULineInfoHandler::CULineInfoHandler(vector<SourceFileInfo>* files,
vector<string>* dirs,
LineMap* linemap):linemap_(linemap),
@ -108,7 +89,7 @@ void CULineInfoHandler::DefineFile(const string& name,
}
}
void CULineInfoHandler::AddLine(uint64 address, uint32 file_num,
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
uint32 line_num, uint32 column_num) {
if (file_num < files_->size()) {
linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(),
@ -198,6 +179,18 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
case DW_AT_decl_file:
current_function_info_->file = files_->at(data).name;
break;
default:
break;
}
}
}
void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
if (current_function_info_) {
switch (attr) {
case DW_AT_specification: {
// Some functions have a "specification" attribute
// which means they were defined elsewhere. The name
@ -205,14 +198,13 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
// the specification DIE. Here we'll assume that
// any DIE referenced in this manner will already have
// been seen, but that's not really required by the spec.
uint64 abs_offset = GetAbsoluteOffset(data, form, current_compilation_unit_offset_);
FunctionMap::iterator iter = offset_to_funcinfo_->find(abs_offset);
FunctionMap::iterator iter = offset_to_funcinfo_->find(data);
if (iter != offset_to_funcinfo_->end()) {
current_function_info_->name = iter->second->name;
current_function_info_->mangled_name = iter->second->mangled_name;
} else {
// If you hit this, this code probably needs to be rewritten.
fprintf(stderr, "Error: DW_AT_specification was seen before the referenced DIE! (Looking for DIE at offset %08llx, in DIE at offset %08llx)\n", abs_offset, offset);
fprintf(stderr, "Error: DW_AT_specification was seen before the referenced DIE! (Looking for DIE at offset %08llx, in DIE at offset %08llx)\n", data, offset);
}
break;
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright 2006 Google Inc. All Rights Reserved.
// 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
@ -92,13 +92,13 @@ class CULineInfoHandler: public LineInfoHandler {
// Called when the line info reader has a new line, address pair
// ready for us. ADDRESS is the address of the code, FILE_NUM is
// the file number containing the code, LINE_NUM is the line number
// in that file for the code, and COLUMN_NUM is the column number
// the code starts at, if we know it (0 otherwise).
virtual void AddLine(uint64 address, uint32 file_num, uint32 line_num,
uint32 column_num);
// ready for us. ADDRESS is the address of the code, LENGTH is the
// length of its machine code in bytes, FILE_NUM is the file number
// containing the code, LINE_NUM is the line number in that file for
// the code, and COLUMN_NUM is the column number the code starts at,
// if we know it (0 otherwise).
virtual void AddLine(uint64 address, uint64 length,
uint32 file_num, uint32 line_num, uint32 column_num);
private:
LineMap* linemap_;
@ -146,6 +146,16 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
enum DwarfForm form,
uint64 data);
// Called when we have an attribute with a DIE reference to give to
// our handler. The attribute is for the DIE at OFFSET from the
// beginning of the .debug_info section, has a name of ATTR, a form of
// FORM, and the offset of the referenced DIE from the start of the
// .debug_info section is in DATA.
virtual void ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
// Called when we have an attribute with string data to give to
// our handler. The attribute is for the DIE at OFFSET from the
// beginning of the .debug_info section, has a name of ATTR, a form of

Просмотреть файл

@ -0,0 +1,178 @@
// 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>
// dump_stabs.cc --- implement the DumpStabsHandler class.
#include <cstdarg>
#include <cxxabi.h>
#include <algorithm>
#include <cassert>
#include "common/linux/dump_stabs.h"
namespace google_breakpad {
using std::string;
// Demangle using abi call.
// Older GCC may not support it.
static string Demangle(const string &mangled) {
int status = 0;
char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
if (status == 0 && demangled != NULL) {
string str(demangled);
free(demangled);
return str;
}
return string(mangled);
}
bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address,
const char *build_directory) {
assert(!in_compilation_unit_);
in_compilation_unit_ = true;
current_source_file_name_ = name;
current_source_file_ = module_->FindFile(name);
comp_unit_base_address_ = address;
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::EndCompilationUnit(uint64_t address) {
assert(in_compilation_unit_);
in_compilation_unit_ = false;
comp_unit_base_address_ = 0;
current_source_file_ = NULL;
current_source_file_name_ = NULL;
if (address)
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::StartFunction(const string &name,
uint64_t address) {
assert(!current_function_);
Module::Function *f = new Module::Function;
f->name = Demangle(name);
f->address = address;
f->size = 0; // We compute this in DumpStabsHandler::Finalize().
f->parameter_size = 0; // We don't provide this information.
current_function_ = f;
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::EndFunction(uint64_t address) {
assert(current_function_);
// Functions in this compilation unit should have address bigger
// than the compilation unit's starting address. There may be a lot
// of duplicated entries for functions in the STABS data; only one
// entry can meet this requirement.
//
// (I don't really understand the above comment; just bringing it
// along from the previous code, and leaving the behaivor unchanged.
// If you know the whole story, please patch this comment. --jimb)
if (current_function_->address >= comp_unit_base_address_)
functions_.push_back(current_function_);
else
delete current_function_;
current_function_ = NULL;
if (address)
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) {
assert(current_function_);
assert(current_source_file_);
if (name != current_source_file_name_) {
current_source_file_ = module_->FindFile(name);
current_source_file_name_ = name;
}
Module::Line line;
line.address = address;
line.size = 0; // We compute this in DumpStabsHandler::Finalize().
line.file = current_source_file_;
line.number = number;
current_function_->lines.push_back(line);
return true;
}
void DumpStabsHandler::Warning(const char *format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}
void DumpStabsHandler::Finalize() {
// Sort our boundary list, so we can search it quickly.
sort(boundaries_.begin(), boundaries_.end());
// Sort all functions by address, just for neatness.
sort(functions_.begin(), functions_.end(),
Module::Function::CompareByAddress);
for (vector<Module::Function *>::iterator func_it = functions_.begin();
func_it != functions_.end();
func_it++) {
Module::Function *f = *func_it;
// Compute the function f's size.
vector<Module::Address>::iterator boundary
= std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address);
if (boundary != boundaries_.end())
f->size = *boundary - f->address;
else
// If this is the last function in the module, and the STABS
// reader was unable to give us its ending address, then assign
// it a bogus, very large value. This will happen at most once
// per module: since we've added all functions' addresses to the
// boundary table, only one can be the last.
f->size = kFallbackSize;
// Compute sizes for each of the function f's lines --- if it has any.
if (!f->lines.empty()) {
stable_sort(f->lines.begin(), f->lines.end(),
Module::Line::CompareByAddress);
vector<Module::Line>::iterator last_line = f->lines.end() - 1;
for (vector<Module::Line>::iterator line_it = f->lines.begin();
line_it != last_line; line_it++)
line_it[0].size = line_it[1].address - line_it[0].address;
// Compute the size of the last line from f's end address.
last_line->size = (f->address + f->size) - last_line->address;
}
}
// Now that everything has a size, add our functions to the module, and
// dispose of our private list.
module_->AddFunctions(functions_.begin(), functions_.end());
functions_.clear();
}
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,139 @@
// -*- mode: C++ -*-
// 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>
// dump_stabs.h: Define the DumpStabsHandler class, which receives
// STABS debugging information from a parser and adds it to a Breakpad
// symbol file.
#ifndef COMMON_LINUX_DUMP_STABS_H__
#define COMMON_LINUX_DUMP_STABS_H__
#include <stdint.h>
#include <string>
#include <vector>
#include "common/linux/module.h"
#include "common/linux/stabs_reader.h"
namespace google_breakpad {
using std::string;
using std::vector;
// A DumpStabsHandler is a handler that receives parsed STABS
// debugging information from a StabsReader, and uses that to populate
// a Module. (All classes are in the google_breakpad namespace.) A
// Module represents the contents of a Breakpad symbol file, and knows
// how to write itself out as such. A DumpStabsHandler thus acts as
// the bridge between STABS and Breakpad data.
class DumpStabsHandler: public google_breakpad::StabsHandler {
public:
// Receive parsed debugging information from a StabsReader, and
// store it all in MODULE.
DumpStabsHandler(Module *module) :
module_(module),
in_compilation_unit_(false),
comp_unit_base_address_(0),
current_function_(NULL),
current_source_file_(NULL),
current_source_file_name_(NULL) { }
// The standard StabsHandler virtual member functions.
bool StartCompilationUnit(const char *name, uint64_t address,
const char *build_directory);
bool EndCompilationUnit(uint64_t address);
bool StartFunction(const string &name, uint64_t address);
bool EndFunction(uint64_t address);
bool Line(uint64_t address, const char *name, int number);
void Warning(const char *format, ...);
// Do any final processing necessary to make module_ contain all the
// data provided by the STABS reader.
//
// Because STABS does not provide reliable size information for
// functions and lines, we need to make a pass over the data after
// processing all the STABS to compute those sizes. We take care of
// that here.
void Finalize();
private:
// An arbitrary, but very large, size to use for functions whose
// size we can't compute properly.
static const uint64_t kFallbackSize = 0x10000000;
// The module we're contributing debugging info to.
Module *module_;
// The functions we've generated so far. We don't add these to
// module_ as we parse them. Instead, we wait until we've computed
// their ending address, and their lines' ending addresses.
//
// We could just stick them in module_ from the outset, but if
// module_ already contains data gathered from other debugging
// formats, that would complicate the size computation.
vector<Module::Function *> functions_;
// Boundary addresses. STABS doesn't necessarily supply sizes for
// functions and lines, so we need to compute them ourselves by
// finding the next object.
vector<Module::Address> boundaries_;
// True if we are currently within a compilation unit: we have gotten a
// StartCompilationUnit call, but no matching EndCompilationUnit call
// yet. We use this for sanity checks.
bool in_compilation_unit_;
// The base address of the current compilation unit. We use this to
// recognize functions we should omit from the symbol file. (If you
// know the details of why we omit these, please patch this
// comment.)
Module::Address comp_unit_base_address_;
// The function we're currently contributing lines to.
Module::Function *current_function_;
// The last Module::File we got a line number in.
Module::File *current_source_file_;
// The pointer in the .stabstr section of the name that
// current_source_file_ is built from. This allows us to quickly
// recognize when the current line is in the same file as the
// previous one (which it usually is).
const char *current_source_file_name_;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_DUMP_STABS_H__

Просмотреть файл

@ -0,0 +1,193 @@
// 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>
// dump_stabs_unittest.cc: Unit tests for DumpStabsHandler.
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/linux/dump_stabs.h"
using google_breakpad::DumpStabsHandler;
using google_breakpad::Module;
using std::vector;
TEST(DumpStabsHandler, SimpleCU) {
Module m("name", "os", "arch", "id");
DumpStabsHandler h(&m);
// Feed in a simple compilation unit that defines a function with
// one line.
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0x9f4d1271e50db93bLL,
"build-directory"));
EXPECT_TRUE(h.StartFunction("function", 0xfde4abbed390c394LL));
EXPECT_TRUE(h.Line(0xfde4abbed390c394LL, "source-file-name", 174823314));
EXPECT_TRUE(h.EndFunction(0xfde4abbed390c3a4LL));
EXPECT_TRUE(h.EndCompilationUnit(0xfee4abbed390c3a4LL));
h.Finalize();
// Now check to see what has been added to the Module.
Module::File *file = m.FindExistingFile("source-file-name");
ASSERT_TRUE(file != NULL);
vector<Module::Function *> functions;
m.GetFunctions(&functions, functions.end());
ASSERT_EQ((size_t) 1, functions.size());
Module::Function *function = functions[0];
EXPECT_STREQ("function", function->name.c_str());
EXPECT_EQ(0xfde4abbed390c394LL, function->address);
EXPECT_EQ(0x10U, function->size);
EXPECT_EQ(0U, function->parameter_size);
ASSERT_EQ((size_t) 1, function->lines.size());
Module::Line *line = &function->lines[0];
EXPECT_EQ(0xfde4abbed390c394LL, line->address);
EXPECT_EQ(0x10U, line->size); // derived from EndFunction
EXPECT_TRUE(line->file == file);
EXPECT_EQ(174823314, line->number);
}
TEST(InferSizes, LineSize) {
Module m("name", "os", "arch", "id");
DumpStabsHandler h(&m);
// Feed in a simple compilation unit that defines a function with
// one line.
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xb4513962eff94e92LL,
"build-directory"));
EXPECT_TRUE(h.StartFunction("function", 0xb4513962eff94e92LL));
EXPECT_TRUE(h.Line(0xb4513962eff94e92LL, "source-file-name-1", 77396614));
EXPECT_TRUE(h.Line(0xb4513963eff94e92LL, "source-file-name-2", 87660088));
EXPECT_TRUE(h.EndFunction(0)); // unknown function end address
EXPECT_TRUE(h.EndCompilationUnit(0)); // unknown CU end address
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit-2", 0xb4523963eff94e92LL,
"build-directory-2")); // next boundary
EXPECT_TRUE(h.EndCompilationUnit(0));
h.Finalize();
// Now check to see what has been added to the Module.
Module::File *file1 = m.FindExistingFile("source-file-name-1");
ASSERT_TRUE(file1 != NULL);
Module::File *file2 = m.FindExistingFile("source-file-name-2");
ASSERT_TRUE(file2 != NULL);
vector<Module::Function *> functions;
m.GetFunctions(&functions, functions.end());
ASSERT_EQ((size_t) 1, functions.size());
Module::Function *function = functions[0];
EXPECT_STREQ("function", function->name.c_str());
EXPECT_EQ(0xb4513962eff94e92LL, function->address);
EXPECT_EQ(0x1000100000000ULL, function->size); // inferred from CU end
EXPECT_EQ(0U, function->parameter_size);
ASSERT_EQ((size_t) 2, function->lines.size());
Module::Line *line1 = &function->lines[0];
EXPECT_EQ(0xb4513962eff94e92LL, line1->address);
EXPECT_EQ(0x100000000ULL, line1->size); // derived from EndFunction
EXPECT_TRUE(line1->file == file1);
EXPECT_EQ(77396614, line1->number);
Module::Line *line2 = &function->lines[1];
EXPECT_EQ(0xb4513963eff94e92LL, line2->address);
EXPECT_EQ(0x1000000000000ULL, line2->size); // derived from EndFunction
EXPECT_TRUE(line2->file == file2);
EXPECT_EQ(87660088, line2->number);
}
TEST(FunctionNames, Mangled) {
Module m("name", "os", "arch", "id");
DumpStabsHandler h(&m);
// Compilation unit with one function, mangled name.
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda63cef7f46cLL,
"build-directory"));
EXPECT_TRUE(h.StartFunction("_ZNSt6vectorIySaIyEE9push_backERKy",
0xf2cfda63cef7f46dLL));
EXPECT_TRUE(h.EndFunction(0));
EXPECT_TRUE(h.EndCompilationUnit(0));
h.Finalize();
// Now check to see what has been added to the Module.
Module::File *file = m.FindExistingFile("compilation-unit");
ASSERT_TRUE(file != NULL);
vector<Module::Function *> functions;
m.GetFunctions(&functions, functions.end());
ASSERT_EQ(1U, functions.size());
Module::Function *function = functions[0];
// This is GCC-specific, but we shouldn't be seeing STABS data anywhere
// but Linux.
EXPECT_STREQ("std::vector<unsigned long long, "
"std::allocator<unsigned long long> >::"
"push_back(unsigned long long const&)",
function->name.c_str());
EXPECT_EQ(0xf2cfda63cef7f46dLL, function->address);
EXPECT_LT(0U, function->size); // should have used dummy size
EXPECT_EQ(0U, function->parameter_size);
ASSERT_EQ(0U, function->lines.size());
}
// The GNU toolchain can omit functions that are not used; however,
// when it does so, it doesn't clean up the debugging information that
// refers to them. In STABS, this results in compilation units whose
// SO addresses are zero.
TEST(Omitted, Function) {
Module m("name", "os", "arch", "id");
DumpStabsHandler h(&m);
// The StartCompilationUnit and EndCompilationUnit calls may both have an
// address of zero if the compilation unit has had sections removed.
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0, "build-directory"));
EXPECT_TRUE(h.StartFunction("function", 0x2a133596));
EXPECT_TRUE(h.EndFunction(0));
EXPECT_TRUE(h.EndCompilationUnit(0));
}
// TODO --- if we actually cared about STABS. Even without these we've
// got full coverage of non-failure source lines in dump_stabs.cc.
// Line size from next line
// Line size from function end
// Line size from next function start
// line size from cu end
// line size from next cu start
// fallback size is something plausible
// function size from function end
// function size from next function start
// function size from cu end
// function size from next cu start
// fallback size is something plausible
// omitting functions outside the compilation unit's address range
// zero-line, one-line, many-line functions

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -27,30 +27,32 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <cxxabi.h>
// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
// Find all the debugging info in a file and dump it as a Breakpad symbol file.
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <link.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm>
#include <cstdarg>
#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <list>
#include <map>
#include <string>
#include <vector>
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/linux/dump_stabs.h"
#include "common/linux/dump_symbols.h"
#include "common/linux/dwarf_cfi_to_module.h"
#include "common/linux/dwarf_cu_to_module.h"
#include "common/linux/dwarf_line_to_module.h"
#include "common/linux/file_id.h"
#include "common/linux/module.h"
#include "common/linux/stabs_reader.h"
@ -58,24 +60,11 @@
// This namespace contains helper functions.
namespace {
using google_breakpad::DumpStabsHandler;
using google_breakpad::DwarfCFIToModule;
using google_breakpad::DwarfCUToModule;
using google_breakpad::DwarfLineToModule;
using google_breakpad::Module;
using std::vector;
// Stab section name.
static const char *kStabName = ".stab";
// Demangle using abi call.
// Older GCC may not support it.
static std::string Demangle(const std::string &mangled) {
int status = 0;
char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
if (status == 0 && demangled != NULL) {
std::string str(demangled);
free(demangled);
return str;
}
return std::string(mangled);
}
// Fix offset into virtual address by adding the mapped base into offsets.
// Make life easier when want to find something by offset.
@ -109,7 +98,7 @@ static bool IsValidElf(const ElfW(Ehdr) *elf_header) {
static const ElfW(Shdr) *FindSectionByName(const char *name,
const ElfW(Shdr) *sections,
const ElfW(Shdr) *strtab,
const ElfW(Shdr) *section_names,
int nsection) {
assert(name != NULL);
assert(sections != NULL);
@ -119,207 +108,24 @@ static const ElfW(Shdr) *FindSectionByName(const char *name,
if (name_len == 0)
return NULL;
// Find the end of the section name section, to make sure that
// comparisons don't run off the end of the section.
const char *names_end =
reinterpret_cast<char*>(section_names->sh_offset + section_names->sh_size);
for (int i = 0; i < nsection; ++i) {
const char *section_name =
reinterpret_cast<char*>(strtab->sh_offset + sections[i].sh_name);
if (!strncmp(name, section_name, name_len))
reinterpret_cast<char*>(section_names->sh_offset + sections[i].sh_name);
if (names_end - section_name >= name_len + 1 &&
strcmp(name, section_name) == 0)
return sections + i;
}
return NULL;
}
// Our handler class for STABS data.
class DumpStabsHandler: public google_breakpad::StabsHandler {
public:
DumpStabsHandler(Module *module) :
module_(module),
comp_unit_base_address_(0),
current_function_(NULL),
current_source_file_(NULL),
current_source_file_name_(NULL) { }
bool StartCompilationUnit(const char *name, uint64_t address,
const char *build_directory);
bool EndCompilationUnit(uint64_t address);
bool StartFunction(const std::string &name, uint64_t address);
bool EndFunction(uint64_t address);
bool Line(uint64_t address, const char *name, int number);
void Warning(const char *format, ...);
// Do any final processing necessary to make module_ contain all the
// data provided by the STABS reader.
//
// Because STABS does not provide reliable size information for
// functions and lines, we need to make a pass over the data after
// processing all the STABS to compute those sizes. We take care of
// that here.
void Finalize();
private:
// An arbitrary, but very large, size to use for functions whose
// size we can't compute properly.
static const uint64_t kFallbackSize = 0x10000000;
// The module we're contributing debugging info to.
Module *module_;
// The functions we've generated so far. We don't add these to
// module_ as we parse them. Instead, we wait until we've computed
// their ending address, and their lines' ending addresses.
//
// We could just stick them in module_ from the outset, but if
// module_ already contains data gathered from other debugging
// formats, that would complicate the size computation.
vector<Module::Function *> functions_;
// Boundary addresses. STABS doesn't necessarily supply sizes for
// functions and lines, so we need to compute them ourselves by
// finding the next object.
vector<Module::Address> boundaries_;
// The base address of the current compilation unit. We use this to
// recognize functions we should omit from the symbol file. (If you
// know the details of why we omit these, please patch this
// comment.)
Module::Address comp_unit_base_address_;
// The function we're currently contributing lines to.
Module::Function *current_function_;
// The last Module::File we got a line number in.
Module::File *current_source_file_;
// The pointer in the .stabstr section of the name that
// current_source_file_ is built from. This allows us to quickly
// recognize when the current line is in the same file as the
// previous one (which it usually is).
const char *current_source_file_name_;
};
bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address,
const char *build_directory) {
assert(! comp_unit_base_address_);
current_source_file_name_ = name;
current_source_file_ = module_->FindFile(name);
comp_unit_base_address_ = address;
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::EndCompilationUnit(uint64_t address) {
assert(comp_unit_base_address_);
comp_unit_base_address_ = 0;
current_source_file_ = NULL;
current_source_file_name_ = NULL;
if (address)
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::StartFunction(const std::string &name,
uint64_t address) {
assert(! current_function_);
Module::Function *f = new Module::Function;
f->name_ = Demangle(name);
f->address_ = address;
f->size_ = 0; // We compute this in DumpStabsHandler::Finalize().
f->parameter_size_ = 0; // We don't provide this information.
current_function_ = f;
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::EndFunction(uint64_t address) {
assert(current_function_);
// Functions in this compilation unit should have address bigger
// than the compilation unit's starting address. There may be a lot
// of duplicated entries for functions in the STABS data; only one
// entry can meet this requirement.
//
// (I don't really understand the above comment; just bringing it
// along from the previous code, and leaving the behaivor unchanged.
// If you know the whole story, please patch this comment. --jimb)
if (current_function_->address_ >= comp_unit_base_address_)
functions_.push_back(current_function_);
else
delete current_function_;
current_function_ = NULL;
if (address)
boundaries_.push_back(static_cast<Module::Address>(address));
return true;
}
bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) {
assert(current_function_);
assert(current_source_file_);
if (name != current_source_file_name_) {
current_source_file_ = module_->FindFile(name);
current_source_file_name_ = name;
}
Module::Line line;
line.address_ = address;
line.size_ = 0; // We compute this in DumpStabsHandler::Finalize().
line.file_ = current_source_file_;
line.number_ = number;
current_function_->lines_.push_back(line);
return true;
}
void DumpStabsHandler::Warning(const char *format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}
void DumpStabsHandler::Finalize() {
// Sort our boundary list, so we can search it quickly.
sort(boundaries_.begin(), boundaries_.end());
// Sort all functions by address, just for neatness.
sort(functions_.begin(), functions_.end(),
Module::Function::CompareByAddress);
for (vector<Module::Function *>::iterator func_it = functions_.begin();
func_it != functions_.end();
func_it++) {
Module::Function *f = *func_it;
// Compute the function f's size.
vector<Module::Address>::iterator boundary
= std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address_);
if (boundary != boundaries_.end())
f->size_ = *boundary - f->address_;
else
// If this is the last function in the module, and the STABS
// reader was unable to give us its ending address, then assign
// it a bogus, very large value. This will happen at most once
// per module: since we've added all functions' addresses to the
// boundary table, only one can be the last.
f->size_ = kFallbackSize;
// Compute sizes for each of the function f's lines --- if it has any.
if (! f->lines_.empty()) {
stable_sort(f->lines_.begin(), f->lines_.end(),
Module::Line::CompareByAddress);
vector<Module::Line>::iterator last_line = f->lines_.end() - 1;
for (vector<Module::Line>::iterator line_it = f->lines_.begin();
line_it != last_line; line_it++)
line_it[0].size_ = line_it[1].address_ - line_it[0].address_;
// Compute the size of the last line from f's end address.
last_line->size_ = (f->address_ + f->size_) - last_line->address_;
}
}
// Now that everything has a size, add our functions to the module, and
// dispose of our private list.
module_->AddFunctions(functions_.begin(), functions_.end());
functions_.clear();
}
static bool LoadSymbols(const ElfW(Shdr) *stab_section,
const ElfW(Shdr) *stabstr_section,
Module *module) {
if (stab_section == NULL || stabstr_section == NULL)
return false;
static bool LoadStabs(const ElfW(Shdr) *stab_section,
const ElfW(Shdr) *stabstr_section,
Module *module) {
// A callback object to handle data from the STABS reader.
DumpStabsHandler handler(module);
// Find the addresses of the STABS data, and create a STABS reader object.
@ -329,13 +135,218 @@ static bool LoadSymbols(const ElfW(Shdr) *stab_section,
stabstr, stabstr_section->sh_size,
&handler);
// Read the STABS data, and do post-processing.
if (! reader.Process())
if (!reader.Process())
return false;
handler.Finalize();
return true;
}
static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) {
// A line-to-module loader that accepts line number info parsed by
// dwarf2reader::LineInfo and populates a Module and a line vector
// with the results.
class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
public:
// Create a line-to-module converter using BYTE_READER.
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
: byte_reader_(byte_reader) { }
void operator()(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) {
DwarfLineToModule handler(module, lines);
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
parser.Start();
}
private:
dwarf2reader::ByteReader *byte_reader_;
};
static bool LoadDwarf(const string &dwarf_filename,
const ElfW(Ehdr) *elf_header,
Module *module) {
// Figure out what endianness this file is.
dwarf2reader::Endianness endianness;
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
endianness = dwarf2reader::ENDIANNESS_LITTLE;
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
endianness = dwarf2reader::ENDIANNESS_BIG;
else {
fprintf(stderr, "bad data encoding in ELF header: %d\n",
elf_header->e_ident[EI_DATA]);
return false;
}
dwarf2reader::ByteReader byte_reader(endianness);
// Construct a context for this file.
DwarfCUToModule::FileContext file_context(dwarf_filename, module);
// Build a map of the ELF file's sections.
const ElfW(Shdr) *sections
= reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
int num_sections = elf_header->e_shnum;
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
for (int i = 0; i < num_sections; i++) {
const ElfW(Shdr) *section = &sections[i];
string name = reinterpret_cast<const char *>(section_names->sh_offset
+ section->sh_name);
const char *contents = reinterpret_cast<const char *>(section->sh_offset);
uint64 length = section->sh_size;
file_context.section_map[name] = std::make_pair(contents, length);
}
// Parse all the compilation units in the .debug_info section.
DumperLineToModule line_to_module(&byte_reader);
std::pair<const char *, uint64> debug_info_section
= file_context.section_map[".debug_info"];
// We should never have been called if the file doesn't have a
// .debug_info section.
assert(debug_info_section.first);
uint64 debug_info_length = debug_info_section.second;
for (uint64 offset = 0; offset < debug_info_length;) {
// Make a handler for the root DIE that populates MODULE with the
// data we find.
DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset);
DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter);
// Make a Dwarf2Handler that drives our DIEHandler.
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
// Make a DWARF parser for the compilation unit at OFFSET.
dwarf2reader::CompilationUnit reader(file_context.section_map,
offset,
&byte_reader,
&die_dispatcher);
// Process the entire compilation unit; get the offset of the next.
offset += reader.Start();
}
return true;
}
// Fill REGISTER_NAMES with the register names appropriate to the
// machine architecture given in HEADER, indexed by the register
// numbers used in DWARF call frame information. Return true on
// success, or false if we don't recognize HEADER's machine
// architecture.
static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header,
vector<string> *register_names)
{
static const char *const i386_names[] = {
"$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi",
"$eip", "$eflags", "$unused1",
"$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
"$unused2", "$unused3",
"$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
"$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
"$fcw", "$fsw", "$mxcsr",
"$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5",
"$tr", "$ldtr",
NULL
};
static const char *const x86_64_names[] = {
"$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp",
"$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
"$rip",
"$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
"$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15",
"$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
"$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
"$rflags",
"$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2",
"$fs.base", "$gs.base", "$unused3", "$unused4",
"$tr", "$ldtr",
"$mxcsr", "$fcw", "$fsw",
NULL
};
static const char *const arm_names[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"fps", "cpsr",
NULL
};
const char * const *name_table;
switch (elf_header->e_machine) {
case EM_386: name_table = i386_names; break;
case EM_ARM: name_table = arm_names; break;
case EM_X86_64: name_table = x86_64_names; break;
default:
return false;
}
register_names->clear();
for (int i = 0; name_table[i]; i++)
register_names->push_back(name_table[i]);
return true;
}
static bool LoadDwarfCFI(const string &dwarf_filename,
const ElfW(Ehdr) *elf_header,
const char *section_name,
const ElfW(Shdr) *section,
bool eh_frame,
const ElfW(Shdr) *got_section,
const ElfW(Shdr) *text_section,
Module *module) {
// Find the appropriate set of register names for this file's
// architecture.
vector<string> register_names;
if (!DwarfCFIRegisterNames(elf_header, &register_names)) {
fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
" cannot convert DWARF call frame information\n",
dwarf_filename.c_str(), elf_header->e_machine);
return false;
}
// Figure out what endianness this file is.
dwarf2reader::Endianness endianness;
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
endianness = dwarf2reader::ENDIANNESS_LITTLE;
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
endianness = dwarf2reader::ENDIANNESS_BIG;
else {
fprintf(stderr, "%s: bad data encoding in ELF header: %d\n",
dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]);
return false;
}
// Find the call frame information and its size.
const char *cfi = reinterpret_cast<const char *>(section->sh_offset);
size_t cfi_size = section->sh_size;
// Plug together the parser, handler, and their entourages.
DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name);
DwarfCFIToModule handler(module, register_names, &module_reporter);
dwarf2reader::ByteReader byte_reader(endianness);
// Since we're using the ElfW macro, we're not actually capable of
// processing both ELF32 and ELF64 files with the same program; that
// would take a bit more work. But this will work out well enough.
if (elf_header->e_ident[EI_CLASS] == ELFCLASS32)
byte_reader.SetAddressSize(4);
else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64)
byte_reader.SetAddressSize(8);
else {
fprintf(stderr, "%s: bad file class in ELF header: %d\n",
dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]);
return false;
}
// Provide the base addresses for .eh_frame encoded pointers, if
// possible.
byte_reader.SetCFIDataBase(section->sh_addr, cfi);
if (got_section)
byte_reader.SetDataBase(got_section->sh_addr);
if (text_section)
byte_reader.SetTextBase(got_section->sh_addr);
dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
section_name);
dwarf2reader::CallFrameInfo parser(cfi, cfi_size,
&byte_reader, &handler, &dwarf_reporter,
eh_frame);
parser.Start();
return true;
}
static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
Module *module) {
// Translate all offsets in section headers into address.
FixAddress(elf_header);
ElfW(Addr) loading_addr = GetLoadingAddress(
@ -344,18 +355,72 @@ static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) {
module->SetLoadAddress(loading_addr);
const ElfW(Shdr) *sections =
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
const ElfW(Shdr) *strtab = sections + elf_header->e_shstrndx;
const ElfW(Shdr) *stab_section =
FindSectionByName(kStabName, sections, strtab, elf_header->e_shnum);
if (stab_section == NULL) {
fprintf(stderr, "Stab section not found.\n");
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
bool found_debug_info_section = false;
// Look for STABS debugging information, and load it if present.
const ElfW(Shdr) *stab_section
= FindSectionByName(".stab", sections, section_names,
elf_header->e_shnum);
if (stab_section) {
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
if (stabstr_section) {
found_debug_info_section = true;
if (!LoadStabs(stab_section, stabstr_section, module))
fprintf(stderr, "\".stab\" section found, but failed to load STABS"
" debugging information\n");
}
}
// Look for DWARF debugging information, and load it if present.
const ElfW(Shdr) *dwarf_section
= FindSectionByName(".debug_info", sections, section_names,
elf_header->e_shnum);
if (dwarf_section) {
found_debug_info_section = true;
if (!LoadDwarf(obj_file, elf_header, module))
fprintf(stderr, "\".debug_info\" section found, but failed to load "
"DWARF debugging information\n");
}
// Dwarf Call Frame Information (CFI) is actually independent from
// the other DWARF debugging information, and can be used alone.
const ElfW(Shdr) *dwarf_cfi_section =
FindSectionByName(".debug_frame", sections, section_names,
elf_header->e_shnum);
if (dwarf_cfi_section) {
// Ignore the return value of this function; even without call frame
// information, the other debugging information could be perfectly
// useful.
LoadDwarfCFI(obj_file, elf_header, ".debug_frame",
dwarf_cfi_section, false, 0, 0, module);
}
// Linux C++ exception handling information can also provide
// unwinding data.
const ElfW(Shdr) *eh_frame_section =
FindSectionByName(".eh_frame", sections, section_names,
elf_header->e_shnum);
if (eh_frame_section) {
// Pointers in .eh_frame data may be relative to the base addresses of
// certain sections. Provide those sections if present.
const ElfW(Shdr) *got_section =
FindSectionByName(".got", sections, section_names, elf_header->e_shnum);
const ElfW(Shdr) *text_section =
FindSectionByName(".text", sections, section_names,
elf_header->e_shnum);
// As above, ignore the return value of this function.
LoadDwarfCFI(obj_file, elf_header, ".eh_frame",
eh_frame_section, true, got_section, text_section, module);
}
if (!found_debug_info_section) {
fprintf(stderr, "file contains no debugging information"
" (no \".stab\" or \".debug_info\" sections)\n");
return false;
}
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
// Load symbols.
return LoadSymbols(stab_section, stabstr_section, module);
return true;
}
//
@ -414,12 +479,18 @@ class MmapWrapper {
// ELF_HEADER.
const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) {
ElfW(Half) arch = elf_header->e_machine;
if (arch == EM_386)
return "x86";
else if (arch == EM_X86_64)
return "x86_64";
else
return NULL;
switch (arch) {
case EM_386: return "x86";
case EM_ARM: return "arm";
case EM_MIPS: return "mips";
case EM_PPC64: return "ppc64";
case EM_PPC: return "ppc";
case EM_S390: return "s390";
case EM_SPARC: return "sparc";
case EM_SPARCV9: return "sparcv9";
case EM_X86_64: return "x86_64";
default: return NULL;
}
}
// Format the Elf file identifier in IDENTIFIER as a UUID with the
@ -456,39 +527,54 @@ std::string BaseFileName(const std::string &filename) {
namespace google_breakpad {
bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
FILE *sym_file) {
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) {
int obj_fd = open(obj_file.c_str(), O_RDONLY);
if (obj_fd < 0)
if (obj_fd < 0) {
fprintf(stderr, "Failed to open ELF file '%s': %s\n",
obj_file.c_str(), strerror(errno));
return false;
}
FDWrapper obj_fd_wrapper(obj_fd);
struct stat st;
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0)
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
obj_file.c_str(), strerror(errno));
return false;
}
void *obj_base = mmap(NULL, st.st_size,
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
if (obj_base == MAP_FAILED)
if (obj_base == MAP_FAILED) {
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
obj_file.c_str(), strerror(errno));
return false;
}
MmapWrapper map_wrapper(obj_base, st.st_size);
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base);
if (!IsValidElf(elf_header))
if (!IsValidElf(elf_header)) {
fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
return false;
}
unsigned char identifier[16];
google_breakpad::FileID file_id(obj_file.c_str());
if (! file_id.ElfFileIdentifier(identifier))
if (!file_id.ElfFileIdentifier(identifier)) {
fprintf(stderr, "Unable to generate file identifier\n");
return false;
}
const char *architecture = ElfArchitecture(elf_header);
if (! architecture)
if (!architecture) {
fprintf(stderr, "Unrecognized ELF machine architecture: %d\n",
elf_header->e_machine);
return false;
}
std::string name = BaseFileName(obj_file);
std::string os = "Linux";
std::string id = FormatIdentifier(identifier);
Module module(name, os, architecture, id);
if (!LoadSymbols(elf_header, &module))
if (!LoadSymbols(obj_file, elf_header, &module))
return false;
if (!module.Write(sym_file))
return false;

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright (c) 2006, Google Inc.
// -*- mode: c++ -*-
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -26,9 +28,9 @@
// 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.
//
// dump_symbols.cc: Implements a linux stab debugging format dumper.
//
// dump_symbols.h: Read debugging information from an ELF file, and write
// it out as a Breakpad symbol file.
#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__
#define COMMON_LINUX_DUMP_SYMBOLS_H__
@ -38,11 +40,10 @@
namespace google_breakpad {
class DumpSymbols {
public:
bool WriteSymbolFile(const std::string &obj_file,
FILE *sym_file);
};
// Find all the debugging information in OBJ_FILE, an ELF executable
// or shared library, and write it to SYM_FILE in the Breakpad symbol
// file format.
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file);
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,185 @@
// -*- mode: c++ -*-
// 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>
// Implementation of google_breakpad::DwarfCFIToModule.
// See dwarf_cfi_to_module.h for details.
#include <sstream>
#include "common/linux/dwarf_cfi_to_module.h"
namespace google_breakpad {
using std::ostringstream;
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address) {
assert(!entry_);
// If dwarf2reader::CallFrameInfo can handle this version and
// augmentation, then we should be okay with that, so there's no
// need to check them here.
// Get ready to collect entries.
entry_ = new Module::StackFrameEntry;
entry_->address = address;
entry_->size = length;
entry_offset_ = offset;
return_address_ = return_address;
// Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
// may not establish any rule for .ra if the return address column
// is an ordinary register, and that register holds the return
// address on entry to the function. So establish an initial .ra
// rule citing the return address register.
if (return_address_ < register_names_.size())
entry_->initial_rules[".ra"] = register_names_[return_address_];
return true;
}
string DwarfCFIToModule::RegisterName(int i) {
assert(entry_);
if (i < 0) {
assert(i == kCFARegister);
return ".cfa";
}
unsigned reg = i;
if (reg == return_address_)
return ".ra";
if (0 <= reg && reg < register_names_.size())
return register_names_[reg];
reporter_->UnnamedRegister(entry_offset_, reg);
char buf[30];
sprintf(buf, "unnamed_register%u", reg);
return buf;
}
void DwarfCFIToModule::Record(Module::Address address, int reg,
const string &rule) {
assert(entry_);
// Is this one of this entry's initial rules?
if (address == entry_->address)
entry_->initial_rules[RegisterName(reg)] = rule;
// File it under the appropriate address.
else
entry_->rule_changes[address][RegisterName(reg)] = rule;
}
bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) {
reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg));
// Treat this as a non-fatal error.
return true;
}
bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) {
ostringstream s;
s << RegisterName(reg);
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::OffsetRule(uint64 address, int reg,
int base_register, long offset) {
ostringstream s;
s << RegisterName(base_register) << " " << offset << " + ^";
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg,
int base_register, long offset) {
ostringstream s;
s << RegisterName(base_register) << " " << offset << " +";
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::RegisterRule(uint64 address, int reg,
int base_register) {
ostringstream s;
s << RegisterName(base_register);
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg,
const string &expression) {
reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
// Treat this as a non-fatal error.
return true;
}
bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg,
const string &expression) {
reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
// Treat this as a non-fatal error.
return true;
}
bool DwarfCFIToModule::End() {
module_->AddStackFrameEntry(entry_);
entry_ = NULL;
return true;
}
void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) {
fprintf(stderr, "%s, section '%s': "
"the call frame entry at offset 0x%zx refers to register %d,"
" whose name we don't know\n",
file_.c_str(), section_.c_str(), offset, reg);
}
void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset,
const string &reg) {
fprintf(stderr, "%s, section '%s': "
"the call frame entry at offset 0x%zx sets the rule for "
"register '%s' to 'undefined', but the Breakpad symbol file format"
" cannot express this\n",
file_.c_str(), section_.c_str(), offset, reg.c_str());
}
void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset,
const string &reg) {
fprintf(stderr, "%s, section '%s': "
"the call frame entry at offset 0x%zx uses a DWARF expression to"
" describe how to recover register '%s', "
" but this translator cannot yet translate DWARF expressions to"
" Breakpad postfix expressions\n",
file_.c_str(), section_.c_str(), offset, reg.c_str());
}
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,154 @@
// -*- mode: c++ -*-
// 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>
// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which
// accepts parsed DWARF call frame info and adds it to a
// google_breakpad::Module object, which can write that information to
// a Breakpad symbol file.
#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H
#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H
#include <cassert>
#include <string>
#include <vector>
#include "common/linux/module.h"
#include "common/dwarf/dwarf2reader.h"
namespace google_breakpad {
using dwarf2reader::CallFrameInfo;
using google_breakpad::Module;
using std::string;
using std::vector;
// A class that accepts parsed call frame information from the DWARF
// CFI parser and populates a google_breakpad::Module object with the
// contents.
class DwarfCFIToModule: public CallFrameInfo::Handler {
public:
// DwarfCFIToModule uses an instance of this class to report errors
// detected while converting DWARF CFI to Breakpad STACK CFI records.
class Reporter {
public:
// Create a reporter that writes messages to the standard error
// stream. FILE is the name of the file we're processing, and
// SECTION is the name of the section within that file that we're
// looking at (.debug_frame, .eh_frame, etc.).
Reporter(const string &file, const string &section)
: file_(file), section_(section) { }
virtual ~Reporter() { }
// The DWARF CFI entry at OFFSET cites register REG, but REG is not
// covered by the vector of register names passed to the
// DwarfCFIToModule constructor, nor does it match the return
// address column number for this entry.
virtual void UnnamedRegister(size_t offset, int reg);
// The DWARF CFI entry at OFFSET says that REG is undefined, but the
// Breakpad symbol file format cannot express this.
virtual void UndefinedNotSupported(size_t offset, const string &reg);
// The DWARF CFI entry at OFFSET says that REG uses a DWARF
// expression to find its value, but DwarfCFIToModule is not
// capable of translating DWARF expressions to Breakpad postfix
// expressions.
virtual void ExpressionsNotSupported(size_t offset, const string &reg);
protected:
string file_, section_;
};
// Create a handler for the dwarf2reader::CallFrameInfo parser that
// records the stack unwinding information it receives in MODULE.
//
// Use REGISTER_NAMES[I] as the name of register number I; *this
// keeps a reference to the vector, so the vector should remain
// alive for as long as the DwarfCFIToModule does.
//
// Use REPORTER for reporting problems encountered in the conversion
// process.
DwarfCFIToModule(Module *module, const vector<string> &register_names,
Reporter *reporter)
: module_(module), register_names_(register_names), reporter_(reporter),
entry_(NULL), return_address_(-1) { }
virtual ~DwarfCFIToModule() { delete entry_; }
virtual bool Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address);
virtual bool UndefinedRule(uint64 address, int reg);
virtual bool SameValueRule(uint64 address, int reg);
virtual bool OffsetRule(uint64 address, int reg,
int base_register, long offset);
virtual bool ValOffsetRule(uint64 address, int reg,
int base_register, long offset);
virtual bool RegisterRule(uint64 address, int reg, int base_register);
virtual bool ExpressionRule(uint64 address, int reg,
const string &expression);
virtual bool ValExpressionRule(uint64 address, int reg,
const string &expression);
virtual bool End();
private:
// Return the name to use for register REG.
string RegisterName(int i);
// Record RULE for register REG at ADDRESS.
void Record(Module::Address address, int reg, const string &rule);
// The module to which we should add entries.
Module *module_;
// Map from register numbers to register names.
const vector<string> &register_names_;
// The reporter to use to report problems.
Reporter *reporter_;
// The current entry we're constructing.
Module::StackFrameEntry *entry_;
// The section offset of the current frame description entry, for
// use in error messages.
size_t entry_offset_;
// The return address column for that entry.
unsigned return_address_;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H

Просмотреть файл

@ -0,0 +1,260 @@
// 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>
// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
#include "breakpad_googletest_includes.h"
#include "common/linux/dwarf_cfi_to_module.h"
using google_breakpad::Module;
using google_breakpad::DwarfCFIToModule;
using testing::ContainerEq;
using testing::Test;
using testing::_;
struct MockCFIReporter: public DwarfCFIToModule::Reporter {
MockCFIReporter(const string &file, const string &section)
: Reporter(file, section) { }
MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string &reg));
MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string &reg));
};
struct DwarfCFIToModuleFixture {
DwarfCFIToModuleFixture()
: module("module name", "module os", "module arch", "module id"),
reporter("reporter file", "reporter section"),
handler(&module, register_names, &reporter) {
register_names.push_back("reg0");
register_names.push_back("reg1");
register_names.push_back("reg2");
register_names.push_back("reg3");
register_names.push_back("reg4");
register_names.push_back("reg5");
register_names.push_back("reg6");
register_names.push_back("reg7");
register_names.push_back("sp");
register_names.push_back("pc");
EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0);
EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0);
EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0);
}
Module module;
vector<string> register_names;
MockCFIReporter reporter;
DwarfCFIToModule handler;
vector<Module::StackFrameEntry *> entries;
};
class Entry: public DwarfCFIToModuleFixture, public Test { };
TEST_F(Entry, Accept) {
ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL,
0xb440ce248169c8d6ULL, 3, "", 0xea93c106));
ASSERT_TRUE(handler.End());
module.GetStackFrameEntries(&entries);
EXPECT_EQ(1U, entries.size());
EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address);
EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size);
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Entry, AcceptOldVersion) {
ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL,
0xc771f44958d40bbcULL, 1, "", 0x093c945e));
ASSERT_TRUE(handler.End());
module.GetStackFrameEntries(&entries);
EXPECT_EQ(1U, entries.size());
EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address);
EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size);
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
struct RuleFixture: public DwarfCFIToModuleFixture {
RuleFixture() : DwarfCFIToModuleFixture() {
entry_address = 0x89327ebf86b47492ULL;
entry_size = 0x2f8cd573072fe02aULL;
return_reg = 0x7886a346;
}
void StartEntry() {
ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size,
3, "", return_reg));
}
void CheckEntry() {
module.GetStackFrameEntries(&entries);
EXPECT_EQ(1U, entries.size());
EXPECT_EQ(entry_address, entries[0]->address);
EXPECT_EQ(entry_size, entries[0]->size);
}
uint64 entry_address, entry_size;
unsigned return_reg;
};
class Rule: public RuleFixture, public Test { };
TEST_F(Rule, UndefinedRule) {
EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7"));
StartEntry();
ASSERT_TRUE(handler.UndefinedRule(entry_address, 7));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, SameValueRule) {
StartEntry();
ASSERT_TRUE(handler.SameValueRule(entry_address, 6));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial["reg6"] = "reg6";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, OffsetRule) {
StartEntry();
ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg,
DwarfCFIToModule::kCFARegister,
16927065));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST_F(Rule, OffsetRuleNegative) {
StartEntry();
ASSERT_TRUE(handler.OffsetRule(entry_address + 1,
DwarfCFIToModule::kCFARegister, 4, -34530721));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST_F(Rule, ValOffsetRule) {
// Use an unnamed register number, to exercise that branch of RegisterName.
EXPECT_CALL(reporter, UnnamedRegister(_, 10));
StartEntry();
ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
DwarfCFIToModule::kCFARegister,
10, 61812979));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 0x5ab7][".cfa"] =
"unnamed_register10 61812979 +";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST_F(Rule, RegisterRule) {
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg3";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, ExpressionRule) {
EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2"));
StartEntry();
ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2,
"it takes two to tango"));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, ValExpressionRule) {
EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0"));
StartEntry();
ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0,
"bit off more than he could chew"));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, DefaultReturnAddressRule) {
return_reg = 2;
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg2";
expected_initial["reg0"] = "reg1";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, DefaultReturnAddressRuleOverride) {
return_reg = 2;
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg1";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, DefaultReturnAddressRuleLater) {
return_reg = 2;
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg2";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 1][".ra"] = "reg1";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}

Просмотреть файл

@ -0,0 +1,877 @@
// 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>
// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h.
#include <algorithm>
#include <cassert>
#include "common/linux/dwarf_cu_to_module.h"
#include "common/linux/dwarf_line_to_module.h"
namespace google_breakpad {
using std::map;
using std::vector;
// Data provided by a DWARF specification DIE.
//
// In DWARF, the DIE for a definition may contain a DW_AT_specification
// attribute giving the offset of the corresponding declaration DIE, and
// the definition DIE may omit information given in the declaration. For
// example, it's common for a function's address range to appear only in
// its definition DIE, but its name to appear only in its declaration
// DIE.
//
// The dumper needs to be able to follow DW_AT_specification links to
// bring all this information together in a FUNC record. Conveniently,
// DIEs that are the target of such links have a DW_AT_declaration flag
// set, so we can identify them when we first see them, and record their
// contents for later reference.
//
// A Specification holds information gathered from a declaration DIE that
// we may need if we find a DW_AT_specification link pointing to it.
struct DwarfCUToModule::Specification {
// The name of the enclosing scope, or the empty string if there is none.
string enclosing_name;
// The name for the specification DIE itself, without any enclosing
// name components.
string unqualified_name;
};
// An abstract origin -- base definition of an inline function.
struct AbstractOrigin {
AbstractOrigin() : name() {}
AbstractOrigin(const string& name) : name(name) {}
string name;
};
typedef map<uint64, AbstractOrigin> AbstractOriginByOffset;
// Data global to the DWARF-bearing file that is private to the
// DWARF-to-Module process.
struct DwarfCUToModule::FilePrivate {
// A map from offsets of DIEs within the .debug_info section to
// Specifications describing those DIEs. Specification references can
// cross compilation unit boundaries.
SpecificationByOffset specifications;
AbstractOriginByOffset origins;
};
DwarfCUToModule::FileContext::FileContext(const string &filename_arg,
Module *module_arg)
: filename(filename_arg), module(module_arg) {
file_private = new FilePrivate();
}
DwarfCUToModule::FileContext::~FileContext() {
delete file_private;
}
// Information global to the particular compilation unit we're
// parsing. This is for data shared across the CU's entire DIE tree,
// and parameters from the code invoking the CU parser.
struct DwarfCUToModule::CUContext {
CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg)
: file_context(file_context_arg),
reporter(reporter_arg),
language(Language::CPlusPlus) { }
~CUContext() {
for (vector<Module::Function *>::iterator it = functions.begin();
it != functions.end(); it++)
delete *it;
};
// The DWARF-bearing file into which this CU was incorporated.
FileContext *file_context;
// For printing error messages.
WarningReporter *reporter;
// The source language of this compilation unit.
const Language *language;
// The functions defined in this compilation unit. We accumulate
// them here during parsing. Then, in DwarfCUToModule::Finish, we
// assign them lines and add them to file_context->module.
//
// Destroying this destroys all the functions this vector points to.
vector<Module::Function *> functions;
};
// Information about the context of a particular DIE. This is for
// information that changes as we descend the tree towards the leaves:
// the containing classes/namespaces, etc.
struct DwarfCUToModule::DIEContext {
// The fully-qualified name of the context. For example, for a
// tree like:
//
// DW_TAG_namespace Foo
// DW_TAG_class Bar
// DW_TAG_subprogram Baz
//
// in a C++ compilation unit, the DIEContext's name for the
// DW_TAG_subprogram DIE would be "Foo::Bar". The DIEContext's
// name for the DW_TAG_namespace DIE would be "".
string name;
};
// An abstract base class for all the dumper's DIE handlers.
class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
public:
// Create a handler for the DIE at OFFSET whose compilation unit is
// described by CU_CONTEXT, and whose immediate context is described
// by PARENT_CONTEXT.
GenericDIEHandler(CUContext *cu_context, DIEContext *parent_context,
uint64 offset)
: cu_context_(cu_context),
parent_context_(parent_context),
offset_(offset),
declaration_(false),
specification_(NULL) { }
// Derived classes' ProcessAttributeUnsigned can defer to this to
// handle DW_AT_declaration, or simply not override it.
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
// Derived classes' ProcessAttributeReference can defer to this to
// handle DW_AT_specification, or simply not override it.
void ProcessAttributeReference(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
// Derived classes' ProcessAttributeReference can defer to this to
// handle DW_AT_specification, or simply not override it.
void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string &data);
protected:
// Compute and return the fully-qualified name of the DIE. If this
// DIE is a declaration DIE, to be cited by other DIEs'
// DW_AT_specification attributes, record its enclosing name and
// unqualified name in the specification table.
//
// Use this from EndAttributes member functions, not ProcessAttribute*
// functions; only the former can be sure that all the DIE's attributes
// have been seen.
string ComputeQualifiedName();
CUContext *cu_context_;
DIEContext *parent_context_;
uint64 offset_;
// If this DIE has a DW_AT_declaration attribute, this is its value.
// It is false on DIEs with no DW_AT_declaration attribute.
bool declaration_;
// If this DIE has a DW_AT_specification attribute, this is the
// Specification structure for the DIE the attribute refers to.
// Otherwise, this is NULL.
Specification *specification_;
// The value of the DW_AT_name attribute, or the empty string if the
// DIE has no such attribute.
string name_attribute_;
};
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned(
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
switch (attr) {
case dwarf2reader::DW_AT_declaration: declaration_ = (data != 0); break;
default: break;
}
}
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
switch (attr) {
case dwarf2reader::DW_AT_specification: {
// Find the Specification to which this attribute refers, and
// set specification_ appropriately. We could do more processing
// here, but it's better to leave the real work to our
// EndAttribute member function, at which point we know we have
// seen all the DIE's attributes.
FileContext *file_context = cu_context_->file_context;
SpecificationByOffset *specifications
= &file_context->file_private->specifications;
SpecificationByOffset::iterator spec = specifications->find(data);
if (spec != specifications->end()) {
specification_ = &spec->second;
} else {
// Technically, there's no reason a DW_AT_specification
// couldn't be a forward reference, but supporting that would
// be a lot of work (changing to a two-pass structure), and I
// don't think any producers we care about ever emit such
// things.
cu_context_->reporter->UnknownSpecification(offset_, data);
}
break;
}
default: break;
}
}
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
switch (attr) {
case dwarf2reader::DW_AT_name: name_attribute_ = data; break;
default: break;
}
}
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
// Find our unqualified name. If the DIE has its own DW_AT_name
// attribute, then use that; otherwise, check our specification.
const string *unqualified_name;
if (name_attribute_.empty() && specification_)
unqualified_name = &specification_->unqualified_name;
else
unqualified_name = &name_attribute_;
// Find the name of our enclosing context. If we have a
// specification, it's the specification's enclosing context that
// counts; otherwise, use this DIE's context.
const string *enclosing_name;
if (specification_)
enclosing_name = &specification_->enclosing_name;
else
enclosing_name = &parent_context_->name;
// If this DIE was marked as a declaration, record its names in the
// specification table.
if (declaration_) {
FileContext *file_context = cu_context_->file_context;
Specification spec;
spec.enclosing_name = *enclosing_name;
spec.unqualified_name = *unqualified_name;
file_context->file_private->specifications[offset_] = spec;
}
// Combine the enclosing name and unqualified name to produce our
// own fully-qualified name.
return cu_context_->language->MakeQualifiedName(*enclosing_name,
*unqualified_name);
}
// A handler class for DW_TAG_subprogram DIEs.
class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
public:
FuncHandler(CUContext *cu_context, DIEContext *parent_context,
uint64 offset)
: GenericDIEHandler(cu_context, parent_context, offset),
low_pc_(0), high_pc_(0), abstract_origin_(NULL), inline_(false) { }
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form,
int64 data);
void ProcessAttributeReference(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
bool EndAttributes();
void Finish();
private:
// The fully-qualified name, as derived from name_attribute_,
// specification_, parent_context_. Computed in EndAttributes.
string name_;
uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc
const AbstractOrigin* abstract_origin_;
bool inline_;
};
void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
switch (attr) {
// If this attribute is present at all --- even if its value is
// DW_INL_not_inlined --- then GCC may cite it as someone else's
// DW_AT_abstract_origin attribute.
case dwarf2reader::DW_AT_inline: inline_ = true; break;
case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break;
case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break;
default:
GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data);
break;
}
}
void DwarfCUToModule::FuncHandler::ProcessAttributeSigned(
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) {
switch (attr) {
// If this attribute is present at all --- even if its value is
// DW_INL_not_inlined --- then GCC may cite it as someone else's
// DW_AT_abstract_origin attribute.
case dwarf2reader::DW_AT_inline: inline_ = true; break;
default:
break;
}
}
void DwarfCUToModule::FuncHandler::ProcessAttributeReference(
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
switch(attr) {
case dwarf2reader::DW_AT_abstract_origin: {
const AbstractOriginByOffset& origins =
cu_context_->file_context->file_private->origins;
AbstractOriginByOffset::const_iterator origin = origins.find(data);
if (origin != origins.end()) {
abstract_origin_ = &(origin->second);
} else {
cu_context_->reporter->UnknownAbstractOrigin(offset_, data);
}
break;
}
default:
GenericDIEHandler::ProcessAttributeReference(attr, form, data);
break;
}
}
bool DwarfCUToModule::FuncHandler::EndAttributes() {
// Compute our name, and record a specification, if appropriate.
name_ = ComputeQualifiedName();
if (name_.empty() && abstract_origin_) {
name_ = abstract_origin_->name;
}
return true;
}
void DwarfCUToModule::FuncHandler::Finish() {
// Did we collect the information we need? Not all DWARF function
// entries have low and high addresses (for example, inlined
// functions that were never used), but all the ones we're
// interested in cover a non-empty range of bytes.
if (low_pc_ < high_pc_) {
// Create a Module::Function based on the data we've gathered, and
// add it to the functions_ list.
Module::Function *func = new Module::Function;
func->name = name_;
func->address = low_pc_;
func->size = high_pc_ - low_pc_;
func->parameter_size = 0;
cu_context_->functions.push_back(func);
} else if (inline_) {
AbstractOrigin origin(name_);
cu_context_->file_context->file_private->origins[offset_] = origin;
}
}
// A handler for DIEs that contain functions and contribute a
// component to their names: namespaces, classes, etc.
class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler {
public:
NamedScopeHandler(CUContext *cu_context, DIEContext *parent_context,
uint64 offset)
: GenericDIEHandler(cu_context, parent_context, offset) { }
bool EndAttributes();
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs);
private:
DIEContext child_context_; // A context for our children.
};
bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
child_context_.name = ComputeQualifiedName();
return true;
}
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
uint64 offset,
enum DwarfTag tag,
const AttributeList &attrs) {
switch (tag) {
case dwarf2reader::DW_TAG_subprogram:
return new FuncHandler(cu_context_, &child_context_, offset);
case dwarf2reader::DW_TAG_namespace:
case dwarf2reader::DW_TAG_class_type:
case dwarf2reader::DW_TAG_structure_type:
case dwarf2reader::DW_TAG_union_type:
return new NamedScopeHandler(cu_context_, &child_context_, offset);
default:
return NULL;
}
};
void DwarfCUToModule::WarningReporter::CUHeading() {
if (printed_cu_header_)
return;
fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%llx):\n",
filename_.c_str(), cu_name_.c_str(), cu_offset_);
printed_cu_header_ = true;
}
void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset,
uint64 target) {
CUHeading();
fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_specification"
" attribute referring to the die at offset 0x%llx, which either"
" was not marked as a declaration, or comes later in the file\n",
filename_.c_str(), offset, target);
}
void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64 offset,
uint64 target) {
CUHeading();
fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_abstract_origin"
" attribute referring to the die at offset 0x%llx, which either"
" was not marked as an inline, or comes later in the file\n",
filename_.c_str(), offset, target);
}
void DwarfCUToModule::WarningReporter::MissingSection(const string &name) {
CUHeading();
fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n",
filename_.c_str(), name.c_str());
}
void DwarfCUToModule::WarningReporter::BadLineInfoOffset(uint64 offset) {
CUHeading();
fprintf(stderr, "%s: warning: line number data offset beyond end"
" of '.debug_line' section\n",
filename_.c_str());
}
void DwarfCUToModule::WarningReporter::UncoveredHeading() {
if (printed_unpaired_header_)
return;
CUHeading();
fprintf(stderr, "%s: warning: skipping unpaired lines/functions:\n",
filename_.c_str());
printed_unpaired_header_ = true;
}
void DwarfCUToModule::WarningReporter::UncoveredFunction(
const Module::Function &function) {
UncoveredHeading();
fprintf(stderr, " function%s: %s\n",
function.size == 0 ? " (zero-length)" : "",
function.name.c_str());
}
void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) {
UncoveredHeading();
fprintf(stderr, " line%s: %s:%d at 0x%llx\n",
(line.size == 0 ? " (zero-length)" : ""),
line.file->name.c_str(), line.number, line.address);
}
DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
LineToModuleFunctor *line_reader,
WarningReporter *reporter)
: line_reader_(line_reader), has_source_line_info_(false) {
cu_context_ = new CUContext(file_context, reporter);
child_context_ = new DIEContext();
}
DwarfCUToModule::~DwarfCUToModule() {
delete cu_context_;
delete child_context_;
}
void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) {
switch (attr) {
case dwarf2reader::DW_AT_language: // source language of this CU
SetLanguage(static_cast<DwarfLanguage>(data));
break;
default:
break;
}
}
void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
switch (attr) {
case dwarf2reader::DW_AT_stmt_list: // Line number information.
has_source_line_info_ = true;
source_line_offset_ = data;
break;
case dwarf2reader::DW_AT_language: // source language of this CU
SetLanguage(static_cast<DwarfLanguage>(data));
break;
default:
break;
}
}
void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
if (attr == dwarf2reader::DW_AT_name)
cu_context_->reporter->SetCUName(data);
}
bool DwarfCUToModule::EndAttributes() {
return true;
}
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
uint64 offset,
enum DwarfTag tag,
const AttributeList &attrs) {
switch (tag) {
case dwarf2reader::DW_TAG_subprogram:
return new FuncHandler(cu_context_, child_context_, offset);
case dwarf2reader::DW_TAG_namespace:
case dwarf2reader::DW_TAG_class_type:
case dwarf2reader::DW_TAG_structure_type:
case dwarf2reader::DW_TAG_union_type:
return new NamedScopeHandler(cu_context_, child_context_, offset);
default:
return NULL;
}
}
void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
switch (language) {
case dwarf2reader::DW_LANG_Java:
cu_context_->language = Language::Java;
break;
// DWARF has no generic language code for assembly language; this is
// what the GNU toolchain uses.
case dwarf2reader::DW_LANG_Mips_Assembler:
cu_context_->language = Language::Assembler;
break;
// C++ covers so many cases that it probably has some way to cope
// with whatever the other languages throw at us. So make it the
// default.
//
// Objective C and Objective C++ seem to create entries for
// methods whose DW_AT_name values are already fully-qualified:
// "-[Classname method:]". These appear at the top level.
//
// DWARF data for C should never include namespaces or functions
// nested in struct types, but if it ever does, then C++'s
// notation is probably not a bad choice for that.
default:
case dwarf2reader::DW_LANG_ObjC:
case dwarf2reader::DW_LANG_ObjC_plus_plus:
case dwarf2reader::DW_LANG_C:
case dwarf2reader::DW_LANG_C89:
case dwarf2reader::DW_LANG_C99:
case dwarf2reader::DW_LANG_C_plus_plus:
cu_context_->language = Language::CPlusPlus;
break;
}
}
void DwarfCUToModule::ReadSourceLines(uint64 offset) {
const dwarf2reader::SectionMap &section_map
= cu_context_->file_context->section_map;
dwarf2reader::SectionMap::const_iterator map_entry
= section_map.find(".debug_line");
if (map_entry == section_map.end()) {
cu_context_->reporter->MissingSection(".debug_line");
return;
}
const char *section_start = map_entry->second.first;
uint64 section_length = map_entry->second.second;
if (offset >= section_length) {
cu_context_->reporter->BadLineInfoOffset(offset);
return;
}
(*line_reader_)(section_start + offset, section_length - offset,
cu_context_->file_context->module, &lines_);
}
namespace {
// Return true if ADDRESS falls within the range of ITEM.
template <class T>
inline bool within(const T &item, Module::Address address) {
// Because Module::Address is unsigned, and unsigned arithmetic
// wraps around, this will be false if ADDRESS falls before the
// start of ITEM, or if it falls after ITEM's end.
return address - item.address < item.size;
}
}
void DwarfCUToModule::AssignLinesToFunctions() {
vector<Module::Function *> *functions = &cu_context_->functions;
WarningReporter *reporter = cu_context_->reporter;
// This would be simpler if we assumed that source line entries
// don't cross function boundaries. However, there's no real reason
// to assume that (say) a series of function definitions on the same
// line wouldn't get coalesced into one line number entry. The
// DWARF spec certainly makes no such promises.
//
// So treat the functions and lines as peers, and take the trouble
// to compute their ranges' intersections precisely. In any case,
// the hair here is a constant factor for performance; the
// complexity from here on out is linear.
// Put both our functions and lines in order by address.
sort(functions->begin(), functions->end(),
Module::Function::CompareByAddress);
sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress);
// The last line that we used any piece of. We use this only for
// generating warnings.
const Module::Line *last_line_used = NULL;
// The last function and line we warned about --- so we can avoid
// doing so more than once.
const Module::Function *last_function_cited = NULL;
const Module::Line *last_line_cited = NULL;
// Make a single pass through both vectors from lower to higher
// addresses, populating each Function's lines vector with lines
// from our lines_ vector that fall within the function's address
// range.
vector<Module::Function *>::iterator func_it = functions->begin();
vector<Module::Line>::const_iterator line_it = lines_.begin();
Module::Address current;
// Pointers to the referents of func_it and line_it, or NULL if the
// iterator is at the end of the sequence.
Module::Function *func;
const Module::Line *line;
// Start current at the beginning of the first line or function,
// whichever is earlier.
if (func_it != functions->end() && line_it != lines_.end()) {
func = *func_it;
line = &*line_it;
current = std::min(func->address, line->address);
} else if (line_it != lines_.end()) {
func = NULL;
line = &*line_it;
current = line->address;
} else if (func_it != functions->end()) {
func = *func_it;
line = NULL;
current = (*func_it)->address;
} else {
return;
}
while (func || line) {
// This loop has two invariants that hold at the top.
//
// First, at least one of the iterators is not at the end of its
// sequence, and those that are not refer to the earliest
// function or line that contains or starts after CURRENT.
//
// Note that every byte is in one of four states: it is covered
// or not covered by a function, and, independently, it is
// covered or not covered by a line.
//
// The second invariant is that CURRENT refers to a byte whose
// state is different from its predecessor, or it refers to the
// first byte in the address space. In other words, CURRENT is
// always the address of a transition.
//
// Note that, although each iteration advances CURRENT from one
// transition address to the next in each iteration, it might
// not advance the iterators. Suppose we have a function that
// starts with a line, has a gap, and then a second line, and
// suppose that we enter an iteration with CURRENT at the end of
// the first line. The next transition address is the start of
// the second line, after the gap, so the iteration should
// advance CURRENT to that point. At the head of that iteration,
// the invariants require that the line iterator be pointing at
// the second line. But this is also true at the head of the
// next. And clearly, the iteration must not change the function
// iterator. So neither iterator moves.
// Assert the first invariant (see above).
assert(!func || current < func->address || within(*func, current));
assert(!line || current < line->address || within(*line, current));
// The next transition after CURRENT.
Module::Address next_transition;
// Figure out which state we're in, add lines or warn, and compute
// the next transition address.
if (func && current >= func->address) {
if (line && current >= line->address) {
// Covered by both a line and a function.
Module::Address func_left = func->size - (current - func->address);
Module::Address line_left = line->size - (current - line->address);
// This may overflow, but things work out.
next_transition = current + std::min(func_left, line_left);
Module::Line l = *line;
l.address = current;
l.size = next_transition - current;
func->lines.push_back(l);
last_line_used = line;
} else {
// Covered by a function, but no line.
if (func != last_function_cited) {
reporter->UncoveredFunction(*func);
last_function_cited = func;
}
if (line && within(*func, line->address))
next_transition = line->address;
else
// If this overflows, we'll catch it below.
next_transition = func->address + func->size;
}
} else {
if (line && current >= line->address) {
// Covered by a line, but no function.
//
// If GCC emits padding after one function to align the start
// of the next, then it will attribute the padding
// instructions to the last source line of function (to reduce
// the size of the line number info), but omit it from the
// DW_AT_{low,high}_pc range given in .debug_info (since it
// costs nothing to be precise there). If we did use at least
// some of the line we're about to skip, and it ends at the
// start of the next function, then assume this is what
// happened, and don't warn.
if (line != last_line_cited
&& !(func
&& line == last_line_used
&& func->address - line->address == line->size)) {
reporter->UncoveredLine(*line);
last_line_cited = line;
}
if (func && within(*line, func->address))
next_transition = func->address;
else
// If this overflows, we'll catch it below.
next_transition = line->address + line->size;
} else {
// Covered by neither a function nor a line. By the invariant,
// both func and line begin after CURRENT. The next transition
// is the start of the next function or next line, whichever
// is earliest.
assert (func || line);
if (func && line)
next_transition = std::min(func->address, line->address);
else if (func)
next_transition = func->address;
else
next_transition = line->address;
}
}
// If a function or line abuts the end of the address space, then
// next_transition may end up being zero, in which case we've completed
// our pass. Handle that here, instead of trying to deal with it in
// each place we compute next_transition.
if (!next_transition)
break;
// Advance iterators as needed. If lines overlap or functions overlap,
// then we could go around more than once. We don't worry too much
// about what result we produce in that case, just as long as we don't
// hang or crash.
while (func_it != functions->end()
&& current >= (*func_it)->address
&& !within(**func_it, next_transition))
func_it++;
func = (func_it != functions->end()) ? *func_it : NULL;
while (line_it != lines_.end()
&& current >= line_it->address
&& !within(*line_it, next_transition))
line_it++;
line = (line_it != lines_.end()) ? &*line_it : NULL;
// We must make progress.
assert(next_transition > current);
current = next_transition;
}
}
void DwarfCUToModule::Finish() {
// Assembly language files have no function data, and that gives us
// no place to store our line numbers (even though the GNU toolchain
// will happily produce source line info for assembly language
// files). To avoid spurious warnings about lines we can't assign
// to functions, skip CUs in languages that lack functions.
if (!cu_context_->language->HasFunctions())
return;
// Read source line info, if we have any.
if (has_source_line_info_)
ReadSourceLines(source_line_offset_);
vector<Module::Function *> *functions = &cu_context_->functions;
// Dole out lines to the appropriate functions.
AssignLinesToFunctions();
// Add our functions, which now have source lines assigned to them,
// to module_.
cu_context_->file_context->module->AddFunctions(functions->begin(),
functions->end());
// Ownership of the function objects has shifted from cu_context to
// the Module.
functions->clear();
}
bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
uint8 address_size,
uint8 offset_size,
uint64 cu_length,
uint8 dwarf_version) {
return dwarf_version >= 2;
}
bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs) {
// We don't deal with partial compilation units (the only other tag
// likely to be used for root DIE).
return tag == dwarf2reader::DW_TAG_compile_unit;
}
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,259 @@
// -*- mode: c++ -*-
// 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>
// Add DWARF debugging information to a Breakpad symbol file. This
// file defines the DwarfCUToModule class, which accepts parsed DWARF
// data and populates a google_breakpad::Module with the results; the
// Module can then write its contents as a Breakpad symbol file.
#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__
#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__
#include <string>
#include <elf.h>
#include <link.h>
#include "common/linux/language.h"
#include "common/linux/module.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/dwarf/dwarf2reader.h"
namespace google_breakpad {
using dwarf2reader::AttributeList;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfLanguage;
using dwarf2reader::DwarfTag;
// Populate a google_breakpad::Module with DWARF debugging information.
//
// An instance of this class can be provided as a handler to a
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the
// results of parsing to populate a google_breakpad::Module with
// source file, function, and source line information.
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
struct FilePrivate;
public:
// Information global to the DWARF-bearing file we are processing,
// for use by DwarfCUToModule. Each DwarfCUToModule instance deals
// with a single compilation unit within the file, but information
// global to the whole file is held here. The client is responsible
// for filling it in appropriately (except for the 'file_private'
// field, which the constructor and destructor take care of), and
// then providing it to the DwarfCUToModule instance for each
// compilation unit we process in that file.
struct FileContext {
FileContext(const string &filename_arg, Module *module_arg);
~FileContext();
// The name of this file, for use in error messages.
string filename;
// A map of this file's sections, used for finding other DWARF
// sections that the .debug_info section may refer to.
dwarf2reader::SectionMap section_map;
// The Module to which we're contributing definitions.
Module *module;
// Inter-compilation unit data used internally by the handlers.
FilePrivate *file_private;
};
// An abstract base class for functors that handle DWARF line data
// for DwarfCUToModule. DwarfCUToModule could certainly just use
// dwarf2reader::LineInfo itself directly, but decoupling things
// this way makes unit testing a little easier.
class LineToModuleFunctor {
public:
LineToModuleFunctor() { }
virtual ~LineToModuleFunctor() { }
// Populate MODULE and LINES with source file names and code/line
// mappings, given a pointer to some DWARF line number data
// PROGRAM, and an overestimate of its size. Add no zero-length
// lines to LINES.
virtual void operator()(const char *program, uint64 length,
Module *module, vector<Module::Line> *lines) = 0;
};
// The interface DwarfCUToModule uses to report warnings. The member
// function definitions for this class write messages to stderr, but
// you can override them if you'd like to detect or report these
// conditions yourself.
class WarningReporter {
public:
// Warn about problems in the DWARF file FILENAME, in the
// compilation unit at OFFSET.
WarningReporter(const string &filename, uint64 cu_offset)
: filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false),
printed_unpaired_header_(false) { }
virtual ~WarningReporter() { }
// Set the name of the compilation unit we're processing to NAME.
virtual void SetCUName(const string &name) { cu_name_ = name; }
// A DW_AT_specification in the DIE at OFFSET refers to a DIE we
// haven't processed yet, or that wasn't marked as a declaration,
// at TARGET.
virtual void UnknownSpecification(uint64 offset, uint64 target);
// A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we
// haven't processed yet, or that wasn't marked as inline, at TARGET.
virtual void UnknownAbstractOrigin(uint64 offset, uint64 target);
// We were unable to find the DWARF section named SECTION_NAME.
virtual void MissingSection(const string &section_name);
// The CU's DW_AT_stmt_list offset OFFSET is bogus.
virtual void BadLineInfoOffset(uint64 offset);
// FUNCTION includes code covered by no line number data.
virtual void UncoveredFunction(const Module::Function &function);
// Line number NUMBER in LINE_FILE, of length LENGTH, includes code
// covered by no function.
virtual void UncoveredLine(const Module::Line &line);
protected:
string filename_;
uint64 cu_offset_;
string cu_name_;
bool printed_cu_header_;
bool printed_unpaired_header_;
private:
// Print a per-CU heading, once.
void CUHeading();
// Print an unpaired function/line heading, once.
void UncoveredHeading();
};
// Create a DWARF debugging info handler for a compilation unit
// within FILE_CONTEXT. This uses information received from the
// dwarf2reader::CompilationUnit DWARF parser to populate
// FILE_CONTEXT->module. Use LINE_READER to handle the compilation
// unit's line number data. Use REPORTER to report problems with the
// data we find.
DwarfCUToModule(FileContext *file_context,
LineToModuleFunctor *line_reader,
WarningReporter *reporter);
~DwarfCUToModule();
void ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form,
int64 data);
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string &data);
bool EndAttributes();
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
const AttributeList &attrs);
// Assign all our source Lines to the Functions that cover their
// addresses, and then add them to module_.
void Finish();
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
bool StartRootDIE(uint64 offset, enum DwarfTag tag,
const AttributeList& attrs);
private:
// Used internally by the handler. Full definitions are in
// dwarf_cu_to_module.cc.
struct FilePrivate;
struct Specification;
struct CUContext;
struct DIEContext;
class GenericDIEHandler;
class FuncHandler;
class NamedScopeHandler;
// A map from section offsets to specifications.
typedef map<uint64, Specification> SpecificationByOffset;
// Set this compilation unit's source language to LANGUAGE.
void SetLanguage(DwarfLanguage language);
// Read source line information at OFFSET in the .debug_line
// section. Record source files in module_, but record source lines
// in lines_; we apportion them to functions in
// AssignLinesToFunctions.
void ReadSourceLines(uint64 offset);
// Assign the lines in lines_ to the individual line lists of the
// functions in functions_. (DWARF line information maps an entire
// compilation unit at a time, and gives no indication of which
// lines belong to which functions, beyond their addresses.)
void AssignLinesToFunctions();
// The only reason cu_context_ and child_context_ are pointers is
// that we want to keep their definitions private to
// dwarf_cu_to_module.cc, instead of listing them all here. They are
// owned by this DwarfCUToModule: the constructor sets them, and the
// destructor deletes them.
// The functor to use to handle line number data.
LineToModuleFunctor *line_reader_;
// This compilation unit's context.
CUContext *cu_context_;
// A context for our children.
DIEContext *child_context_;
// True if this compilation unit has source line information.
bool has_source_line_info_;
// The offset of this compilation unit's line number information in
// the .debug_line section.
uint64 source_line_offset_;
// The line numbers we have seen thus far. We accumulate these here
// during parsing. Then, in Finish, we call AssignLinesToFunctions
// to dole them out to the appropriate functions.
vector<Module::Line> lines_;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,132 @@
// 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>
// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
// See dwarf_line_to_module.h for details.
#include "common/linux/dwarf_line_to_module.h"
// Trying to support Windows paths in a reasonable way adds a lot of
// variations to test; it would be better to just put off dealing with
// it until we actually have to deal with DWARF on Windows.
// Return true if PATH is an absolute path, false if it is relative.
static bool PathIsAbsolute(const string &path) {
return (path.size() >= 1 && path[0] == '/');
}
// If PATH is an absolute path, return PATH. If PATH is a relative path,
// treat it as relative to BASE and return the combined path.
static string ExpandPath(const string &path, const string &base) {
if (PathIsAbsolute(path))
return path;
return base + "/" + path;
}
namespace google_breakpad {
void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) {
// Directory number zero is reserved to mean the compilation
// directory. Silently ignore attempts to redefine it.
if (dir_num != 0)
directories_[dir_num] = name;
}
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length) {
if (file_num == -1)
file_num = ++highest_file_number_;
else if (file_num > highest_file_number_)
highest_file_number_ = file_num;
std::string full_name;
if (dir_num != 0) {
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
if (directory_it != directories_.end()) {
full_name = ExpandPath(name, directory_it->second);
} else {
if (!warned_bad_directory_number_) {
fprintf(stderr, "warning: DWARF line number data refers to undefined"
" directory numbers\n");
warned_bad_directory_number_ = true;
}
full_name = name; // just treat name as relative
}
} else {
// Directory number zero is the compilation directory; we just report
// relative paths in that case.
full_name = name;
}
// Find a Module::File object of the given name, and add it to the
// file table.
files_[file_num] = module_->FindFile(full_name);
}
void DwarfLineToModule::AddLine(uint64 address, uint64 length,
uint32 file_num, uint32 line_num,
uint32 column_num) {
if (length == 0)
return;
// Clip lines not to extend beyond the end of the address space.
if (address + length < address)
length = -address;
// Should we omit this line? (See the comments for omitted_line_end_.)
if (address == 0 || address == omitted_line_end_) {
omitted_line_end_ = address + length;
return;
} else {
omitted_line_end_ = 0;
}
// Find the source file being referred to.
Module::File *file = files_[file_num];
if (!file) {
if (!warned_bad_file_number_) {
fprintf(stderr, "warning: DWARF line number data refers to "
"undefined file numbers\n");
warned_bad_file_number_ = true;
}
return;
}
Module::Line line;
line.address = address;
// We set the size when we get the next line or the EndSequence call.
line.size = length;
line.file = file;
line.number = line_num;
lines_->push_back(line);
}
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,179 @@
// -*- mode: c++ -*-
// 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>
// The DwarfLineToModule class accepts line number information from a
// DWARF parser and adds it to a google_breakpad::Module. The Module
// can write that data out as a Breakpad symbol file.
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#include "common/linux/module.h"
#include "common/dwarf/dwarf2reader.h"
namespace google_breakpad {
// A class for producing a vector of google_breakpad::Module::Line
// instances from parsed DWARF line number data.
//
// An instance of this class can be provided as a handler to a
// dwarf2reader::LineInfo DWARF line number information parser. The
// handler accepts source location information from the parser and
// uses it to produce a vector of google_breakpad::Module::Line
// objects, referring to google_breakpad::Module::File objects added
// to a particular google_breakpad::Module.
//
// GNU toolchain omitted sections support:
// ======================================
//
// Given the right options, the GNU toolchain will omit unreferenced
// functions from the final executable. Unfortunately, when it does so, it
// does not remove the associated portions of the DWARF line number
// program; instead, it gives the DW_LNE_set_address instructions referring
// to the now-deleted code addresses of zero. Given this input, the DWARF
// line parser will call AddLine with a series of lines starting at address
// zero. For example, here is the output from 'readelf -wl' for a program
// with four functions, the first three of which have been omitted:
//
// Line Number Statements:
// Extended opcode 2: set Address to 0x0
// Advance Line by 14 to 15
// Copy
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
// Advance PC by 2 to 0xd
// Extended opcode 1: End of Sequence
//
// Extended opcode 2: set Address to 0x0
// Advance Line by 14 to 15
// Copy
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
// Advance PC by 2 to 0xd
// Extended opcode 1: End of Sequence
//
// Extended opcode 2: set Address to 0x0
// Advance Line by 19 to 20
// Copy
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21
// Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22
// Advance PC by 2 to 0xa
// Extended opcode 1: End of Sequence
//
// Extended opcode 2: set Address to 0x80483a4
// Advance Line by 23 to 24
// Copy
// Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25
// Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26
// Advance PC by 6 to 0x80483bd
// Extended opcode 1: End of Sequence
//
// Instead of collecting runs of lines describing code that is not there,
// we try to recognize and drop them. Since the linker doesn't explicitly
// distinguish references to dropped sections from genuine references to
// code at address zero, we must use a heuristic. We have chosen:
//
// - If a line starts at address zero, omit it. (On the platforms
// breakpad targets, it is extremely unlikely that there will be code
// at address zero.)
//
// - If a line starts immediately after an omitted line, omit it too.
class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
public:
// As the DWARF line info parser passes us line records, add source
// files to MODULE, and add all lines to the end of LINES. LINES
// need not be empty. If the parser hands us a zero-length line, we
// omit it. If the parser hands us a line that extends beyond the
// end of the address space, we clip it. It's up to our client to
// sort out which lines belong to which functions; we don't add them
// to any particular function in MODULE ourselves.
DwarfLineToModule(Module *module, vector<Module::Line> *lines)
: module_(module),
lines_(lines),
highest_file_number_(-1),
omitted_line_end_(0),
warned_bad_file_number_(false),
warned_bad_directory_number_(false) { }
~DwarfLineToModule() { }
void DefineDir(const std::string &name, uint32 dir_num);
void DefineFile(const std::string &name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length);
void AddLine(uint64 address, uint64 length,
uint32 file_num, uint32 line_num, uint32 column_num);
private:
typedef std::map<uint32, std::string> DirectoryTable;
typedef std::map<uint32, Module::File *> FileTable;
// The module we're contributing debugging info to. Owned by our
// client.
Module *module_;
// The vector of lines we're accumulating. Owned by our client.
//
// In a Module, as in a breakpad symbol file, lines belong to
// specific functions, but DWARF simply assigns lines to addresses;
// one must infer the line/function relationship using the
// functions' beginning and ending addresses. So we can't add these
// to the appropriate function from module_ until we've read the
// function info as well. Instead, we accumulate lines here, and let
// whoever constructed this sort it all out.
vector<Module::Line> *lines_;
// A table mapping directory numbers to paths.
DirectoryTable directories_;
// A table mapping file numbers to Module::File pointers.
FileTable files_;
// The highest file number we've seen so far, or -1 if we've seen
// none. Used for dynamically defined file numbers.
int32 highest_file_number_;
// This is the ending address of the last line we omitted, or zero if we
// didn't omit the previous line. It is zero before we have received any
// AddLine calls.
uint64 omitted_line_end_;
// True if we've warned about:
bool warned_bad_file_number_; // bad file numbers
bool warned_bad_directory_number_; // bad directory numbers
};
} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H

Просмотреть файл

@ -0,0 +1,339 @@
// 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>
// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule.
#include "breakpad_googletest_includes.h"
#include "common/linux/dwarf_line_to_module.h"
using google_breakpad::DwarfLineToModule;
using google_breakpad::Module;
using google_breakpad::Module;
TEST(Simple, One) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
0x4c090cbf, 0x1cf9fe0d);
vector<Module::File *> files;
m.GetFiles(&files);
EXPECT_EQ(1U, files.size());
EXPECT_STREQ("file1", files[0]->name.c_str());
EXPECT_EQ(1U, lines.size());
EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address);
EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size);
EXPECT_TRUE(lines[0].file == files[0]);
EXPECT_EQ(0x4c090cbf, lines[0].number);
}
TEST(Simple, Many) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("directory1", 0x838299ab);
h.DefineDir("directory2", 0xf85de023);
h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0);
h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0);
h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0);
h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0);
h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a,
0x15b0f0a9U, 0x3ff5abd6U);
h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4,
0x4d259ce9U, 0x41c5ee32U);
h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56,
0x1ee9fa4fU, 0xbf70e46aU);
h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c,
0x77fc280eU, 0x2c4a728cU);
h.DefineFile("file3", -1, 0, 0, 0);
h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5,
0x75047044U, 0xb6a0016cU);
vector<Module::File *> files;
m.GetFiles(&files);
ASSERT_EQ(5U, files.size());
EXPECT_STREQ("directory1/file1", files[0]->name.c_str());
EXPECT_STREQ("directory1/file2", files[1]->name.c_str());
EXPECT_STREQ("directory2/file1", files[2]->name.c_str());
EXPECT_STREQ("directory2/file2", files[3]->name.c_str());
EXPECT_STREQ("file3", files[4]->name.c_str());
ASSERT_EQ(5U, lines.size());
EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address);
EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size);
EXPECT_TRUE(lines[0].file == files[0]);
EXPECT_EQ(0x15b0f0a9, lines[0].number);
EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address);
EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size);
EXPECT_TRUE(lines[1].file == files[2]);
EXPECT_EQ(0x4d259ce9, lines[1].number);
EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address);
EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size);
EXPECT_TRUE(lines[2].file == files[1]);
EXPECT_EQ(0x1ee9fa4f, lines[2].number);
EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address);
EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size);
EXPECT_TRUE(lines[3].file == files[3]);
EXPECT_EQ(0x77fc280e, lines[3].number);
EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address);
EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size);
EXPECT_TRUE(lines[4].file == files[4]);
EXPECT_EQ(0x75047044, lines[4].number);
}
TEST(Filenames, Absolute) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("directory1", 1);
h.DefineFile("/absolute", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
vector<Module::File *> files;
m.GetFiles(&files);
ASSERT_EQ(1U, files.size());
EXPECT_STREQ("/absolute", files[0]->name.c_str());
ASSERT_EQ(1U, lines.size());
EXPECT_TRUE(lines[0].file == files[0]);
}
TEST(Filenames, Relative) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("directory1", 1);
h.DefineFile("relative", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
vector<Module::File *> files;
m.GetFiles(&files);
ASSERT_EQ(1U, files.size());
EXPECT_STREQ("directory1/relative", files[0]->name.c_str());
ASSERT_EQ(1U, lines.size());
EXPECT_TRUE(lines[0].file == files[0]);
}
TEST(Filenames, StrangeFile) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("directory1", 1);
h.DefineFile("", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("directory1/", lines[0].file->name.c_str());
}
TEST(Filenames, StrangeDirectory) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("", 1);
h.DefineFile("file1", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("/file1", lines[0].file->name.c_str());
}
TEST(Filenames, StrangeDirectoryAndFile) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("", 1);
h.DefineFile("", 1, 1, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("/", lines[0].file->name.c_str());
}
// We should silently ignore attempts to define directory number zero,
// since that is always the compilation directory.
TEST(Errors, DirectoryZero) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("directory0", 0); // should be ignored
h.DefineFile("relative", 1, 0, 0, 0);
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("relative", lines[0].file->name.c_str());
}
// We should refuse to add lines with bogus file numbers. We should
// produce only one warning, however.
TEST(Errors, BadFileNumber) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("relative", 1, 0, 0, 0);
h.AddLine(1, 1, 2, 0, 0); // bad file number
h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning)
EXPECT_EQ(0U, lines.size());
}
// We should treat files with bogus directory numbers as relative to
// the compilation unit.
TEST(Errors, BadDirectoryNumber) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineDir("directory1", 1);
h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning)
h.AddLine(1, 1, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str());
}
// We promise not to report empty lines.
TEST(Errors, EmptyLine) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(1, 0, 1, 0, 0);
ASSERT_EQ(0U, lines.size());
}
// We are supposed to clip lines that extend beyond the end of the
// address space.
TEST(Errors, BigLine) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
ASSERT_EQ(1U, lines.size());
EXPECT_EQ(1U, lines[0].size);
}
// The 'Omitted' tests verify that we correctly omit line information
// for code in sections that the linker has dropped. See "GNU
// toolchain omitted sections support" at the top of the
// DwarfLineToModule class.
TEST(Omitted, DroppedThenGood) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
h.AddLine(20, 10, 1, 13059195, 0); // should be recorded
ASSERT_EQ(1U, lines.size());
EXPECT_EQ(13059195, lines[0].number);
}
TEST(Omitted, GoodThenDropped) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
h.AddLine(0, 10, 1, 44793413, 0); // should be omitted
ASSERT_EQ(1U, lines.size());
EXPECT_EQ(41454594, lines[0].number);
}
TEST(Omitted, Mix1) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
h.AddLine(0xdfb5a72d, 10, 1, 39847385, 0); // should be recorded
h.AddLine(0, 0x78, 1, 23053829, 0); // should be omitted
h.AddLine(0x78, 0x6a, 1, 65317783, 0); // should be omitted
h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0); // should be omitted
h.AddLine(0x9fe0cea5, 10, 1, 91806582, 0); // should be recorded
h.AddLine(0x7e41a109, 10, 1, 56169221, 0); // should be recorded
ASSERT_EQ(4U, lines.size());
EXPECT_EQ(58932642, lines[0].number);
EXPECT_EQ(39847385, lines[1].number);
EXPECT_EQ(91806582, lines[2].number);
EXPECT_EQ(56169221, lines[3].number);
}
TEST(Omitted, Mix2) {
Module m("name", "os", "architecture", "id");
vector<Module::Line> lines;
DwarfLineToModule h(&m, &lines);
h.DefineFile("filename1", 1, 0, 0, 0);
h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted
h.AddLine(0xf2, 0xb9, 1, 78958222, 0); // should be omitted
h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0); // should be omitted
h.AddLine(0x4e4d271e, 9, 1, 67355743, 0); // should be recorded
h.AddLine(0xdfb5a72d, 30, 1, 23365776, 0); // should be recorded
h.AddLine(0, 0x64, 1, 76196762, 0); // should be omitted
h.AddLine(0x64, 0x33, 1, 71066611, 0); // should be omitted
h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0); // should be omitted
ASSERT_EQ(2U, lines.size());
EXPECT_EQ(67355743, lines[0].number);
EXPECT_EQ(23365776, lines[1].number);
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without

Просмотреть файл

@ -0,0 +1,82 @@
// 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>
// language.cc: Subclasses and singletons for google_breakpad::Language.
// See language.h for details.
#include "common/linux/language.h"
namespace google_breakpad {
// C++ language-specific operations.
class CPPLanguage: public Language {
public:
string MakeQualifiedName(const string &parent_name,
const string &name) const {
if (parent_name.empty())
return name;
else
return parent_name + "::" + name;
}
};
const CPPLanguage CPPLanguageSingleton;
// Java language-specific operations.
class JavaLanguage: public Language {
public:
string MakeQualifiedName(const string &parent_name,
const string &name) const {
if (parent_name.empty())
return name;
else
return parent_name + "." + name;
}
};
JavaLanguage JavaLanguageSingleton;
// Assembler language-specific operations.
class AssemblerLanguage: public Language {
bool HasFunctions() const { return false; }
string MakeQualifiedName(const string &parent_name,
const string &name) const {
return name;
}
};
AssemblerLanguage AssemblerLanguageSingleton;
const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
const Language * const Language::Java = &JavaLanguageSingleton;
const Language * const Language::Assembler = &AssemblerLanguageSingleton;
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,84 @@
// -*- mode: c++ -*-
// 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>
// language.h: Define google_breakpad::Language. Instances of
// subclasses of this class provide language-appropriate operations
// for the Breakpad symbol dumper.
#ifndef COMMON_LINUX_LANGUAGE_H__
#define COMMON_LINUX_LANGUAGE_H__
#include <string>
namespace google_breakpad {
using std::string;
// An abstract base class for language-specific operations. We choose
// an instance of a subclass of this when we find the CU's language.
// This class's definitions are appropriate for CUs with no specified
// language.
class Language {
public:
// Return true if this language has functions to which we can assign
// line numbers. (Debugging info for assembly language, for example,
// can have source location information, but does not have functions
// recorded using DW_TAG_subprogram DIEs.)
virtual bool HasFunctions() const { return true; }
// Construct a fully-qualified, language-appropriate form of NAME,
// given that PARENT_NAME is the name of the construct enclosing
// NAME. If PARENT_NAME is the empty string, then NAME is a
// top-level name.
//
// This API sort of assumes that a fully-qualified name is always
// some simple textual composition of the unqualified name and its
// parent's name, and that we don't need to know anything else about
// the parent or the child (say, their DIEs' tags) to do the job.
// This is true for the languages we support at the moment, and
// keeps things concrete. Perhaps a more refined operation would
// take into account the parent and child DIE types, allow languages
// to use their own data type for complex parent names, etc. But if
// C++ doesn't need all that, who would?
virtual string MakeQualifiedName (const string &parent_name,
const string &name) const = 0;
// Instances for specific languages.
static const Language * const CPlusPlus,
* const Java,
* const Assembler;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_LANGUAGE_H__

Просмотреть файл

@ -77,7 +77,8 @@
* Porting to other related platforms should not be difficult.
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
defined(__mips__) || defined(__PPC__)) && defined(__linux)
defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__)) \
&& defined(__linux)
#ifndef SYS_CPLUSPLUS
#ifdef __cplusplus
@ -105,7 +106,6 @@ extern "C" {
/* Include definitions of the ABI currently in use. */
#include <sgidefs.h>
#endif
#endif
/* As glibc often provides subtly incompatible data structures (and implicit
@ -217,7 +217,8 @@ struct kernel_rusage {
};
struct siginfo;
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__)
#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \
|| defined(__PPC__)
/* include/asm-{arm,i386,mips,ppc}/signal.h */
struct kernel_old_sigaction {
@ -354,7 +355,7 @@ struct kernel_stat64 {
#endif
/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */
#if defined(__i386__) || defined(__ARM_ARCH_3__)
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
struct kernel_stat {
/* The kernel headers suggest that st_dev and st_rdev should be 32bit
* quantities encoding 12bit major and 20bit minor numbers in an interleaved
@ -520,7 +521,7 @@ struct kernel_statfs {
/* Definitions missing from the standard header files */
#ifndef O_DIRECTORY
#if defined(__ARM_ARCH_3__)
#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
#define O_DIRECTORY 0040000
#else
#define O_DIRECTORY 0200000
@ -641,7 +642,7 @@ struct kernel_statfs {
#define __NR_move_pages 317
#endif
/* End of i386 definitions */
#elif defined(__ARM_ARCH_3__)
#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
#ifndef __NR_setresuid
#define __NR_setresuid (__NR_SYSCALL_BASE + 164)
#define __NR_setresgid (__NR_SYSCALL_BASE + 170)
@ -715,7 +716,7 @@ struct kernel_statfs {
#ifndef __NR_move_pages
#define __NR_move_pages (__NR_SYSCALL_BASE + 344)
#endif
/* End of ARM 3 definitions */
/* End of ARM 3/EABI definitions */
#elif defined(__x86_64__)
#ifndef __NR_setresuid
#define __NR_setresuid 117
@ -1087,7 +1088,8 @@ struct kernel_statfs {
#endif
#undef LSS_RETURN
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__))
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \
|| defined(__ARM_EABI__))
/* Failing system calls return a negative result in the range of
* -1..-4095. These are "errno" values with the sign inverted.
*/
@ -1526,11 +1528,6 @@ struct kernel_statfs {
return res;
}
#elif defined(__ARM_ARCH_3__)
/* Most definitions of _syscallX() neglect to mark "memory" as being
* clobbered. This causes problems with compilers, that do a better job
* at optimizing across __asm__ calls.
* So, we just have to redefine all fo the _syscallX() macros.
*/
#undef LSS_REG
#define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
#undef LSS_BODY
@ -1646,6 +1643,135 @@ struct kernel_statfs {
}
LSS_RETURN(int, __res);
}
#elif defined(__ARM_EABI__)
/* Most definitions of _syscallX() neglect to mark "memory" as being
* clobbered. This causes problems with compilers, that do a better job
* at optimizing across __asm__ calls.
* So, we just have to redefine all fo the _syscallX() macros.
*/
#undef LSS_REG
#define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
#undef LSS_BODY
#define LSS_BODY(type,name,args...) \
register long __res_r0 __asm__("r0"); \
long __res; \
__asm__ __volatile__ ("push {r7}\n" \
"mov r7, %1\n" \
"swi 0x0\n" \
"pop {r7}\n" \
: "=r"(__res_r0) \
: "i"(__NR_##name) , ## args \
: "lr", "memory"); \
__res = __res_r0; \
LSS_RETURN(type, __res)
#undef _syscall0
#define _syscall0(type, name) \
type LSS_NAME(name)() { \
LSS_BODY(type, name); \
}
#undef _syscall1
#define _syscall1(type, name, type1, arg1) \
type LSS_NAME(name)(type1 arg1) { \
LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \
}
#undef _syscall2
#define _syscall2(type, name, type1, arg1, type2, arg2) \
type LSS_NAME(name)(type1 arg1, type2 arg2) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \
}
#undef _syscall3
#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \
}
#undef _syscall4
#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_REG(3, arg4); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \
}
#undef _syscall5
#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
type5,arg5) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
type5 arg5) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_REG(3, arg4); LSS_REG(4, arg5); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
"r"(__r4)); \
}
#undef _syscall6
#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
type5,arg5,type6,arg6) \
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
type5 arg5, type6 arg6) { \
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
"r"(__r4), "r"(__r5)); \
}
LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
int flags, void *arg, int *parent_tidptr,
void *newtls, int *child_tidptr) {
long __res;
{
register int __flags __asm__("r0") = flags;
register void *__stack __asm__("r1") = child_stack;
register void *__ptid __asm__("r2") = parent_tidptr;
register void *__tls __asm__("r3") = newtls;
register int *__ctid __asm__("r4") = child_tidptr;
__asm__ __volatile__(/* if (fn == NULL || child_stack == NULL)
* return -EINVAL;
*/
"cmp %2,#0\n"
"cmpne %3,#0\n"
"moveq %0,%1\n"
"beq 1f\n"
/* Push "arg" and "fn" onto the stack that will be
* used by the child.
*/
"str %5,[%3,#-4]!\n"
"str %2,[%3,#-4]!\n"
/* %r0 = syscall(%r0 = flags,
* %r1 = child_stack,
* %r2 = parent_tidptr,
* %r3 = newtls,
* %r4 = child_tidptr)
*/
"mov r7, %9\n"
"swi 0x0\n"
/* if (%r0 != 0)
* return %r0;
*/
"movs %0,r0\n"
"bne 1f\n"
/* In the child, now. Call "fn(arg)".
*/
"ldr r0,[sp, #4]\n"
"mov lr,pc\n"
"ldr pc,[sp]\n"
/* Call _exit(%r0).
*/
"mov r7, %10\n"
"swi 0x0\n"
"1:\n"
: "=r" (__res)
: "i"(-EINVAL),
"r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
"r"(__ptid), "r"(__tls), "r"(__ctid),
"i"(__NR_clone), "i"(__NR_exit)
: "lr", "memory");
}
LSS_RETURN(int, __res);
}
#elif defined(__mips__)
#undef LSS_REG
#define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \
@ -2082,8 +2208,10 @@ struct kernel_statfs {
LSS_INLINE _syscall0(pid_t, getppid)
LSS_INLINE _syscall2(int, getpriority, int, a,
int, b)
#if !defined(__ARM_EABI__)
LSS_INLINE _syscall2(int, getrlimit, int, r,
struct kernel_rlimit*, l)
#endif
LSS_INLINE _syscall1(pid_t, getsid, pid_t, p)
LSS_INLINE _syscall0(pid_t, _gettid)
LSS_INLINE _syscall5(int, setxattr, const char *,p,
@ -2237,6 +2365,7 @@ struct kernel_statfs {
}
#endif
#if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32)
LSS_INLINE _syscall4(pid_t, wait4, pid_t, p,
int*, s, int, o,
@ -2250,13 +2379,15 @@ struct kernel_statfs {
LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m)
LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f)
#endif
#if defined(__i386__) || defined(__ARM_ARCH_3__)
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
#define __NR__setfsgid32 __NR_setfsgid32
#define __NR__setfsuid32 __NR_setfsuid32
#define __NR__setresgid32 __NR_setresgid32
#define __NR__setresuid32 __NR_setresuid32
#if defined(__ARM_EABI__)
LSS_INLINE _syscall2(int, ugetrlimit, int, r,
struct kernel_rlimit*, l)
#endif
LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f)
LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f)
LSS_INLINE _syscall3(int, _setresgid32, gid_t, r,
@ -2365,6 +2496,7 @@ struct kernel_statfs {
}
}
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__)
#define __NR__sigaction __NR_sigaction
#define __NR__sigpending __NR_sigpending
@ -2375,7 +2507,9 @@ struct kernel_statfs {
struct kernel_stat64 *, b)
LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
loff_t *, res, uint, wh)
#if !defined(__ARM_EABI__)
LSS_INLINE _syscall1(void*, mmap, void*, a)
#endif
LSS_INLINE _syscall6(void*, mmap2, void*, s,
size_t, l, int, p,
int, f, int, d,
@ -2590,12 +2724,24 @@ struct kernel_statfs {
LSS_SC_BODY(4, int, 8, d, type, protocol, sv);
}
#endif
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
#if defined(__ARM_EABI__)
LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg,
int, flags);
LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*,
msg, int, flags);
LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t, len,
int, falgs, const struct kernel_sockaddr*, to,
unsigned int, tolen);
LSS_INLINE _syscall2(int, shutdown, int, s, int, how);
LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol);
LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol,
int*, sv);
#endif
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
#define __NR__socketcall __NR_socketcall
LSS_INLINE _syscall2(int, _socketcall, int, c,
va_list, a)
LSS_INLINE int LSS_NAME(socketcall)(int op, ...) {
int rc;
va_list ap;
@ -2604,29 +2750,24 @@ struct kernel_statfs {
va_end(ap);
return rc;
}
LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
int flags){
return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags);
}
LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
const struct kernel_msghdr *msg,
int flags) {
return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags);
}
LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
int flags,
const struct kernel_sockaddr *to,
unsigned int tolen) {
return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen);
}
LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
return LSS_NAME(socketcall)(13, s, how);
}
LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
return LSS_NAME(socketcall)(1, domain, type, protocol);
}
@ -2673,6 +2814,7 @@ struct kernel_statfs {
#endif
/* TODO(csilvers): see if ppc can/should support this as well */
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
defined(__ARM_EABI__) || \
(defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64)
#define __NR__statfs64 __NR_statfs64
#define __NR__fstatfs64 __NR_fstatfs64
@ -2743,8 +2885,13 @@ struct kernel_statfs {
switch (name) {
case _SC_OPEN_MAX: {
struct kernel_rlimit limit;
#if defined(__ARM_EABI__)
return LSS_NAME(ugetrlimit)(RLIMIT_NOFILE, &limit) < 0
? 8192 : limit.rlim_cur;
#else
return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0
? 8192 : limit.rlim_cur;
? 8192 : limit.rlim_cur;
#endif
}
case _SC_PAGESIZE:
return __getpagesize();

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -27,8 +27,13 @@
// (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>
// module.cc: Implement google_breakpad::Module. See module.h.
#include <cerrno>
#include <cstring>
#include "common/linux/module.h"
namespace google_breakpad {
@ -47,6 +52,9 @@ Module::~Module() {
for (vector<Function *>::iterator it = functions_.begin();
it != functions_.end(); it++)
delete *it;
for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
it != stack_frame_entries_.end(); it++)
delete *it;
}
void Module::SetLoadAddress(Address address) {
@ -62,6 +70,15 @@ void Module::AddFunctions(vector<Function *>::iterator begin,
functions_.insert(functions_.end(), begin, end);
}
void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
stack_frame_entries_.push_back(stack_frame_entry);
}
void Module::GetFunctions(vector<Function *> *vec,
vector<Function *>::iterator i) {
vec->insert(i, functions_.begin(), functions_.end());
}
Module::File *Module::FindFile(const string &name) {
// A tricky bit here. The key of each map entry needs to be a
// pointer to the entry's File's name string. This means that we
@ -77,10 +94,10 @@ Module::File *Module::FindFile(const string &name) {
if (destiny == files_.end()
|| *destiny->first != name) { // Repeated string comparison, boo hoo.
File *file = new File;
file->name_ = name;
file->source_id_ = -1;
file->name = name;
file->source_id = -1;
destiny = files_.insert(destiny,
FileByNameMap::value_type(&file->name_, file));
FileByNameMap::value_type(&file->name, file));
}
return destiny->second;
}
@ -90,20 +107,35 @@ Module::File *Module::FindFile(const char *name) {
return FindFile(name_string);
}
Module::File *Module::FindExistingFile(const string &name) {
FileByNameMap::iterator it = files_.find(&name);
return (it == files_.end()) ? NULL : it->second;
}
void Module::GetFiles(vector<File *> *vec) {
vec->clear();
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
vec->push_back(it->second);
}
void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
*vec = stack_frame_entries_;
}
void Module::AssignSourceIds() {
// First, give every source file an id of -1.
for (FileByNameMap::iterator file_it = files_.begin();
file_it != files_.end(); file_it++)
file_it->second->source_id_ = -1;
file_it->second->source_id = -1;
// Next, mark all files actually cited by our functions' line number
// info, by setting each one's source id to zero.
for (vector<Function *>::const_iterator func_it = functions_.begin();
func_it != functions_.end(); func_it++) {
Function *func = *func_it;
for (vector<Line>::iterator line_it = func->lines_.begin();
line_it != func->lines_.end(); line_it++)
line_it->file_->source_id_ = 0;
for (vector<Line>::iterator line_it = func->lines.begin();
line_it != func->lines.end(); line_it++)
line_it->file->source_id = 0;
}
// Finally, assign source ids to those files that have been marked.
@ -113,8 +145,8 @@ void Module::AssignSourceIds() {
int next_source_id = 0;
for (FileByNameMap::iterator file_it = files_.begin();
file_it != files_.end(); file_it++)
if (! file_it->second->source_id_)
file_it->second->source_id_ = next_source_id++;
if (! file_it->second->source_id)
file_it->second->source_id = next_source_id++;
}
bool Module::ReportError() {
@ -123,20 +155,33 @@ bool Module::ReportError() {
return false;
}
bool Module::WriteRuleMap(const RuleMap &rule_map, FILE *stream) {
for (RuleMap::const_iterator it = rule_map.begin();
it != rule_map.end(); it++) {
if (it != rule_map.begin() &&
0 > putc(' ', stream))
return false;
if (0 > fprintf(stream, "%s: %s", it->first.c_str(), it->second.c_str()))
return false;
}
return true;
}
bool Module::Write(FILE *stream) {
if (0 > fprintf(stream, "MODULE %s %s %s %s\n",
os_.c_str(), architecture_.c_str(), id_.c_str(),
name_.c_str()))
return ReportError();
// Write out files.
AssignSourceIds();
// Write out files.
for (FileByNameMap::iterator file_it = files_.begin();
file_it != files_.end(); file_it++) {
File *file = file_it->second;
if (file->source_id_ >= 0) {
if (file->source_id >= 0) {
if (0 > fprintf(stream, "FILE %d %s\n",
file->source_id_, file->name_.c_str()))
file->source_id, file->name.c_str()))
return ReportError();
}
}
@ -145,22 +190,45 @@ bool Module::Write(FILE *stream) {
for (vector<Function *>::const_iterator func_it = functions_.begin();
func_it != functions_.end(); func_it++) {
Function *func = *func_it;
if (0 > fprintf(stream, "FUNC %lx %lx %lu %s\n",
(unsigned long) (func->address_ - load_address_),
(unsigned long) func->size_,
(unsigned long) func->parameter_size_,
func->name_.c_str()))
if (0 > fprintf(stream, "FUNC %llx %llx %llx %s\n",
(unsigned long long) (func->address - load_address_),
(unsigned long long) func->size,
(unsigned long long) func->parameter_size,
func->name.c_str()))
return ReportError();
for (vector<Line>::iterator line_it = func->lines_.begin();
line_it != func->lines_.end(); line_it++)
if (0 > fprintf(stream, "%lx %lx %d %d\n",
(unsigned long) (line_it->address_ - load_address_),
(unsigned long) line_it->size_,
line_it->number_,
line_it->file_->source_id_))
for (vector<Line>::iterator line_it = func->lines.begin();
line_it != func->lines.end(); line_it++)
if (0 > fprintf(stream, "%llx %llx %d %d\n",
(unsigned long long) (line_it->address - load_address_),
(unsigned long long) line_it->size,
line_it->number,
line_it->file->source_id))
return ReportError();
}
// Write out 'STACK CFI INIT' and 'STACK CFI' records.
vector<StackFrameEntry *>::const_iterator frame_it;
for (frame_it = stack_frame_entries_.begin();
frame_it != stack_frame_entries_.end(); frame_it++) {
StackFrameEntry *entry = *frame_it;
if (0 > fprintf(stream, "STACK CFI INIT %llx %llx ",
(unsigned long long) entry->address - load_address_,
(unsigned long long) entry->size)
|| !WriteRuleMap(entry->initial_rules, stream)
|| 0 > putc('\n', stream))
return ReportError();
// Write out this entry's delta rules as 'STACK CFI' records.
for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin();
delta_it != entry->rule_changes.end(); delta_it++) {
if (0 > fprintf(stream, "STACK CFI %llx ",
(unsigned long long) delta_it->first - load_address_)
|| !WriteRuleMap(delta_it->second, stream)
|| 0 > putc('\n', stream))
return ReportError();
}
}
return true;
}

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright (c) 2009, Google Inc. -*- mode: c++ -*-
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -27,7 +29,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// module.h: defines google_breakpad::Module, for writing breakpad symbol files
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// module.h: Define google_breakpad::Module. A Module holds debugging
// information, and can write that information out as a Breakpad
// symbol file.
#ifndef COMMON_LINUX_MODULE_H__
#define COMMON_LINUX_MODULE_H__
@ -65,12 +71,12 @@ class Module {
// A source file.
struct File {
// The name of the source file.
string name_;
string name;
// The file's source id. The Write member function clears this
// field and assigns source ids a fresh, so any value placed here
// before calling Write will be lost.
int source_id_;
int source_id;
};
// A function.
@ -78,21 +84,21 @@ class Module {
// For sorting by address. (Not style-guide compliant, but it's
// stupid not to put this in the struct.)
static bool CompareByAddress(const Function *x, const Function *y) {
return x->address_ < y->address_;
return x->address < y->address;
}
// The function's name.
string name_;
string name;
// The start address and length of the function's code.
Address address_, size_;
Address address, size;
// The function's parameter size.
Address parameter_size_;
Address parameter_size;
// Source lines belonging to this function, sorted by increasing
// address.
vector<Line> lines_;
vector<Line> lines;
};
// A source line.
@ -100,12 +106,41 @@ class Module {
// For sorting by address. (Not style-guide compliant, but it's
// stupid not to put this in the struct.)
static bool CompareByAddress(const Module::Line &x, const Module::Line &y) {
return x.address_ < y.address_;
return x.address < y.address;
}
Address address_, size_; // The address and size of the line's code.
File *file_; // The source file.
int number_; // The source line number.
Address address, size; // The address and size of the line's code.
File *file; // The source file.
int number; // The source line number.
};
// A map from register names to postfix expressions that recover
// their their values. This can represent a complete set of rules to
// follow at some address, or a set of changes to be applied to an
// extant set of rules.
typedef map<string, string> RuleMap;
// A map from addresses to RuleMaps, representing changes that take
// effect at given addresses.
typedef map<Address, RuleMap> RuleChangeMap;
// A range of 'STACK CFI' stack walking information. An instance of
// this structure corresponds to a 'STACK CFI INIT' record and the
// subsequent 'STACK CFI' records that fall within its range.
struct StackFrameEntry {
// The starting address and number of bytes of machine code this
// entry covers.
Address address, size;
// The initial register recovery rules, in force at the starting
// address.
RuleMap initial_rules;
// A map from addresses to rule changes. To find the rules in
// force at a given address, start with initial_rules, and then
// apply the changes given in this map for all addresses up to and
// including the address you're interested in.
RuleChangeMap rule_changes;
};
// Create a new module with the given name, operating system,
@ -123,26 +158,64 @@ class Module {
void SetLoadAddress(Address load_address);
// Add FUNCTION to the module.
// Destroying this module frees all Function objects that have been
// added with this function.
// This module owns all Function objects added with this function:
// destroying the module destroys them as well.
void AddFunction(Function *function);
// Add all the functions in [BEGIN,END) to the module.
// Destroying this module frees all Function objects that have been
// added with this function.
// This module owns all Function objects added with this function:
// destroying the module destroys them as well.
void AddFunctions(vector<Function *>::iterator begin,
vector<Function *>::iterator end);
// If this module has a file named NAME, return a pointer to it. If
// Add STACK_FRAME_ENTRY to the module.
//
// This module owns all StackFrameEntry objects added with this
// function: destroying the module destroys them as well.
void AddStackFrameEntry(StackFrameEntry *stack_frame_entry);
// If this module has a file named NAME, return a pointer to it. If
// it has none, then create one and return a pointer to the new
// file. Destroying this module frees all File objects that have
// been created using this function, or with Insert.
// file. This module owns all File objects created using these
// functions; destroying the module destroys them as well.
File *FindFile(const string &name);
File *FindFile(const char *name);
// Write this module to STREAM in the breakpad symbol format.
// Return true if all goes well, or false if an error occurs. This
// method writes out:
// If this module has a file named NAME, return a pointer to it.
// Otherwise, return NULL.
File *FindExistingFile(const string &name);
// Insert pointers to the functions added to this module at I in
// VEC. The pointed-to Functions are still owned by this module.
// (Since this is effectively a copy of the function list, this is
// mostly useful for testing; other uses should probably get a more
// appropriate interface.)
void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i);
// Clear VEC and fill it with pointers to the Files added to this
// module, sorted by name. The pointed-to Files are still owned by
// this module. (Since this is effectively a copy of the file list,
// this is mostly useful for testing; other uses should probably get
// a more appropriate interface.)
void GetFiles(vector<File *> *vec);
// Clear VEC and fill it with pointers to the StackFrameEntry
// objects that have been added to this module. (Since this is
// effectively a copy of the stack frame entry list, this is mostly
// useful for testing; other uses should probably get
// a more appropriate interface.)
void GetStackFrameEntries(vector<StackFrameEntry *> *vec);
// Find those files in this module that are actually referred to by
// functions' line number data, and assign them source id numbers.
// Set the source id numbers for all other files --- unused by the
// source line data --- to -1. We do this before writing out the
// symbol file, at which point we omit any unused files.
void AssignSourceIds();
// Call AssignSourceIds, and write this module to STREAM in the
// breakpad symbol format. Return true if all goes well, or false if
// an error occurs. This method writes out:
// - a header based on the values given to the constructor,
// - the source files added via FindFile, and finally
// - the functions added via AddFunctions, each with its lines.
@ -152,17 +225,15 @@ class Module {
private:
// Find those files in this module that are actually referred to by
// functions' line number data, and assign them source id numbers.
// Set the source id numbers for all other files --- unused by the
// source line data --- to -1. We do this before writing out the
// symbol file, at which point we omit any unused files.
void AssignSourceIds();
// Report an error that has occurred writing the symbol file, using
// errno to find the appropriate cause. Return false.
static bool ReportError();
// Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI'
// records, without a final newline. Return true if all goes well;
// if an error occurs, return false, and leave errno set.
static bool WriteRuleMap(const RuleMap &rule_map, FILE *stream);
// Module header entries.
string name_, os_, architecture_, id_;
@ -186,6 +257,10 @@ private:
// point to.
FileByNameMap files_; // This module's source files.
vector<Function *> functions_; // This module's functions.
// The module owns all the call frame info entries that have been
// added to it.
vector<StackFrameEntry *> stack_frame_entries_;
};
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,396 @@
// 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>
// module_unittest.cc: Unit tests for google_breakpad::Module.
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/linux/module.h"
using google_breakpad::Module;
using std::string;
using std::vector;
using testing::ContainerEq;
// Return a FILE * referring to a temporary file that will be deleted
// automatically when the stream is closed or the program exits.
FILE *checked_tmpfile() {
FILE *f = tmpfile();
if (!f) {
fprintf(stderr, "error creating temporary file: %s\n", strerror(errno));
exit(1);
}
return f;
}
// Read from STREAM until end of file, and return the contents as a
// string.
string checked_read(FILE *stream) {
string contents;
int c;
while ((c = getc(stream)) != EOF)
contents.push_back(c);
if (ferror(stream)) {
fprintf(stderr, "error reading temporary file contents: %s\n",
strerror(errno));
exit(1);
}
return contents;
}
// Apply 'fflush' to STREAM, and check for errors.
void checked_fflush(FILE *stream) {
if (fflush(stream) == EOF) {
fprintf(stderr, "error flushing temporary file stream: %s\n",
strerror(errno));
exit(1);
}
}
// Apply 'fclose' to STREAM, and check for errors.
void checked_fclose(FILE *stream) {
if (fclose(stream) == EOF) {
fprintf(stderr, "error closing temporary file stream: %s\n",
strerror(errno));
exit(1);
}
}
#define MODULE_NAME "name with spaces"
#define MODULE_OS "os-name"
#define MODULE_ARCH "architecture"
#define MODULE_ID "id-string"
TEST(Write, Header) {
FILE *f = checked_tmpfile();
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n",
contents.c_str());
}
TEST(Write, OneLineFunc) {
FILE *f = checked_tmpfile();
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
Module::File *file = m.FindFile("file_name.cc");
Module::Function *function = new(Module::Function);
function->name = "function_name";
function->address = 0xe165bf8023b9d9abLL;
function->size = 0x1e4bb0eb1cbf5b09LL;
function->parameter_size = 0x772beee89114358aLL;
Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL,
file, 67519080 };
function->lines.push_back(line);
m.AddFunction(function);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 file_name.cc\n"
"FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a"
" function_name\n"
"e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n",
contents.c_str());
}
TEST(Write, RelativeLoadAddress) {
FILE *f = checked_tmpfile();
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
m.SetLoadAddress(0x2ab698b0b6407073LL);
// Some source files. We will expect to see them in lexicographic order.
Module::File *file1 = m.FindFile("filename-b.cc");
Module::File *file2 = m.FindFile("filename-a.cc");
// A function.
Module::Function *function = new(Module::Function);
function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)";
function->address = 0xbec774ea5dd935f3LL;
function->size = 0x2922088f98d3f6fcLL;
function->parameter_size = 0xe5e9aa008bd5f0d0LL;
// Some source lines. The module should not sort these.
Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
file1, 41676901 };
Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL,
file2, 67519080 };
function->lines.push_back(line2);
function->lines.push_back(line1);
m.AddFunction(function);
// Some stack information.
Module::StackFrameEntry *entry = new Module::StackFrameEntry();
entry->address = 0x30f9e5c83323973dULL;
entry->size = 0x49fc9ca7c7c13dc2ULL;
entry->initial_rules[".cfa"] = "he was a handsome man";
entry->initial_rules["and"] = "what i want to know is";
entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
"do you like your blueeyed boy";
entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
m.AddStackFrameEntry(entry);
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename-a.cc\n"
"FILE 1 filename-b.cc\n"
"FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
" A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
"b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n"
"9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n"
"STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2"
" .cfa: he was a handsome man"
" and: what i want to know is\n"
"STACK CFI 6434d177ce326cb"
" Mister: Death"
" how: do you like your blueeyed boy\n",
contents.c_str());
}
TEST(Write, OmitUnusedFiles) {
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Create some source files.
Module::File *file1 = m.FindFile("filename1");
m.FindFile("filename2"); // not used by any line
Module::File *file3 = m.FindFile("filename3");
// Create a function.
Module::Function *function = new(Module::Function);
function->name = "function_name";
function->address = 0x9b926d464f0b9384LL;
function->size = 0x4f524a4ba795e6a6LL;
function->parameter_size = 0xbbe8133a6641c9b7LL;
// Source files that refer to some files, but not others.
Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL,
file1, 137850127 };
Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL,
file3, 28113549 };
function->lines.push_back(line1);
function->lines.push_back(line2);
m.AddFunction(function);
m.AssignSourceIds();
vector<Module::File *> vec;
m.GetFiles(&vec);
EXPECT_EQ((size_t) 3, vec.size());
EXPECT_STREQ("filename1", vec[0]->name.c_str());
EXPECT_NE(-1, vec[0]->source_id);
// Expect filename2 not to be used.
EXPECT_STREQ("filename2", vec[1]->name.c_str());
EXPECT_EQ(-1, vec[1]->source_id);
EXPECT_STREQ("filename3", vec[2]->name.c_str());
EXPECT_NE(-1, vec[2]->source_id);
FILE *f = checked_tmpfile();
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename1\n"
"FILE 1 filename3\n"
"FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7"
" function_name\n"
"595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n"
"401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n",
contents.c_str());
}
TEST(Construct, AddFunctions) {
FILE *f = checked_tmpfile();
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Two functions.
Module::Function *function1 = new(Module::Function);
function1->name = "_without_form";
function1->address = 0xd35024aa7ca7da5cLL;
function1->size = 0x200b26e605f99071LL;
function1->parameter_size = 0xf14ac4fed48c4a99LL;
Module::Function *function2 = new(Module::Function);
function2->name = "_and_void";
function2->address = 0x2987743d0b35b13fLL;
function2->size = 0xb369db048deb3010LL;
function2->parameter_size = 0x938e556cb5a79988LL;
// Put them in a vector.
vector<Module::Function *> vec;
vec.push_back(function1);
vec.push_back(function2);
m.AddFunctions(vec.begin(), vec.end());
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99"
" _without_form\n"
"FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
" _and_void\n",
contents.c_str());
// Check that m.GetFunctions returns the functions we expect.
vec.clear();
m.GetFunctions(&vec, vec.end());
EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1));
EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2));
EXPECT_EQ((size_t) 2, vec.size());
}
TEST(Construct, AddFrames) {
FILE *f = checked_tmpfile();
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// First STACK CFI entry, with no initial rules or deltas.
Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
entry1->address = 0xddb5f41285aa7757ULL;
entry1->size = 0x1486493370dc5073ULL;
m.AddStackFrameEntry(entry1);
// Second STACK CFI entry, with initial rules but no deltas.
Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
entry2->address = 0x8064f3af5e067e38ULL;
entry2->size = 0x0de2a5ee55509407ULL;
entry2->initial_rules[".cfa"] = "I think that I shall never see";
entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
m.AddStackFrameEntry(entry2);
// Third STACK CFI entry, with initial rules and deltas.
Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
entry3->address = 0x5e8d0db0a7075c6cULL;
entry3->size = 0x1c7edb12a7aea229ULL;
entry3->initial_rules[".cfa"] = "Whose woods are these";
entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
"the village though";
entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
"he will not see me stopping here";
entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
"his house is in";
entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
"I think I know";
m.AddStackFrameEntry(entry3);
// Check that Write writes STACK CFI records properly.
m.Write(f);
checked_fflush(f);
rewind(f);
string contents = checked_read(f);
checked_fclose(f);
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
"STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
" .cfa: I think that I shall never see"
" cannoli: a tree whose hungry mouth is prest"
" stromboli: a poem lovely as a tree\n"
"STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
" .cfa: Whose woods are these\n"
"STACK CFI 36682fad3763ffff"
" .cfa: I think I know"
" stromboli: his house is in\n"
"STACK CFI 47ceb0f63c269d7f"
" calzone: the village though"
" cannoli: he will not see me stopping here\n",
contents.c_str());
// Check that GetStackFrameEntries works.
vector<Module::StackFrameEntry *> entries;
m.GetStackFrameEntries(&entries);
ASSERT_EQ(3U, entries.size());
// Check first entry.
EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
ASSERT_EQ(0U, entries[0]->initial_rules.size());
ASSERT_EQ(0U, entries[0]->rule_changes.size());
// Check second entry.
EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
ASSERT_EQ(3U, entries[1]->initial_rules.size());
Module::RuleMap entry2_initial;
entry2_initial[".cfa"] = "I think that I shall never see";
entry2_initial["stromboli"] = "a poem lovely as a tree";
entry2_initial["cannoli"] = "a tree whose hungry mouth is prest";
EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
ASSERT_EQ(0U, entries[1]->rule_changes.size());
// Check third entry.
EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
Module::RuleMap entry3_initial;
entry3_initial[".cfa"] = "Whose woods are these";
EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
Module::RuleChangeMap entry3_changes;
entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
"he will not see me stopping here";
EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
}
TEST(Construct, UniqueFiles) {
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
Module::File *file1 = m.FindFile("foo");
Module::File *file2 = m.FindFile(string("bar"));
Module::File *file3 = m.FindFile(string("foo"));
Module::File *file4 = m.FindFile("bar");
EXPECT_NE(file1, file2);
EXPECT_EQ(file1, file3);
EXPECT_EQ(file2, file4);
EXPECT_EQ(file1, m.FindExistingFile("foo"));
EXPECT_TRUE(m.FindExistingFile("baz") == NULL);
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright 2009 Google Inc. All Rights Reserved.
// 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
@ -26,7 +26,10 @@
// (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>
// This file implements the google_breakpad::StabsReader class.
// See stabs_reader.h.
#include <a.out.h>
#include <stab.h>
@ -43,6 +46,8 @@ StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
stabstr_(stabstr),
stabstr_size_(stabstr_size),
handler_(handler),
string_offset_(0),
next_cu_string_offset_(0),
symbol_(NULL),
current_source_file_(NULL) {
symbols_ = reinterpret_cast<const struct nlist *>(stab);
@ -50,7 +55,7 @@ StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
}
const char *StabsReader::SymbolString() {
ptrdiff_t offset = symbol_->n_un.n_strx;
ptrdiff_t offset = string_offset_ + symbol_->n_un.n_strx;
if (offset < 0 || (size_t) offset >= stabstr_size_) {
handler_->Warning("symbol %d: name offset outside the string section",
symbol_ - symbols_);
@ -67,6 +72,21 @@ bool StabsReader::Process() {
if (symbol_->n_type == N_SO) {
if (! ProcessCompilationUnit())
return false;
} else if (symbol_->n_type == N_UNDF) {
// At the head of each compilation unit's entries there is an
// N_UNDF stab giving the number of symbols in the compilation
// unit, and the number of bytes that compilation unit's strings
// take up in the .stabstr section. Each CU's strings are
// separate; the n_strx values are offsets within the current
// CU's portion of the .stabstr section.
//
// As an optimization, the GNU linker combines all the
// compilation units into one, with a single N_UNDF at the
// beginning. However, other linkers, like Gold, do not perform
// this optimization.
string_offset_ = next_cu_string_offset_;
next_cu_string_offset_ = SymbolValue();
symbol_++;
} else
symbol_++;
}

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright 2009 Google Inc. All Rights Reserved. -*- mode: c++ -*-
// -*- mode: c++ -*-
// 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
@ -26,15 +28,20 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file contains definitions related to the STABS reader and
// its handler interfaces.
// A description of the STABS debugging format can be found at
// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// stabs_reader.h: Define StabsReader, a parser for STABS debugging
// information. A description of the STABS debugging format can be
// found at:
//
// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html
//
// The comments here assume you understand the format.
//
// This reader assumes that the system's <a.out.h> and <stab.h>
// This parser assumes that the system's <a.out.h> and <stab.h>
// headers accurately describe the layout of the STABS data; this code
// is not cross-platform safe.
// will not parse STABS data for a system with a different address
// size or endianness.
#ifndef COMMON_LINUX_STABS_READER_H__
#define COMMON_LINUX_STABS_READER_H__
@ -94,6 +101,13 @@ class StabsReader {
StabsHandler *handler_;
// The offset of the current compilation unit's strings within stabstr_.
size_t string_offset_;
// The value string_offset_ should have for the next compilation unit,
// as established by N_UNDF entries.
size_t next_cu_string_offset_;
// The current symbol we're processing.
const struct nlist *symbol_;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -27,7 +27,9 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// stabs_reader_unittest.cc: Unit tests for StabsReader.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader.
#include <a.out.h>
#include <cassert>
@ -53,6 +55,7 @@ using std::ostringstream;
using std::string;
using ::testing::_;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::Sequence;
@ -70,7 +73,8 @@ namespace {
// sections, and then pass those to StabsReader to look at. The
// human-readable file is called a "mock stabs file".
//
// Each line of a mock stabs file should have the following form:
// Except for compilation unit boundary lines (described below), each
// line of a mock stabs file should have the following form:
//
// TYPE OTHER DESC VALUE NAME
//
@ -91,6 +95,10 @@ namespace {
// is misleading, but that's how it is. For SO, this may be a
// filename; for FUN, this is the function name, plus type data; and
// so on.
//
// A compilation unit boundary line has the form:
//
// cu-boundary FILENAME
// I don't know if the whole parser/handler pattern is really worth
// the bureaucracy in this case. But just writing it out as
@ -108,6 +116,11 @@ class MockStabsHandler {
// stops, and its Process member function returns false.
virtual bool Entry(enum __stab_debug_code type, char other, short desc,
unsigned long value, const string &name) { return true; }
// Report a compilation unit boundary whose filename is FILENAME. As
// for the Entry function, this should return true to continue
// parsing, or false to stop processing.
virtual bool CUBoundary(const string &filename) { return true; }
// Report an error in parsing the mock stabs data. If this returns true,
// the parser continues; if it returns false, the parser stops and
// its Process member function returns false.
@ -159,7 +172,7 @@ bool MockStabsParser::Process() {
// terminating newline.
for(;;) {
string line;
std::getline(*stream_, line, '\n');
getline(*stream_, line, '\n');
if (line.empty() && stream_->eof())
break;
line_number_++;
@ -190,24 +203,32 @@ bool MockStabsParser::ParseLine(const string &line) {
// Parse and validate the stabs type.
string typeName;
linestream >> typeName;
StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName);
if (typeIt == type_names_.end())
return handler_->Error("%s:%d: unrecognized stab type: %s\n",
filename_.c_str(), line_number_, typeName.c_str());
// These are int, not char and unsigned char, to ensure they're parsed
// as decimal numbers, not characters.
int otherInt, descInt;
unsigned long value;
linestream >> otherInt >> descInt >> value;
if (linestream.fail())
return handler_->Error("%s:%d: malformed mock stabs input line\n",
filename_.c_str(), line_number_);
if (linestream.peek() == ' ')
linestream.get();
string name;
getline(linestream, name, '\n');
return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second),
otherInt, descInt, value, name);
if (typeName == "cu-boundary") {
if (linestream.peek() == ' ')
linestream.get();
string filename;
getline(linestream, filename, '\n');
return handler_->CUBoundary(filename);
} else {
StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName);
if (typeIt == type_names_.end())
return handler_->Error("%s:%d: unrecognized stab type: %s\n",
filename_.c_str(), line_number_, typeName.c_str());
// These are int, not char and unsigned char, to ensure they're parsed
// as decimal numbers, not characters.
int otherInt, descInt;
unsigned long value;
linestream >> otherInt >> descInt >> value;
if (linestream.fail())
return handler_->Error("%s:%d: malformed mock stabs input line\n",
filename_.c_str(), line_number_);
if (linestream.peek() == ' ')
linestream.get();
string name;
getline(linestream, name, '\n');
return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second),
otherInt, descInt, value, name);
}
}
// A class for constructing .stab sections.
@ -227,9 +248,15 @@ class StabSection {
// call to Append. The caller should initialize the returned entry
// as needed.
struct nlist *Append();
// Set the first entry's n_desc field to COUNT, and set its n_value field
// to STRING_SIZE.
void SetHeader(short count, unsigned long string_size);
// Set SECTION to the contents of a .stab section holding the
// accumulated list of entries added with Append.
void GetSection(string *section);
// Clear the array, and prepare this StabSection to accumulate a fresh
// set of entries.
void Clear();
private:
// The array of stabs entries,
@ -248,11 +275,23 @@ struct nlist *StabSection::Append() {
return &entries_[used_++];
}
void StabSection::SetHeader(short count, unsigned long string_size) {
assert(used_ >= 1);
entries_[0].n_desc = count;
entries_[0].n_value = string_size;
}
void StabSection::GetSection(string *section) {
section->assign(reinterpret_cast<char *>(entries_),
sizeof(*entries_) * used_);
}
void StabSection::Clear() {
used_ = 0;
size_ = 1;
entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_);
}
// A class for building .stabstr sections.
//
// A .stabstr section is an array of characters containing a bunch of
@ -273,6 +312,9 @@ class StabstrSection {
// Set SECTION to the contents of a .stabstr section in which the
// strings passed to Insert appear at the indices we promised.
void GetSection(string *section);
// Clear the contents of this StabstrSection, and prepare it to
// accumulate a new set of strings.
void Clear();
private:
// Maps from strings to .stabstr indices and back.
typedef map<string, size_t> StringToIndex;
@ -317,6 +359,12 @@ void StabstrSection::GetSection(string *section) {
}
}
void StabstrSection::Clear() {
string_indices_.clear();
string_indices_[""] = 0;
next_byte_ = 1;
}
// A mock stabs parser handler class that builds .stab and .stabstr
// sections.
class StabsSectionsBuilder: public MockStabsHandler {
@ -325,13 +373,15 @@ class StabsSectionsBuilder: public MockStabsHandler {
// and construct .stab and .stabstr sections. FILENAME should be
// the name of the mock stabs input file; we use it in error
// messages.
StabsSectionsBuilder(const string &filename):
filename_(filename), error_count_(0) { }
StabsSectionsBuilder(const string &filename)
: filename_(filename), error_count_(0), has_header_(false),
entry_count_(0) { }
// Overridden virtual member functions.
bool Entry(enum __stab_debug_code type, char other, short desc,
unsigned long value, const string &name);
virtual bool Error(const char *format, ...);
bool CUBoundary(const string &filename);
bool Error(const char *format, ...);
// Set SECTION to the contents of a .stab or .stabstr section
// reflecting the entries that have been passed to us via Entry.
@ -339,10 +389,24 @@ class StabsSectionsBuilder: public MockStabsHandler {
void GetStabstr(string *section);
private:
StabSection stab_; // stabs entries we've seen
StabstrSection stabstr_; // and the strings they love
const string &filename_; // input filename, for error messages
int error_count_; // number of errors we've seen so far
// Finish a compilation unit. If there are any entries accumulated in
// stab_ and stabstr_, add them as a new compilation unit to
// finished_cu_stabs_ and finished_cu_stabstr_, and then clear stab_ and
// stabstr_.
void FinishCU();
const string &filename_; // input filename, for error messages
int error_count_; // number of errors we've seen so far
// The following members accumulate the contents of a single compilation
// unit, possibly headed by an N_UNDF stab.
bool has_header_; // true if we have an N_UNDF header
int entry_count_; // the number of entries we've seen
StabSection stab_; // 'struct nlist' entries
StabstrSection stabstr_; // and the strings they love
// Accumulated .stab and .stabstr content for all compilation units.
string finished_cu_stab_, finished_cu_stabstr_;
};
bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other,
@ -354,9 +418,48 @@ bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other,
entry->n_desc = desc;
entry->n_value = value;
entry->n_un.n_strx = stabstr_.Insert(name);
entry_count_++;
return true;
}
bool StabsSectionsBuilder::CUBoundary(const string &filename) {
FinishCU();
// Add a header for the compilation unit.
assert(!has_header_);
assert(entry_count_ == 0);
struct nlist *entry = stab_.Append();
entry->n_type = N_UNDF;
entry->n_other = 0;
entry->n_desc = 0; // will be set to number of entries
entry->n_value = 0; // will be set to size of .stabstr data
entry->n_un.n_strx = stabstr_.Insert(filename);
has_header_ = true;
// The N_UNDF header isn't included in the symbol count, so we
// shouldn't bump entry_count_ here.
return true;
}
void StabsSectionsBuilder::FinishCU() {
if (entry_count_ > 0) {
// Get the strings first, so we can record their size in the header.
string stabstr;
stabstr_.GetSection(&stabstr);
finished_cu_stabstr_ += stabstr;
// Initialize our header, if we have one, and extract the .stab data.
if (has_header_)
stab_.SetHeader(entry_count_, stabstr.size());
string stab;
stab_.GetSection(&stab);
finished_cu_stab_ += stab;
}
stab_.Clear();
stabstr_.Clear();
has_header_ = false;
entry_count_ = 0;
}
bool StabsSectionsBuilder::Error(const char *format, ...) {
va_list args;
va_start(args, format);
@ -373,11 +476,13 @@ bool StabsSectionsBuilder::Error(const char *format, ...) {
}
void StabsSectionsBuilder::GetStab(string *section) {
stab_.GetSection(section);
FinishCU();
*section = finished_cu_stab_;
}
void StabsSectionsBuilder::GetStabstr(string *section) {
stabstr_.GetSection(section);
FinishCU();
*section = finished_cu_stabstr_;
}
class MockStabsReaderHandler: public StabsHandler {
@ -428,7 +533,7 @@ static bool ApplyHandlerToMockStabsData(StabsHandler *handler,
return reader.Process();
}
TEST(StabsReaderTestCase, MockStabsInput) {
TEST(StabsReader, MockStabsInput) {
MockStabsReaderHandler mock_handler;
{
@ -467,7 +572,7 @@ TEST(StabsReaderTestCase, MockStabsInput) {
"common/linux/testdata/stabs_reader_unittest.input1"));
}
TEST(StabsReaderTestCase, AbruptCU) {
TEST(StabsReader, AbruptCU) {
MockStabsReaderHandler mock_handler;
{
@ -485,7 +590,7 @@ TEST(StabsReaderTestCase, AbruptCU) {
"common/linux/testdata/stabs_reader_unittest.input2"));
}
TEST(StabsReaderTestCase, AbruptFunction) {
TEST(StabsReader, AbruptFunction) {
MockStabsReaderHandler mock_handler;
{
@ -507,7 +612,7 @@ TEST(StabsReaderTestCase, AbruptFunction) {
"common/linux/testdata/stabs_reader_unittest.input3"));
}
TEST(StabsReaderTestCase, NoCU) {
TEST(StabsReader, NoCU) {
MockStabsReaderHandler mock_handler;
EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _))
@ -521,7 +626,7 @@ TEST(StabsReaderTestCase, NoCU) {
}
TEST(StabsReaderTestCase, NoCUEnd) {
TEST(StabsReader, NoCUEnd) {
MockStabsReaderHandler mock_handler;
{
@ -545,4 +650,36 @@ TEST(StabsReaderTestCase, NoCUEnd) {
}
TEST(StabsReader, MultipleCUs) {
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler,
StartCompilationUnit(StrEq("antimony"), 0x12, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x22))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndFunction(0x32))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(0x32))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler,
StartCompilationUnit(StrEq("aluminum"), 0x42, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0x52))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndFunction(0x62))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(0x62))
.WillOnce(Return(true));
}
ASSERT_TRUE(ApplyHandlerToMockStabsData(
&mock_handler,
"common/linux/testdata/stabs_reader_unittest.input6"));
}
// name duplication
} // anonymous namespace

Просмотреть файл

@ -0,0 +1,676 @@
// -*- mode: c++ -*-
// Test data for pairing functions and lines.
//
// For a pair of functions that are adjacent (10,20),(20,25) and a
// pair that are not (10,15),(20,25), we include a test case for every
// possible arrangement of two lines relative to those functions. We
// include cases only for non-empty ranges, since empty functions and
// lines are dropped before we do any pairing.
//
// Each test case is represented by a macro call of the form:
//
// PAIRING(func1_start, func1_end, func2_start, func2_end,
// line1_start, line1_end, line2_start, line2_end,
// func1_num_lines, func2_num_lines,
// func1_line1_start, func1_line1_end,
// func1_line2_start, func1_line2_end,
// func2_line1_start, func2_line1_end,
// func2_line2_start, func2_line2_end,
// uncovered_funcs, uncovered_lines)
//
// where:
// - funcN_{start,end} is the range of the N'th function
// - lineN_{start,end} is the range of the N'th function
// - funcN_num_lines is the number of source lines that should be
// paired with the N'th function
// - funcN_lineM_{start,end} is the range of the M'th line
// paired with the N'th function, where 0,0 indicates that
// there should be no such line paired
// - uncovered_funcs is the number of functions with area that is
// uncovered by any line, and
// - uncovered_lines is the reverse.
// func1 func2 line1 line2 num pairing1 pairing2 uncovered
PAIRING(10, 20, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #0
PAIRING(10, 20, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #1
PAIRING(10, 20, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #2
PAIRING(10, 20, 20, 25, 6, 7, 7, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #3
PAIRING(10, 20, 20, 25, 6, 7, 7, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #4
PAIRING(10, 20, 20, 25, 6, 7, 7, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #5
PAIRING(10, 20, 20, 25, 6, 7, 7, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #6
PAIRING(10, 20, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #7
PAIRING(10, 20, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #8
PAIRING(10, 20, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #9
PAIRING(10, 20, 20, 25, 6, 7, 8, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #10
PAIRING(10, 20, 20, 25, 6, 7, 8, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #11
PAIRING(10, 20, 20, 25, 6, 7, 8, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #12
PAIRING(10, 20, 20, 25, 6, 7, 8, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #13
PAIRING(10, 20, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #14
PAIRING(10, 20, 20, 25, 6, 7, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #15
PAIRING(10, 20, 20, 25, 6, 7, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #16
PAIRING(10, 20, 20, 25, 6, 7, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #17
PAIRING(10, 20, 20, 25, 6, 7, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #18
PAIRING(10, 20, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #19
PAIRING(10, 20, 20, 25, 6, 7, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #20
PAIRING(10, 20, 20, 25, 6, 7, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #21
PAIRING(10, 20, 20, 25, 6, 7, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #22
PAIRING(10, 20, 20, 25, 6, 7, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #23
PAIRING(10, 20, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #24
PAIRING(10, 20, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #25
PAIRING(10, 20, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #26
PAIRING(10, 20, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #27
PAIRING(10, 20, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #28
PAIRING(10, 20, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #29
PAIRING(10, 20, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #30
PAIRING(10, 20, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #31
PAIRING(10, 20, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #32
PAIRING(10, 20, 20, 25, 6, 10, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #33
PAIRING(10, 20, 20, 25, 6, 10, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #34
PAIRING(10, 20, 20, 25, 6, 10, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #35
PAIRING(10, 20, 20, 25, 6, 10, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #36
PAIRING(10, 20, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #37
PAIRING(10, 20, 20, 25, 6, 10, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #38
PAIRING(10, 20, 20, 25, 6, 10, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #39
PAIRING(10, 20, 20, 25, 6, 10, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #40
PAIRING(10, 20, 20, 25, 6, 10, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #41
PAIRING(10, 20, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #42
PAIRING(10, 20, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #43
PAIRING(10, 20, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #44
PAIRING(10, 20, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #45
PAIRING(10, 20, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #46
PAIRING(10, 20, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #47
PAIRING(10, 20, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #48
PAIRING(10, 20, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #49
PAIRING(10, 20, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #50
PAIRING(10, 20, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 1) // #51
PAIRING(10, 20, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 1) // #52
PAIRING(10, 20, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #53
PAIRING(10, 20, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 2) // #54
PAIRING(10, 20, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #55
PAIRING(10, 20, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 1) // #56
PAIRING(10, 20, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 1) // #57
PAIRING(10, 20, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #58
PAIRING(10, 20, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 2) // #59
PAIRING(10, 20, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #60
PAIRING(10, 20, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #61
PAIRING(10, 20, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #62
PAIRING(10, 20, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #63
PAIRING(10, 20, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #64
PAIRING(10, 20, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #65
PAIRING(10, 20, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #66
PAIRING(10, 20, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #67
PAIRING(10, 20, 20, 25, 6, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #68
PAIRING(10, 20, 20, 25, 6, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #69
PAIRING(10, 20, 20, 25, 6, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #70
PAIRING(10, 20, 20, 25, 6, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 1) // #71
PAIRING(10, 20, 20, 25, 6, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #72
PAIRING(10, 20, 20, 25, 6, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 2) // #73
PAIRING(10, 20, 20, 25, 6, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #74
PAIRING(10, 20, 20, 25, 6, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #75
PAIRING(10, 20, 20, 25, 6, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 1) // #76
PAIRING(10, 20, 20, 25, 6, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #77
PAIRING(10, 20, 20, 25, 6, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 2) // #78
PAIRING(10, 20, 20, 25, 6, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 1) // #79
PAIRING(10, 20, 20, 25, 6, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #80
PAIRING(10, 20, 20, 25, 6, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 2) // #81
PAIRING(10, 20, 20, 25, 6, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #82
PAIRING(10, 20, 20, 25, 6, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #83
PAIRING(10, 20, 20, 25, 6, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #84
PAIRING(10, 20, 20, 25, 6, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #85
PAIRING(10, 20, 20, 25, 6, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #86
PAIRING(10, 20, 20, 25, 6, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #87
PAIRING(10, 20, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #88
PAIRING(10, 20, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 0) // #89
PAIRING(10, 20, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 0) // #90
PAIRING(10, 20, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 0) // #91
PAIRING(10, 20, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #92
PAIRING(10, 20, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #93
PAIRING(10, 20, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 0) // #94
PAIRING(10, 20, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 0) // #95
PAIRING(10, 20, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 0) // #96
PAIRING(10, 20, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #97
PAIRING(10, 20, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #98
PAIRING(10, 20, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #99
PAIRING(10, 20, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #100
PAIRING(10, 20, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #101
PAIRING(10, 20, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #102
PAIRING(10, 20, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #103
PAIRING(10, 20, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #104
PAIRING(10, 20, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #105
PAIRING(10, 20, 20, 25, 10, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 0) // #106
PAIRING(10, 20, 20, 25, 10, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 0) // #107
PAIRING(10, 20, 20, 25, 10, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #108
PAIRING(10, 20, 20, 25, 10, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 0) // #109
PAIRING(10, 20, 20, 25, 10, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 0) // #110
PAIRING(10, 20, 20, 25, 10, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #111
PAIRING(10, 20, 20, 25, 10, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #112
PAIRING(10, 20, 20, 25, 10, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #113
PAIRING(10, 20, 20, 25, 10, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 0) // #114
PAIRING(10, 20, 20, 25, 10, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 0) // #115
PAIRING(10, 20, 20, 25, 10, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #116
PAIRING(10, 20, 20, 25, 10, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 0) // #117
PAIRING(10, 20, 20, 25, 10, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 0) // #118
PAIRING(10, 20, 20, 25, 10, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #119
PAIRING(10, 20, 20, 25, 10, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #120
PAIRING(10, 20, 20, 25, 10, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #121
PAIRING(10, 20, 20, 25, 10, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #122
PAIRING(10, 20, 20, 25, 10, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #123
PAIRING(10, 20, 20, 25, 10, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #124
PAIRING(10, 20, 20, 25, 10, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #125
PAIRING(10, 20, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #126
PAIRING(10, 20, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 20, 0, 0, 0, 0, 2, 0) // #127
PAIRING(10, 20, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 20, 20, 21, 0, 0, 2, 0) // #128
PAIRING(10, 20, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 0) // #129
PAIRING(10, 20, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 1) // #130
PAIRING(10, 20, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #131
PAIRING(10, 20, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 20, 0, 0, 0, 0, 2, 0) // #132
PAIRING(10, 20, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 20, 20, 21, 0, 0, 2, 0) // #133
PAIRING(10, 20, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 0) // #134
PAIRING(10, 20, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 1) // #135
PAIRING(10, 20, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #136
PAIRING(10, 20, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #137
PAIRING(10, 20, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #138
PAIRING(10, 20, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #139
PAIRING(10, 20, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #140
PAIRING(10, 20, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #141
PAIRING(10, 20, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #142
PAIRING(10, 20, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #143
PAIRING(10, 20, 20, 25, 11, 20, 20, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 0) // #144
PAIRING(10, 20, 20, 25, 11, 20, 20, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 0) // #145
PAIRING(10, 20, 20, 25, 11, 20, 20, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #146
PAIRING(10, 20, 20, 25, 11, 20, 21, 22, 1, 1, 11, 20, 0, 0, 21, 22, 0, 0, 2, 0) // #147
PAIRING(10, 20, 20, 25, 11, 20, 21, 25, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 0) // #148
PAIRING(10, 20, 20, 25, 11, 20, 21, 26, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 1) // #149
PAIRING(10, 20, 20, 25, 11, 20, 25, 26, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #150
PAIRING(10, 20, 20, 25, 11, 20, 26, 27, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #151
PAIRING(10, 20, 20, 25, 11, 21, 21, 22, 1, 2, 11, 20, 0, 0, 20, 21, 21, 22, 2, 0) // #152
PAIRING(10, 20, 20, 25, 11, 21, 21, 25, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 0) // #153
PAIRING(10, 20, 20, 25, 11, 21, 21, 26, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 1) // #154
PAIRING(10, 20, 20, 25, 11, 21, 22, 23, 1, 2, 11, 20, 0, 0, 20, 21, 22, 23, 2, 0) // #155
PAIRING(10, 20, 20, 25, 11, 21, 22, 25, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 0) // #156
PAIRING(10, 20, 20, 25, 11, 21, 22, 26, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 1) // #157
PAIRING(10, 20, 20, 25, 11, 21, 25, 26, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #158
PAIRING(10, 20, 20, 25, 11, 21, 26, 27, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #159
PAIRING(10, 20, 20, 25, 11, 25, 25, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #160
PAIRING(10, 20, 20, 25, 11, 25, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #161
PAIRING(10, 20, 20, 25, 11, 26, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #162
PAIRING(10, 20, 20, 25, 11, 26, 27, 28, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #163
PAIRING(10, 20, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #164
PAIRING(10, 20, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #165
PAIRING(10, 20, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #166
PAIRING(10, 20, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #167
PAIRING(10, 20, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #168
PAIRING(10, 20, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #169
PAIRING(10, 20, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #170
PAIRING(10, 20, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #171
PAIRING(10, 20, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #172
PAIRING(10, 20, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #173
PAIRING(10, 20, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #174
PAIRING(10, 20, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #175
PAIRING(10, 20, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #176
PAIRING(10, 20, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #177
PAIRING(10, 20, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #178
PAIRING(10, 20, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #179
PAIRING(10, 20, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #180
PAIRING(10, 20, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #181
PAIRING(10, 20, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #182
PAIRING(10, 20, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #183
PAIRING(10, 20, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #184
PAIRING(10, 20, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #185
PAIRING(10, 20, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #186
PAIRING(10, 20, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #187
PAIRING(10, 20, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #188
PAIRING(10, 20, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #189
PAIRING(10, 20, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #190
PAIRING(10, 20, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #191
PAIRING(10, 15, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #192
PAIRING(10, 15, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #193
PAIRING(10, 15, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #194
PAIRING(10, 15, 20, 25, 6, 7, 7, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #195
PAIRING(10, 15, 20, 25, 6, 7, 7, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #196
PAIRING(10, 15, 20, 25, 6, 7, 7, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #197
PAIRING(10, 15, 20, 25, 6, 7, 7, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #198
PAIRING(10, 15, 20, 25, 6, 7, 7, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #199
PAIRING(10, 15, 20, 25, 6, 7, 7, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #200
PAIRING(10, 15, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #201
PAIRING(10, 15, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #202
PAIRING(10, 15, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #203
PAIRING(10, 15, 20, 25, 6, 7, 8, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #204
PAIRING(10, 15, 20, 25, 6, 7, 8, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #205
PAIRING(10, 15, 20, 25, 6, 7, 8, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #206
PAIRING(10, 15, 20, 25, 6, 7, 8, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #207
PAIRING(10, 15, 20, 25, 6, 7, 8, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #208
PAIRING(10, 15, 20, 25, 6, 7, 8, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #209
PAIRING(10, 15, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #210
PAIRING(10, 15, 20, 25, 6, 7, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #211
PAIRING(10, 15, 20, 25, 6, 7, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #212
PAIRING(10, 15, 20, 25, 6, 7, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #213
PAIRING(10, 15, 20, 25, 6, 7, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #214
PAIRING(10, 15, 20, 25, 6, 7, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #215
PAIRING(10, 15, 20, 25, 6, 7, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #216
PAIRING(10, 15, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #217
PAIRING(10, 15, 20, 25, 6, 7, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #218
PAIRING(10, 15, 20, 25, 6, 7, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #219
PAIRING(10, 15, 20, 25, 6, 7, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #220
PAIRING(10, 15, 20, 25, 6, 7, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #221
PAIRING(10, 15, 20, 25, 6, 7, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #222
PAIRING(10, 15, 20, 25, 6, 7, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #223
PAIRING(10, 15, 20, 25, 6, 7, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #224
PAIRING(10, 15, 20, 25, 6, 7, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #225
PAIRING(10, 15, 20, 25, 6, 7, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #226
PAIRING(10, 15, 20, 25, 6, 7, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #227
PAIRING(10, 15, 20, 25, 6, 7, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #228
PAIRING(10, 15, 20, 25, 6, 7, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #229
PAIRING(10, 15, 20, 25, 6, 7, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #230
PAIRING(10, 15, 20, 25, 6, 7, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #231
PAIRING(10, 15, 20, 25, 6, 7, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #232
PAIRING(10, 15, 20, 25, 6, 7, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #233
PAIRING(10, 15, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #234
PAIRING(10, 15, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #235
PAIRING(10, 15, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #236
PAIRING(10, 15, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #237
PAIRING(10, 15, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #238
PAIRING(10, 15, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #239
PAIRING(10, 15, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #240
PAIRING(10, 15, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #241
PAIRING(10, 15, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #242
PAIRING(10, 15, 20, 25, 6, 10, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #243
PAIRING(10, 15, 20, 25, 6, 10, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #244
PAIRING(10, 15, 20, 25, 6, 10, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #245
PAIRING(10, 15, 20, 25, 6, 10, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #246
PAIRING(10, 15, 20, 25, 6, 10, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #247
PAIRING(10, 15, 20, 25, 6, 10, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #248
PAIRING(10, 15, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #249
PAIRING(10, 15, 20, 25, 6, 10, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #250
PAIRING(10, 15, 20, 25, 6, 10, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #251
PAIRING(10, 15, 20, 25, 6, 10, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #252
PAIRING(10, 15, 20, 25, 6, 10, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #253
PAIRING(10, 15, 20, 25, 6, 10, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #254
PAIRING(10, 15, 20, 25, 6, 10, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #255
PAIRING(10, 15, 20, 25, 6, 10, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #256
PAIRING(10, 15, 20, 25, 6, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #257
PAIRING(10, 15, 20, 25, 6, 10, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #258
PAIRING(10, 15, 20, 25, 6, 10, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #259
PAIRING(10, 15, 20, 25, 6, 10, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #260
PAIRING(10, 15, 20, 25, 6, 10, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #261
PAIRING(10, 15, 20, 25, 6, 10, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #262
PAIRING(10, 15, 20, 25, 6, 10, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #263
PAIRING(10, 15, 20, 25, 6, 10, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #264
PAIRING(10, 15, 20, 25, 6, 10, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #265
PAIRING(10, 15, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #266
PAIRING(10, 15, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #267
PAIRING(10, 15, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #268
PAIRING(10, 15, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #269
PAIRING(10, 15, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #270
PAIRING(10, 15, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #271
PAIRING(10, 15, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #272
PAIRING(10, 15, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #273
PAIRING(10, 15, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #274
PAIRING(10, 15, 20, 25, 6, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #275
PAIRING(10, 15, 20, 25, 6, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 2) // #276
PAIRING(10, 15, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #277
PAIRING(10, 15, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 2) // #278
PAIRING(10, 15, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #279
PAIRING(10, 15, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #280
PAIRING(10, 15, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #281
PAIRING(10, 15, 20, 25, 6, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #282
PAIRING(10, 15, 20, 25, 6, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 2) // #283
PAIRING(10, 15, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #284
PAIRING(10, 15, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 2) // #285
PAIRING(10, 15, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #286
PAIRING(10, 15, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #287
PAIRING(10, 15, 20, 25, 6, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #288
PAIRING(10, 15, 20, 25, 6, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #289
PAIRING(10, 15, 20, 25, 6, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #290
PAIRING(10, 15, 20, 25, 6, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #291
PAIRING(10, 15, 20, 25, 6, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #292
PAIRING(10, 15, 20, 25, 6, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #293
PAIRING(10, 15, 20, 25, 6, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #294
PAIRING(10, 15, 20, 25, 6, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #295
PAIRING(10, 15, 20, 25, 6, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #296
PAIRING(10, 15, 20, 25, 6, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #297
PAIRING(10, 15, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #298
PAIRING(10, 15, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #299
PAIRING(10, 15, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #300
PAIRING(10, 15, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #301
PAIRING(10, 15, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #302
PAIRING(10, 15, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #303
PAIRING(10, 15, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #304
PAIRING(10, 15, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #305
PAIRING(10, 15, 20, 25, 6, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #306
PAIRING(10, 15, 20, 25, 6, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #307
PAIRING(10, 15, 20, 25, 6, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #308
PAIRING(10, 15, 20, 25, 6, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #309
PAIRING(10, 15, 20, 25, 6, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #310
PAIRING(10, 15, 20, 25, 6, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #311
PAIRING(10, 15, 20, 25, 6, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #312
PAIRING(10, 15, 20, 25, 6, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #313
PAIRING(10, 15, 20, 25, 6, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #314
PAIRING(10, 15, 20, 25, 6, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #315
PAIRING(10, 15, 20, 25, 6, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #316
PAIRING(10, 15, 20, 25, 6, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #317
PAIRING(10, 15, 20, 25, 6, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #318
PAIRING(10, 15, 20, 25, 6, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #319
PAIRING(10, 15, 20, 25, 6, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #320
PAIRING(10, 15, 20, 25, 6, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #321
PAIRING(10, 15, 20, 25, 6, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #322
PAIRING(10, 15, 20, 25, 6, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #323
PAIRING(10, 15, 20, 25, 6, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #324
PAIRING(10, 15, 20, 25, 6, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #325
PAIRING(10, 15, 20, 25, 6, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #326
PAIRING(10, 15, 20, 25, 6, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #327
PAIRING(10, 15, 20, 25, 6, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #328
PAIRING(10, 15, 20, 25, 6, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #329
PAIRING(10, 15, 20, 25, 6, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #330
PAIRING(10, 15, 20, 25, 6, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #331
PAIRING(10, 15, 20, 25, 6, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #332
PAIRING(10, 15, 20, 25, 6, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #333
PAIRING(10, 15, 20, 25, 6, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #334
PAIRING(10, 15, 20, 25, 6, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #335
PAIRING(10, 15, 20, 25, 6, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #336
PAIRING(10, 15, 20, 25, 6, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #337
PAIRING(10, 15, 20, 25, 6, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #338
PAIRING(10, 15, 20, 25, 6, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #339
PAIRING(10, 15, 20, 25, 6, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #340
PAIRING(10, 15, 20, 25, 6, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #341
PAIRING(10, 15, 20, 25, 6, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #342
PAIRING(10, 15, 20, 25, 6, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #343
PAIRING(10, 15, 20, 25, 6, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #344
PAIRING(10, 15, 20, 25, 6, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #345
PAIRING(10, 15, 20, 25, 6, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #346
PAIRING(10, 15, 20, 25, 6, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #347
PAIRING(10, 15, 20, 25, 6, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #348
PAIRING(10, 15, 20, 25, 6, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #349
PAIRING(10, 15, 20, 25, 6, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #350
PAIRING(10, 15, 20, 25, 6, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #351
PAIRING(10, 15, 20, 25, 6, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #352
PAIRING(10, 15, 20, 25, 6, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #353
PAIRING(10, 15, 20, 25, 6, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #354
PAIRING(10, 15, 20, 25, 6, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #355
PAIRING(10, 15, 20, 25, 6, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #356
PAIRING(10, 15, 20, 25, 6, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #357
PAIRING(10, 15, 20, 25, 6, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #358
PAIRING(10, 15, 20, 25, 6, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #359
PAIRING(10, 15, 20, 25, 6, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #360
PAIRING(10, 15, 20, 25, 6, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #361
PAIRING(10, 15, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #362
PAIRING(10, 15, 20, 25, 10, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #363
PAIRING(10, 15, 20, 25, 10, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #364
PAIRING(10, 15, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #365
PAIRING(10, 15, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 1) // #366
PAIRING(10, 15, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #367
PAIRING(10, 15, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #368
PAIRING(10, 15, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #369
PAIRING(10, 15, 20, 25, 10, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #370
PAIRING(10, 15, 20, 25, 10, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #371
PAIRING(10, 15, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #372
PAIRING(10, 15, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 1) // #373
PAIRING(10, 15, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #374
PAIRING(10, 15, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #375
PAIRING(10, 15, 20, 25, 10, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #376
PAIRING(10, 15, 20, 25, 10, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #377
PAIRING(10, 15, 20, 25, 10, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #378
PAIRING(10, 15, 20, 25, 10, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #379
PAIRING(10, 15, 20, 25, 10, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #380
PAIRING(10, 15, 20, 25, 10, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #381
PAIRING(10, 15, 20, 25, 10, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #382
PAIRING(10, 15, 20, 25, 10, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #383
PAIRING(10, 15, 20, 25, 10, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #384
PAIRING(10, 15, 20, 25, 10, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #385
PAIRING(10, 15, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #386
PAIRING(10, 15, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #387
PAIRING(10, 15, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #388
PAIRING(10, 15, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #389
PAIRING(10, 15, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #390
PAIRING(10, 15, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #391
PAIRING(10, 15, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #392
PAIRING(10, 15, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #393
PAIRING(10, 15, 20, 25, 10, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #394
PAIRING(10, 15, 20, 25, 10, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #395
PAIRING(10, 15, 20, 25, 10, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #396
PAIRING(10, 15, 20, 25, 10, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #397
PAIRING(10, 15, 20, 25, 10, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #398
PAIRING(10, 15, 20, 25, 10, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #399
PAIRING(10, 15, 20, 25, 10, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #400
PAIRING(10, 15, 20, 25, 10, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #401
PAIRING(10, 15, 20, 25, 10, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #402
PAIRING(10, 15, 20, 25, 10, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #403
PAIRING(10, 15, 20, 25, 10, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #404
PAIRING(10, 15, 20, 25, 10, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #405
PAIRING(10, 15, 20, 25, 10, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #406
PAIRING(10, 15, 20, 25, 10, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #407
PAIRING(10, 15, 20, 25, 10, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #408
PAIRING(10, 15, 20, 25, 10, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #409
PAIRING(10, 15, 20, 25, 10, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #410
PAIRING(10, 15, 20, 25, 10, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #411
PAIRING(10, 15, 20, 25, 10, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #412
PAIRING(10, 15, 20, 25, 10, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #413
PAIRING(10, 15, 20, 25, 10, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #414
PAIRING(10, 15, 20, 25, 10, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #415
PAIRING(10, 15, 20, 25, 10, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #416
PAIRING(10, 15, 20, 25, 10, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #417
PAIRING(10, 15, 20, 25, 10, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #418
PAIRING(10, 15, 20, 25, 10, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #419
PAIRING(10, 15, 20, 25, 10, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #420
PAIRING(10, 15, 20, 25, 10, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #421
PAIRING(10, 15, 20, 25, 10, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #422
PAIRING(10, 15, 20, 25, 10, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #423
PAIRING(10, 15, 20, 25, 10, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #424
PAIRING(10, 15, 20, 25, 10, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #425
PAIRING(10, 15, 20, 25, 10, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #426
PAIRING(10, 15, 20, 25, 10, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #427
PAIRING(10, 15, 20, 25, 10, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #428
PAIRING(10, 15, 20, 25, 10, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #429
PAIRING(10, 15, 20, 25, 10, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #430
PAIRING(10, 15, 20, 25, 10, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #431
PAIRING(10, 15, 20, 25, 10, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #432
PAIRING(10, 15, 20, 25, 10, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #433
PAIRING(10, 15, 20, 25, 10, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #434
PAIRING(10, 15, 20, 25, 10, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #435
PAIRING(10, 15, 20, 25, 10, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #436
PAIRING(10, 15, 20, 25, 10, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #437
PAIRING(10, 15, 20, 25, 10, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #438
PAIRING(10, 15, 20, 25, 10, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #439
PAIRING(10, 15, 20, 25, 10, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #440
PAIRING(10, 15, 20, 25, 10, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #441
PAIRING(10, 15, 20, 25, 10, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #442
PAIRING(10, 15, 20, 25, 10, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #443
PAIRING(10, 15, 20, 25, 10, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #444
PAIRING(10, 15, 20, 25, 10, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #445
PAIRING(10, 15, 20, 25, 10, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #446
PAIRING(10, 15, 20, 25, 10, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #447
PAIRING(10, 15, 20, 25, 10, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #448
PAIRING(10, 15, 20, 25, 10, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #449
PAIRING(10, 15, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #450
PAIRING(10, 15, 20, 25, 11, 12, 12, 15, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #451
PAIRING(10, 15, 20, 25, 11, 12, 12, 16, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 1) // #452
PAIRING(10, 15, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #453
PAIRING(10, 15, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 15, 20, 21, 0, 0, 2, 1) // #454
PAIRING(10, 15, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #455
PAIRING(10, 15, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #456
PAIRING(10, 15, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #457
PAIRING(10, 15, 20, 25, 11, 12, 13, 15, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #458
PAIRING(10, 15, 20, 25, 11, 12, 13, 16, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 1) // #459
PAIRING(10, 15, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #460
PAIRING(10, 15, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 15, 20, 21, 0, 0, 2, 1) // #461
PAIRING(10, 15, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #462
PAIRING(10, 15, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #463
PAIRING(10, 15, 20, 25, 11, 12, 15, 16, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #464
PAIRING(10, 15, 20, 25, 11, 12, 15, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #465
PAIRING(10, 15, 20, 25, 11, 12, 15, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #466
PAIRING(10, 15, 20, 25, 11, 12, 15, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #467
PAIRING(10, 15, 20, 25, 11, 12, 15, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #468
PAIRING(10, 15, 20, 25, 11, 12, 16, 17, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #469
PAIRING(10, 15, 20, 25, 11, 12, 16, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #470
PAIRING(10, 15, 20, 25, 11, 12, 16, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #471
PAIRING(10, 15, 20, 25, 11, 12, 16, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #472
PAIRING(10, 15, 20, 25, 11, 12, 16, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #473
PAIRING(10, 15, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #474
PAIRING(10, 15, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #475
PAIRING(10, 15, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #476
PAIRING(10, 15, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #477
PAIRING(10, 15, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #478
PAIRING(10, 15, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #479
PAIRING(10, 15, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #480
PAIRING(10, 15, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #481
PAIRING(10, 15, 20, 25, 11, 15, 15, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #482
PAIRING(10, 15, 20, 25, 11, 15, 15, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #483
PAIRING(10, 15, 20, 25, 11, 15, 15, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #484
PAIRING(10, 15, 20, 25, 11, 15, 15, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #485
PAIRING(10, 15, 20, 25, 11, 15, 15, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #486
PAIRING(10, 15, 20, 25, 11, 15, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #487
PAIRING(10, 15, 20, 25, 11, 15, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #488
PAIRING(10, 15, 20, 25, 11, 15, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #489
PAIRING(10, 15, 20, 25, 11, 15, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #490
PAIRING(10, 15, 20, 25, 11, 15, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #491
PAIRING(10, 15, 20, 25, 11, 15, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #492
PAIRING(10, 15, 20, 25, 11, 15, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #493
PAIRING(10, 15, 20, 25, 11, 15, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #494
PAIRING(10, 15, 20, 25, 11, 15, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #495
PAIRING(10, 15, 20, 25, 11, 15, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #496
PAIRING(10, 15, 20, 25, 11, 15, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #497
PAIRING(10, 15, 20, 25, 11, 15, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #498
PAIRING(10, 15, 20, 25, 11, 15, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #499
PAIRING(10, 15, 20, 25, 11, 16, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #500
PAIRING(10, 15, 20, 25, 11, 16, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #501
PAIRING(10, 15, 20, 25, 11, 16, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #502
PAIRING(10, 15, 20, 25, 11, 16, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #503
PAIRING(10, 15, 20, 25, 11, 16, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #504
PAIRING(10, 15, 20, 25, 11, 16, 17, 18, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #505
PAIRING(10, 15, 20, 25, 11, 16, 17, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #506
PAIRING(10, 15, 20, 25, 11, 16, 17, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #507
PAIRING(10, 15, 20, 25, 11, 16, 17, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #508
PAIRING(10, 15, 20, 25, 11, 16, 17, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #509
PAIRING(10, 15, 20, 25, 11, 16, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #510
PAIRING(10, 15, 20, 25, 11, 16, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #511
PAIRING(10, 15, 20, 25, 11, 16, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #512
PAIRING(10, 15, 20, 25, 11, 16, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 1) // #513
PAIRING(10, 15, 20, 25, 11, 16, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #514
PAIRING(10, 15, 20, 25, 11, 16, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 2) // #515
PAIRING(10, 15, 20, 25, 11, 16, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #516
PAIRING(10, 15, 20, 25, 11, 16, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #517
PAIRING(10, 15, 20, 25, 11, 20, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #518
PAIRING(10, 15, 20, 25, 11, 20, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #519
PAIRING(10, 15, 20, 25, 11, 20, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #520
PAIRING(10, 15, 20, 25, 11, 20, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #521
PAIRING(10, 15, 20, 25, 11, 20, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #522
PAIRING(10, 15, 20, 25, 11, 20, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #523
PAIRING(10, 15, 20, 25, 11, 20, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #524
PAIRING(10, 15, 20, 25, 11, 20, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #525
PAIRING(10, 15, 20, 25, 11, 21, 21, 22, 1, 2, 11, 15, 0, 0, 20, 21, 21, 22, 2, 1) // #526
PAIRING(10, 15, 20, 25, 11, 21, 21, 25, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 1) // #527
PAIRING(10, 15, 20, 25, 11, 21, 21, 26, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 2) // #528
PAIRING(10, 15, 20, 25, 11, 21, 22, 23, 1, 2, 11, 15, 0, 0, 20, 21, 22, 23, 2, 1) // #529
PAIRING(10, 15, 20, 25, 11, 21, 22, 25, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 1) // #530
PAIRING(10, 15, 20, 25, 11, 21, 22, 26, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 2) // #531
PAIRING(10, 15, 20, 25, 11, 21, 25, 26, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #532
PAIRING(10, 15, 20, 25, 11, 21, 26, 27, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #533
PAIRING(10, 15, 20, 25, 11, 25, 25, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #534
PAIRING(10, 15, 20, 25, 11, 25, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #535
PAIRING(10, 15, 20, 25, 11, 26, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #536
PAIRING(10, 15, 20, 25, 11, 26, 27, 28, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #537
PAIRING(10, 15, 20, 25, 15, 16, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #538
PAIRING(10, 15, 20, 25, 15, 16, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #539
PAIRING(10, 15, 20, 25, 15, 16, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #540
PAIRING(10, 15, 20, 25, 15, 16, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #541
PAIRING(10, 15, 20, 25, 15, 16, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #542
PAIRING(10, 15, 20, 25, 15, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #543
PAIRING(10, 15, 20, 25, 15, 16, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #544
PAIRING(10, 15, 20, 25, 15, 16, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #545
PAIRING(10, 15, 20, 25, 15, 16, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #546
PAIRING(10, 15, 20, 25, 15, 16, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #547
PAIRING(10, 15, 20, 25, 15, 16, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #548
PAIRING(10, 15, 20, 25, 15, 16, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #549
PAIRING(10, 15, 20, 25, 15, 16, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #550
PAIRING(10, 15, 20, 25, 15, 16, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #551
PAIRING(10, 15, 20, 25, 15, 16, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #552
PAIRING(10, 15, 20, 25, 15, 16, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #553
PAIRING(10, 15, 20, 25, 15, 16, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #554
PAIRING(10, 15, 20, 25, 15, 16, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #555
PAIRING(10, 15, 20, 25, 15, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #556
PAIRING(10, 15, 20, 25, 15, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #557
PAIRING(10, 15, 20, 25, 15, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #558
PAIRING(10, 15, 20, 25, 15, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #559
PAIRING(10, 15, 20, 25, 15, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #560
PAIRING(10, 15, 20, 25, 15, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #561
PAIRING(10, 15, 20, 25, 15, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #562
PAIRING(10, 15, 20, 25, 15, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #563
PAIRING(10, 15, 20, 25, 15, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #564
PAIRING(10, 15, 20, 25, 15, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #565
PAIRING(10, 15, 20, 25, 15, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #566
PAIRING(10, 15, 20, 25, 15, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #567
PAIRING(10, 15, 20, 25, 15, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #568
PAIRING(10, 15, 20, 25, 15, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #569
PAIRING(10, 15, 20, 25, 15, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #570
PAIRING(10, 15, 20, 25, 15, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #571
PAIRING(10, 15, 20, 25, 15, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #572
PAIRING(10, 15, 20, 25, 15, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #573
PAIRING(10, 15, 20, 25, 15, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #574
PAIRING(10, 15, 20, 25, 15, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #575
PAIRING(10, 15, 20, 25, 16, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #576
PAIRING(10, 15, 20, 25, 16, 17, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #577
PAIRING(10, 15, 20, 25, 16, 17, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #578
PAIRING(10, 15, 20, 25, 16, 17, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #579
PAIRING(10, 15, 20, 25, 16, 17, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #580
PAIRING(10, 15, 20, 25, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #581
PAIRING(10, 15, 20, 25, 16, 17, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #582
PAIRING(10, 15, 20, 25, 16, 17, 18, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #583
PAIRING(10, 15, 20, 25, 16, 17, 18, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #584
PAIRING(10, 15, 20, 25, 16, 17, 18, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #585
PAIRING(10, 15, 20, 25, 16, 17, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #586
PAIRING(10, 15, 20, 25, 16, 17, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #587
PAIRING(10, 15, 20, 25, 16, 17, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #588
PAIRING(10, 15, 20, 25, 16, 17, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #589
PAIRING(10, 15, 20, 25, 16, 17, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #590
PAIRING(10, 15, 20, 25, 16, 17, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #591
PAIRING(10, 15, 20, 25, 16, 17, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #592
PAIRING(10, 15, 20, 25, 16, 17, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #593
PAIRING(10, 15, 20, 25, 16, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #594
PAIRING(10, 15, 20, 25, 16, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #595
PAIRING(10, 15, 20, 25, 16, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #596
PAIRING(10, 15, 20, 25, 16, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #597
PAIRING(10, 15, 20, 25, 16, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #598
PAIRING(10, 15, 20, 25, 16, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #599
PAIRING(10, 15, 20, 25, 16, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #600
PAIRING(10, 15, 20, 25, 16, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #601
PAIRING(10, 15, 20, 25, 16, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #602
PAIRING(10, 15, 20, 25, 16, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #603
PAIRING(10, 15, 20, 25, 16, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #604
PAIRING(10, 15, 20, 25, 16, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #605
PAIRING(10, 15, 20, 25, 16, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #606
PAIRING(10, 15, 20, 25, 16, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #607
PAIRING(10, 15, 20, 25, 16, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #608
PAIRING(10, 15, 20, 25, 16, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #609
PAIRING(10, 15, 20, 25, 16, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #610
PAIRING(10, 15, 20, 25, 16, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #611
PAIRING(10, 15, 20, 25, 16, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #612
PAIRING(10, 15, 20, 25, 16, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #613
PAIRING(10, 15, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #614
PAIRING(10, 15, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #615
PAIRING(10, 15, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #616
PAIRING(10, 15, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #617
PAIRING(10, 15, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #618
PAIRING(10, 15, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #619
PAIRING(10, 15, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #620
PAIRING(10, 15, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #621
PAIRING(10, 15, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #622
PAIRING(10, 15, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #623
PAIRING(10, 15, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #624
PAIRING(10, 15, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #625
PAIRING(10, 15, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #626
PAIRING(10, 15, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #627
PAIRING(10, 15, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #628
PAIRING(10, 15, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #629
PAIRING(10, 15, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #630
PAIRING(10, 15, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #631
PAIRING(10, 15, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #632
PAIRING(10, 15, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #633
PAIRING(10, 15, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #634
PAIRING(10, 15, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #635
PAIRING(10, 15, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #636
PAIRING(10, 15, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #637
PAIRING(10, 15, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #638
PAIRING(10, 15, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #639
PAIRING(10, 15, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #640
PAIRING(10, 15, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #641

Просмотреть файл

@ -0,0 +1,8 @@
cu-boundary antimony
SO 10 11 0x12 antimony
FUN 20 21 0x22 arsenic
SO 30 31 0x32
cu-boundary aluminum
SO 40 41 0x42 aluminum
FUN 50 51 0x52 selenium
SO 60 61 0x62

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2007, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without

Просмотреть файл

@ -9,6 +9,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define if you have POSIX threads libraries and header files. */
#undef HAVE_PTHREAD
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
@ -30,6 +33,10 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
@ -45,9 +52,16 @@
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS

Просмотреть файл

@ -116,6 +116,16 @@ typedef struct {
} MDRawContextARM;
/* Indices into iregs for registers with a dedicated or conventional
* purpose.
*/
enum MDARMRegisterNumbers {
MD_CONTEXT_ARM_REG_FP = 11,
MD_CONTEXT_ARM_REG_SP = 13,
MD_CONTEXT_ARM_REG_LR = 14,
MD_CONTEXT_ARM_REG_PC = 15
};
/* For (MDRawContextARM).context_flags. These values indicate the type of
* context stored in the structure. */
#define MD_CONTEXT_ARM_INTEGER (MD_CONTEXT_ARM | 0x00000002)

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -60,10 +60,10 @@ class BasicSourceLineResolver : public SourceLineResolverInterface {
virtual bool LoadModuleUsingMapBuffer(const string &module_name,
const string &map_buffer);
virtual bool HasModule(const string &module_name) const;
virtual StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const;
virtual void FillSourceLineInfo(StackFrame *frame) const;
virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const;
virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const;
private:
template<class T> class MemAddrMap;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -50,10 +50,10 @@ class MemoryRegion {
virtual ~MemoryRegion() {}
// The base address of this memory region.
virtual u_int64_t GetBase() = 0;
virtual u_int64_t GetBase() const = 0;
// The size of this memory region.
virtual u_int32_t GetSize() = 0;
virtual u_int32_t GetSize() const = 0;
// Access to data of various sizes within the memory region. address
// is a pointer to read, and it must lie within the memory region as
@ -63,10 +63,10 @@ class MemoryRegion {
// program. Returns true on success. Fails and returns false if address
// is out of the region's bounds (after considering the width of value),
// or for other types of errors.
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) = 0;
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) = 0;
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) = 0;
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) = 0;
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const =0;
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const =0;
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const =0;
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const =0;
};

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -242,22 +242,22 @@ class MinidumpMemoryRegion : public MinidumpObject,
// Returns a pointer to the base of the memory region. Returns the
// cached value if available, otherwise, reads the minidump file and
// caches the memory region.
const u_int8_t* GetMemory();
const u_int8_t* GetMemory() const;
// The address of the base of the memory region.
u_int64_t GetBase();
u_int64_t GetBase() const;
// The size, in bytes, of the memory region.
u_int32_t GetSize();
u_int32_t GetSize() const;
// Frees the cached memory region, if cached.
void FreeMemory();
// Obtains the value of memory at the pointer specified by address.
bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value);
bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value);
bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value);
bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value);
bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const;
bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const;
bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const;
bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const;
// Print a human-readable representation of the object to stdout.
void Print();
@ -274,7 +274,7 @@ class MinidumpMemoryRegion : public MinidumpObject,
// Implementation for GetMemoryAtAddress
template<typename T> bool GetMemoryAtAddressInternal(u_int64_t address,
T* value);
T* value) const;
// The largest memory region that will be read from a minidump. The
// default is 1MB.
@ -285,7 +285,7 @@ class MinidumpMemoryRegion : public MinidumpObject,
MDMemoryDescriptor* descriptor_;
// Cached memory.
vector<u_int8_t>* memory_;
mutable vector<u_int8_t>* memory_;
};

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright (c) 2006, Google Inc.
// -*- mode: C++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -40,7 +42,8 @@ namespace google_breakpad {
using std::string;
struct StackFrame;
struct StackFrameInfo;
struct WindowsFrameInfo;
struct CFIFrameInfo;
class SourceLineResolverInterface {
public:
@ -65,12 +68,22 @@ class SourceLineResolverInterface {
// Fills in the function_base, function_name, source_file_name,
// and source_line fields of the StackFrame. The instruction and
// module_name fields must already be filled in. Additional debugging
// information, if available, is returned. If the information is not
// available, returns NULL. A NULL return value does not indicate an
// error. The caller takes ownership of any returned StackFrameInfo
// object.
virtual StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const = 0;
// module_name fields must already be filled in.
virtual void FillSourceLineInfo(StackFrame *frame) const = 0;
// If Windows stack walking information is available covering
// FRAME's instruction address, return a WindowsFrameInfo structure
// describing it. If the information is not available, returns NULL.
// A NULL return value does not indicate an error. The caller takes
// ownership of any returned WindowsFrameInfo object.
virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame)
const = 0;
// If CFI stack walking information is available covering ADDRESS,
// return a CFIFrameInfo structure describing it. If the information
// is not available, return NULL. The caller takes ownership of any
// returned CFIFrameInfo object.
virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const = 0;
protected:
// SourceLineResolverInterface cannot be instantiated except by subclasses

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright (c) 2006, Google Inc.
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -44,17 +46,27 @@
namespace google_breakpad {
struct WindowsFrameInfo;
struct CFIFrameInfo;
struct StackFrameX86 : public StackFrame {
// ContextValidity has one entry for each relevant hardware pointer register
// (%eip and %esp) and one entry for each nonvolatile (callee-save) register.
// ContextValidity has one entry for each relevant hardware pointer
// register (%eip and %esp) and one entry for each general-purpose
// register. It's worthwhile having validity flags for caller-saves
// registers: they are valid in the youngest frame, and such a frame
// might save a callee-saves register in a caller-saves register, but
// SimpleCFIWalker won't touch registers unless they're marked as valid.
enum ContextValidity {
CONTEXT_VALID_NONE = 0,
CONTEXT_VALID_EIP = 1 << 0,
CONTEXT_VALID_ESP = 1 << 1,
CONTEXT_VALID_EBP = 1 << 2,
CONTEXT_VALID_EBX = 1 << 3,
CONTEXT_VALID_ESI = 1 << 4,
CONTEXT_VALID_EDI = 1 << 5,
CONTEXT_VALID_EAX = 1 << 3,
CONTEXT_VALID_EBX = 1 << 4,
CONTEXT_VALID_ECX = 1 << 5,
CONTEXT_VALID_EDX = 1 << 6,
CONTEXT_VALID_ESI = 1 << 7,
CONTEXT_VALID_EDI = 1 << 8,
CONTEXT_VALID_ALL = -1
};
@ -74,7 +86,10 @@ struct StackFrameX86 : public StackFrame {
StackFrameX86()
: context(),
context_validity(CONTEXT_VALID_NONE),
trust(FRAME_TRUST_NONE) {}
trust(FRAME_TRUST_NONE),
windows_frame_info(NULL),
cfi_frame_info(NULL) {}
~StackFrameX86();
// Register state. This is only fully valid for the topmost frame in a
// stack. In other frames, the values of nonvolatile registers may be
@ -90,6 +105,11 @@ struct StackFrameX86 : public StackFrame {
// Amount of trust the stack walker has in the instruction pointer
// of this frame.
FrameTrust trust;
// Any stack walking information we found describing this.instruction.
// These may be NULL if there is no such information for that address.
WindowsFrameInfo *windows_frame_info;
CFIFrameInfo *cfi_frame_info;
};
struct StackFramePPC : public StackFrame {
@ -119,28 +139,44 @@ struct StackFramePPC : public StackFrame {
};
struct StackFrameAMD64 : public StackFrame {
// ContextValidity has one entry for each relevant hardware pointer register
// (%rip and %rsp) and one entry for each nonvolatile (callee-save) register.
//FIXME: validate this list
// ContextValidity has one entry for each register that we might be able
// to recover.
enum ContextValidity {
CONTEXT_VALID_NONE = 0,
CONTEXT_VALID_RIP = 1 << 0,
CONTEXT_VALID_RSP = 1 << 1,
CONTEXT_VALID_RBP = 1 << 2,
CONTEXT_VALID_NONE = 0,
CONTEXT_VALID_RAX = 1 << 0,
CONTEXT_VALID_RDX = 1 << 1,
CONTEXT_VALID_RCX = 1 << 2,
CONTEXT_VALID_RBX = 1 << 3,
CONTEXT_VALID_RSI = 1 << 4,
CONTEXT_VALID_RDI = 1 << 5,
CONTEXT_VALID_RBP = 1 << 6,
CONTEXT_VALID_RSP = 1 << 7,
CONTEXT_VALID_R8 = 1 << 8,
CONTEXT_VALID_R9 = 1 << 9,
CONTEXT_VALID_R10 = 1 << 10,
CONTEXT_VALID_R11 = 1 << 11,
CONTEXT_VALID_R12 = 1 << 12,
CONTEXT_VALID_R13 = 1 << 13,
CONTEXT_VALID_R14 = 1 << 14,
CONTEXT_VALID_R15 = 1 << 15,
CONTEXT_VALID_RIP = 1 << 16,
CONTEXT_VALID_ALL = -1
};
StackFrameAMD64() : context(), context_validity(CONTEXT_VALID_NONE) {}
// Register state. This is only fully valid for the topmost frame in a
// stack. In other frames, the values of nonvolatile registers may be
// present, given sufficient debugging information. Refer to
// context_validity.
// Register state. This is only fully valid for the topmost frame in a
// stack. In other frames, which registers are present depends on what
// debugging information we had available. Refer to context_validity.
MDRawContextAMD64 context;
// context_validity is actually ContextValidity, but int is used because
// the OR operator doesn't work well with enumerated types. This indicates
// which fields in context are valid.
// For each register in context whose value has been recovered, we set
// the corresponding CONTEXT_VALID_ bit in context_validity.
//
// context_validity's type should actually be ContextValidity, but
// we use int instead because the bitwise inclusive or operator
// yields an int when applied to enum values, and C++ doesn't
// silently convert from ints to enums.
int context_validity;
};
@ -169,29 +205,54 @@ struct StackFrameSPARC : public StackFrame {
};
struct StackFrameARM : public StackFrame {
// ContextValidity should eventually contain entries for the validity of
// other nonvolatile (callee-save) registers as in
// StackFrameX86::ContextValidity. I suspect this list is sufficient
// for arm stackwalking.
// A flag for each register we might know.
enum ContextValidity {
CONTEXT_VALID_NONE = 0,
CONTEXT_VALID_R13 = 1 << 0,
CONTEXT_VALID_R14 = 1 << 1,
CONTEXT_VALID_R15 = 1 << 2,
CONTEXT_VALID_ALL = -1
CONTEXT_VALID_R0 = 1 << 0,
CONTEXT_VALID_R1 = 1 << 1,
CONTEXT_VALID_R2 = 1 << 2,
CONTEXT_VALID_R3 = 1 << 3,
CONTEXT_VALID_R4 = 1 << 4,
CONTEXT_VALID_R5 = 1 << 5,
CONTEXT_VALID_R6 = 1 << 6,
CONTEXT_VALID_R7 = 1 << 7,
CONTEXT_VALID_R8 = 1 << 8,
CONTEXT_VALID_R9 = 1 << 9,
CONTEXT_VALID_R10 = 1 << 10,
CONTEXT_VALID_R11 = 1 << 11,
CONTEXT_VALID_R12 = 1 << 12,
CONTEXT_VALID_R13 = 1 << 13,
CONTEXT_VALID_R14 = 1 << 14,
CONTEXT_VALID_R15 = 1 << 15,
CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE,
// Aliases for registers with dedicated or conventional roles.
CONTEXT_VALID_FP = CONTEXT_VALID_R11,
CONTEXT_VALID_SP = CONTEXT_VALID_R13,
CONTEXT_VALID_LR = CONTEXT_VALID_R14,
CONTEXT_VALID_PC = CONTEXT_VALID_R15
};
StackFrameARM() : context(), context_validity(CONTEXT_VALID_NONE) {}
// Return the ContextValidity flag for register rN.
static ContextValidity RegisterValidFlag(int n) {
return ContextValidity(1 << 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.
MDRawContextARM context;
// context_validity is actually ContextValidity, but int is used because
// the OR operator doesn't work well with enumerated types. This indicates
// which fields in context are valid.
// For each register in context whose value has been recovered, we set
// the corresponding CONTEXT_VALID_ bit in context_validity.
//
// context_validity's type should actually be ContextValidity, but
// we use int instead because the bitwise inclusive or operator
// yields an int when applied to enum values, and C++ doesn't
// silently convert from ints to enums.
int context_validity;
};

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -41,23 +41,23 @@
#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
#define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
#include <vector>
#include <set>
#include <string>
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
class CallStack;
class CodeModule;
class CodeModules;
template<typename T> class linked_ptr;
class MemoryRegion;
class MinidumpContext;
class SourceLineResolverInterface;
struct StackFrame;
struct StackFrameInfo;
class SymbolSupplier;
class SystemInfo;
using std::vector;
using std::set;
class Stackwalker {
@ -118,6 +118,10 @@ class Stackwalker {
// This field is optional and may be NULL.
const CodeModules *modules_;
protected:
// The SourceLineResolver implementation.
SourceLineResolverInterface *resolver_;
private:
// Obtains the context frame, the innermost called procedure in a stack
// trace. Returns NULL on failure. GetContextFrame allocates a new
@ -133,15 +137,15 @@ class Stackwalker {
// the end of the stack has been reached). GetCallerFrame allocates a new
// StackFrame (or StackFrame subclass), ownership of which is taken by
// the caller.
virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) = 0;
virtual StackFrame* GetCallerFrame(const CallStack *stack) = 0;
// The optional SymbolSupplier for resolving source line info.
SymbolSupplier *supplier_;
// The SourceLineResolver implementation
SourceLineResolverInterface *resolver_;
// A list of modules that we haven't found symbols for. We track
// this in order to avoid repeatedly looking them up again within
// one minidump.
set<std::string> no_symbol_modules_;
};

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -46,7 +46,8 @@
#include "google_breakpad/processor/stack_frame.h"
#include "processor/linked_ptr.h"
#include "processor/scoped_ptr.h"
#include "processor/stack_frame_info.h"
#include "processor/windows_frame_info.h"
#include "processor/cfi_frame_info.h"
using std::map;
using std::vector;
@ -114,28 +115,38 @@ class BasicSourceLineResolver::Module {
bool LoadMapFromBuffer(const string &map_buffer);
// Looks up the given relative address, and fills the StackFrame struct
// with the result. Additional debugging information, if available, is
// returned. If no additional information is available, returns NULL.
// A NULL return value is not an error. The caller takes ownership of
// any returned StackFrameInfo object.
StackFrameInfo* LookupAddress(StackFrame *frame) const;
// with the result.
void LookupAddress(StackFrame *frame) const;
// If Windows stack walking information is available covering ADDRESS,
// return a WindowsFrameInfo structure describing it. If the information
// is not available, returns NULL. A NULL return value does not indicate
// an error. The caller takes ownership of any returned WindowsFrameInfo
// object.
WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const;
// If CFI stack walking information is available covering ADDRESS,
// return a CFIFrameInfo structure describing it. If the information
// is not available, return NULL. The caller takes ownership of any
// returned CFIFrameInfo object.
CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const;
private:
friend class BasicSourceLineResolver;
typedef map<int, string> FileMap;
// The types for stack_info_. This is equivalent to MS DIA's
// The types for windows_frame_info_. This is equivalent to MS DIA's
// StackFrameTypeEnum. Each identifies a different type of frame
// information, although all are represented in the symbol file in the
// same format. These are used as indices to the stack_info_ array.
enum StackInfoTypes {
STACK_INFO_FPO = 0,
STACK_INFO_TRAP, // not used here
STACK_INFO_TSS, // not used here
STACK_INFO_STANDARD,
STACK_INFO_FRAME_DATA,
STACK_INFO_LAST, // must be the last sequentially-numbered item
STACK_INFO_UNKNOWN = -1
// same format. These are used as indices to the windows_frame_info_ array.
enum WindowsFrameInfoTypes {
WINDOWS_FRAME_INFO_FPO = 0,
WINDOWS_FRAME_INFO_TRAP, // not used here
WINDOWS_FRAME_INFO_TSS, // not used here
WINDOWS_FRAME_INFO_STANDARD,
WINDOWS_FRAME_INFO_FRAME_DATA,
WINDOWS_FRAME_INFO_LAST, // must be the last sequentially-numbered item
WINDOWS_FRAME_INFO_UNKNOWN = -1
};
// Splits line into at most max_tokens space-separated tokens, placing
@ -163,20 +174,49 @@ class BasicSourceLineResolver::Module {
// Returns false if an error occurs.
bool ParsePublicSymbol(char *public_line);
// Parses a stack frame info declaration, storing it in stack_info_.
// Parses a STACK WIN or STACK CFI frame info declaration, storing
// it in the appropriate table.
bool ParseStackInfo(char *stack_info_line);
// Parses a STACK WIN record, storing it in windows_frame_info_.
bool ParseWindowsFrameInfo(char *stack_info_line);
// Parses a STACK CFI record, storing it in cfi_frame_info_.
bool ParseCFIFrameInfo(char *stack_info_line);
// Parse RULE_SET, a series of rules of the sort appearing in STACK
// CFI records, and store the given rules in FRAME_INFO.
bool ParseCFIRuleSet(const string &rule_set, CFIFrameInfo *frame_info) const;
string name_;
FileMap files_;
RangeMap< MemAddr, linked_ptr<Function> > functions_;
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
// Each element in the array is a ContainedRangeMap for a type listed in
// StackInfoTypes. These are split by type because there may be overlaps
// between maps of different types, but some information is only available
// as certain types.
ContainedRangeMap< MemAddr, linked_ptr<StackFrameInfo> >
stack_info_[STACK_INFO_LAST];
// Each element in the array is a ContainedRangeMap for a type
// listed in WindowsFrameInfoTypes. These are split by type because
// there may be overlaps between maps of different types, but some
// information is only available as certain types.
ContainedRangeMap< MemAddr, linked_ptr<WindowsFrameInfo> >
windows_frame_info_[WINDOWS_FRAME_INFO_LAST];
// DWARF CFI stack walking data. The Module stores the initial rule sets
// and rule deltas as strings, just as they appear in the symbol file:
// although the file may contain hundreds of thousands of STACK CFI
// records, walking a stack will only ever use a few of them, so it's
// best to delay parsing a record until it's actually needed.
// STACK CFI INIT records: for each range, an initial set of register
// recovery rules. The RangeMap's itself gives the starting and ending
// addresses.
RangeMap<MemAddr, string> cfi_initial_rules_;
// STACK CFI records: at a given address, the changes to the register
// recovery rules that take effect at that address. The map key is the
// starting address; the ending address is the key of the next entry in
// this map, or the end of the range as given by the cfi_initial_rules_
// entry (which FindCFIFrameInfo looks up first).
map<MemAddr, string> cfi_delta_rules_;
};
BasicSourceLineResolver::BasicSourceLineResolver() : modules_(new ModuleMap) {
@ -236,12 +276,32 @@ bool BasicSourceLineResolver::HasModule(const string &module_name) const {
return modules_->find(module_name) != modules_->end();
}
StackFrameInfo* BasicSourceLineResolver::FillSourceLineInfo(
StackFrame *frame) const {
void BasicSourceLineResolver::FillSourceLineInfo(StackFrame *frame) const {
if (frame->module) {
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
if (it != modules_->end()) {
return it->second->LookupAddress(frame);
it->second->LookupAddress(frame);
}
}
}
WindowsFrameInfo *BasicSourceLineResolver::FindWindowsFrameInfo(
const StackFrame *frame) const {
if (frame->module) {
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
if (it != modules_->end()) {
return it->second->FindWindowsFrameInfo(frame);
}
}
return NULL;
}
CFIFrameInfo *BasicSourceLineResolver::FindCFIFrameInfo(
const StackFrame *frame) const {
if (frame->module) {
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
if (it != modules_->end()) {
return it->second->FindCFIFrameInfo(frame);
}
}
return NULL;
@ -380,7 +440,7 @@ bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) {
return false;
}
BPLOG(ERROR) << "Opening " << map_file;
BPLOG(INFO) << "Opening " << map_file;
FILE *f = fopen(map_file.c_str(), "rt");
if (!f) {
@ -413,42 +473,15 @@ bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) {
return LoadMapFromBuffer(map_buffer);
}
StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress(
StackFrame *frame) const {
void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
linked_ptr<StackFrameInfo> retrieved_info;
// Check for debugging info first, before any possible early returns.
//
// We only know about STACK_INFO_FRAME_DATA and STACK_INFO_FPO. Prefer
// them in this order. STACK_INFO_FRAME_DATA is the newer type that
// includes its own program string. STACK_INFO_FPO is the older type
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
if (!stack_info_[STACK_INFO_FRAME_DATA].RetrieveRange(address,
&retrieved_info)) {
stack_info_[STACK_INFO_FPO].RetrieveRange(address, &retrieved_info);
}
scoped_ptr<StackFrameInfo> frame_info;
if (retrieved_info.get()) {
frame_info.reset(new StackFrameInfo());
frame_info->CopyFrom(*retrieved_info.get());
}
// First, look for a matching FUNC range. Use RetrieveNearestRange instead
// of RetrieveRange so that the nearest function can be compared to the
// nearest PUBLIC symbol if the address does not lie within the function.
// Having access to the highest function below address, even when address
// is outside of the function, is useful: if the function is higher than
// the nearest PUBLIC symbol, then it means that the PUBLIC symbols is not
// valid for the address, and no function information should be filled in.
// Using RetrieveNearestRange instead of RetrieveRange means that we need
// to verify that address is within the range before using a FUNC.
//
// If no FUNC containing the address is found, look for the nearest PUBLIC
// symbol, being careful not to use a public symbol at a lower address than
// the nearest FUNC.
int parameter_size = 0;
// First, look for a FUNC record that covers address. Use
// RetrieveNearestRange instead of RetrieveRange so that, if there
// is no such function, we can use the next function to bound the
// extent of the PUBLIC symbol we find, below. This does mean we
// need to check that address indeed falls within the function we
// find; do the range comparison in an overflow-friendly way.
linked_ptr<Function> func;
linked_ptr<PublicSymbol> public_symbol;
MemAddr function_base;
@ -456,9 +489,7 @@ StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress(
MemAddr public_address;
if (functions_.RetrieveNearestRange(address, &func,
&function_base, &function_size) &&
address >= function_base && address < function_base + function_size) {
parameter_size = func->parameter_size;
address >= function_base && address - function_base < function_size) {
frame->function_name = func->name;
frame->function_base = frame->module->base_address() + function_base;
@ -474,27 +505,99 @@ StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress(
}
} else if (public_symbols_.Retrieve(address,
&public_symbol, &public_address) &&
(!func.get() || public_address > function_base + function_size)) {
parameter_size = public_symbol->parameter_size;
(!func.get() || public_address > function_base)) {
frame->function_name = public_symbol->name;
frame->function_base = frame->module->base_address() + public_address;
} else {
// No FUNC or PUBLIC data available.
return frame_info.release();
}
}
WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo(
const StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
scoped_ptr<WindowsFrameInfo> result(new WindowsFrameInfo());
// We only know about WINDOWS_FRAME_INFO_FRAME_DATA and
// WINDOWS_FRAME_INFO_FPO. Prefer them in this order.
// WINDOWS_FRAME_INFO_FRAME_DATA is the newer type that includes its
// own program string. WINDOWS_FRAME_INFO_FPO is the older type
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
linked_ptr<WindowsFrameInfo> frame_info;
if ((windows_frame_info_[WINDOWS_FRAME_INFO_FRAME_DATA]
.RetrieveRange(address, &frame_info))
|| (windows_frame_info_[WINDOWS_FRAME_INFO_FPO]
.RetrieveRange(address, &frame_info))) {
result->CopyFrom(*frame_info.get());
return result.release();
}
if (!frame_info.get()) {
// Even without a relevant STACK line, many functions contain information
// about how much space their parameters consume on the stack. Prefer
// the STACK stuff (above), but if it's not present, take the
// information from the FUNC or PUBLIC line.
frame_info.reset(new StackFrameInfo());
frame_info->parameter_size = parameter_size;
frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE;
// Even without a relevant STACK line, many functions contain
// information about how much space their parameters consume on the
// stack. Use RetrieveNearestRange instead of RetrieveRange, so that
// we can use the function to bound the extent of the PUBLIC symbol,
// below. However, this does mean we need to check that ADDRESS
// falls within the retrieved function's range; do the range
// comparison in an overflow-friendly way.
linked_ptr<Function> function;
MemAddr function_base, function_size;
if (functions_.RetrieveNearestRange(address, &function,
&function_base, &function_size) &&
address >= function_base && address - function_base < function_size) {
result->parameter_size = function->parameter_size;
result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE;
return result.release();
}
return frame_info.release();
// PUBLIC symbols might have a parameter size. Use the function we
// found above to limit the range the public symbol covers.
linked_ptr<PublicSymbol> public_symbol;
MemAddr public_address;
if (public_symbols_.Retrieve(address, &public_symbol, &public_address) &&
(!function.get() || public_address > function_base)) {
result->parameter_size = public_symbol->parameter_size;
}
return NULL;
}
CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo(
const StackFrame *frame) const {
MemAddr address = frame->instruction - frame->module->base_address();
MemAddr initial_base, initial_size;
string initial_rules;
// Find the initial rule whose range covers this address. That
// provides an initial set of register recovery rules. Then, walk
// forward from the initial rule's starting address to frame's
// instruction address, applying delta rules.
if (!cfi_initial_rules_.RetrieveRange(address, &initial_rules,
&initial_base, &initial_size)) {
return NULL;
}
// Create a frame info structure, and populate it with the rules from
// the STACK CFI INIT record.
scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
if (!ParseCFIRuleSet(initial_rules, rules.get()))
return NULL;
// Find the first delta rule that falls within the initial rule's range.
map<MemAddr, string>::const_iterator delta =
cfi_delta_rules_.lower_bound(initial_base);
// Apply delta rules up to and including the frame's address.
while (delta != cfi_delta_rules_.end() && delta->first <= address) {
ParseCFIRuleSet(delta->second, rules.get());
delta++;
}
return rules.release();
}
bool BasicSourceLineResolver::Module::ParseCFIRuleSet(
const string &rule_set, CFIFrameInfo *frame_info) const {
CFIFrameInfoParseHandler handler(frame_info);
CFIRuleParser parser(&handler);
return parser.Parse(rule_set);
}
// static
@ -616,49 +719,61 @@ bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
}
bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
// STACK WIN <type> <rva> <code_size> <prolog_size> <epliog_size>
// <parameter_size> <saved_register_size> <local_size> <max_stack_size>
// <has_program_string> <program_string_OR_allocates_base_pointer>
//
// If has_program_string is 1, the rest of the line is a program string.
// Otherwise, the final token tells whether the stack info indicates that
// a base pointer has been allocated.
//
// Expect has_program_string to be 1 when type is STACK_INFO_FRAME_DATA and
// 0 when type is STACK_INFO_FPO, but don't enforce this.
// Skip "STACK " prefix.
stack_info_line += 6;
// Find the token indicating what sort of stack frame walking
// information this is.
while (*stack_info_line == ' ')
stack_info_line++;
const char *platform = stack_info_line;
while (!strchr(" \r\n", *stack_info_line))
stack_info_line++;
*stack_info_line++ = '\0';
// MSVC stack frame info.
if (strcmp(platform, "WIN") == 0)
return ParseWindowsFrameInfo(stack_info_line);
// DWARF CFI stack frame info
else if (strcmp(platform, "CFI") == 0)
return ParseCFIFrameInfo(stack_info_line);
// Something unrecognized.
else
return false;
}
bool BasicSourceLineResolver::Module::ParseWindowsFrameInfo(
char *stack_info_line) {
// The format of a STACK WIN record is documented at:
//
// http://code.google.com/p/google-breakpad/wiki/SymbolFiles
vector<char*> tokens;
if (!Tokenize(stack_info_line, 12, &tokens))
if (!Tokenize(stack_info_line, 11, &tokens))
return false;
// Only MSVC stack frame info is understood for now.
const char *platform = tokens[0];
if (strcmp(platform, "WIN") != 0)
int type = strtol(tokens[0], NULL, 16);
if (type < 0 || type > WINDOWS_FRAME_INFO_LAST - 1)
return false;
int type = strtol(tokens[1], NULL, 16);
if (type < 0 || type > STACK_INFO_LAST - 1)
return false;
u_int64_t rva = strtoull(tokens[2], NULL, 16);
u_int64_t code_size = strtoull(tokens[3], NULL, 16);
u_int32_t prolog_size = strtoul(tokens[4], NULL, 16);
u_int32_t epilog_size = strtoul(tokens[5], NULL, 16);
u_int32_t parameter_size = strtoul(tokens[6], NULL, 16);
u_int32_t saved_register_size = strtoul(tokens[7], NULL, 16);
u_int32_t local_size = strtoul(tokens[8], NULL, 16);
u_int32_t max_stack_size = strtoul(tokens[9], NULL, 16);
int has_program_string = strtoul(tokens[10], NULL, 16);
u_int64_t rva = strtoull(tokens[1], NULL, 16);
u_int64_t code_size = strtoull(tokens[2], NULL, 16);
u_int32_t prolog_size = strtoul(tokens[3], NULL, 16);
u_int32_t epilog_size = strtoul(tokens[4], NULL, 16);
u_int32_t parameter_size = strtoul(tokens[5], NULL, 16);
u_int32_t saved_register_size = strtoul(tokens[6], NULL, 16);
u_int32_t local_size = strtoul(tokens[7], NULL, 16);
u_int32_t max_stack_size = strtoul(tokens[8], NULL, 16);
int has_program_string = strtoul(tokens[9], NULL, 16);
const char *program_string = "";
int allocates_base_pointer = 0;
if (has_program_string) {
program_string = tokens[11];
program_string = tokens[10];
} else {
allocates_base_pointer = strtoul(tokens[11], NULL, 16);
allocates_base_pointer = strtoul(tokens[10], NULL, 16);
}
// TODO(mmentovai): I wanted to use StoreRange's return value as this
@ -681,8 +796,8 @@ bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
// if ContainedRangeMap were modified to allow replacement of
// already-stored values.
linked_ptr<StackFrameInfo> stack_frame_info(
new StackFrameInfo(prolog_size,
linked_ptr<WindowsFrameInfo> stack_frame_info(
new WindowsFrameInfo(prolog_size,
epilog_size,
parameter_size,
saved_register_size,
@ -690,11 +805,46 @@ bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
max_stack_size,
allocates_base_pointer,
program_string));
stack_info_[type].StoreRange(rva, code_size, stack_frame_info);
windows_frame_info_[type].StoreRange(rva, code_size, stack_frame_info);
return true;
}
bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
char *stack_info_line) {
char *cursor;
// Is this an INIT record or a delta record?
char *init_or_address = strtok_r(stack_info_line, " \r\n", &cursor);
if (!init_or_address)
return false;
if (strcmp(init_or_address, "INIT") == 0) {
// This record has the form "STACK INIT <address> <size> <rules...>".
char *address_field = strtok_r(NULL, " \r\n", &cursor);
if (!address_field) return false;
char *size_field = strtok_r(NULL, " \r\n", &cursor);
if (!size_field) return false;
char *initial_rules = strtok_r(NULL, "\r\n", &cursor);
if (!initial_rules) return false;
MemAddr address = strtoul(address_field, NULL, 16);
MemAddr size = strtoul(size_field, NULL, 16);
cfi_initial_rules_.StoreRange(address, size, initial_rules);
return true;
}
// This record has the form "STACK <address> <rules...>".
char *address_field = init_or_address;
char *delta_rules = strtok_r(NULL, "\r\n", &cursor);
if (!delta_rules) return false;
MemAddr address = strtoul(address_field, NULL, 16);
cfi_delta_rules_[address] = delta_rules;
return true;
}
bool BasicSourceLineResolver::CompareString::operator()(
const string &s1, const string &s2) const {
return strcmp(s1.c_str(), s2.c_str()) < 0;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -32,10 +32,12 @@
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/code_module.h"
#include "google_breakpad/processor/stack_frame.h"
#include "google_breakpad/processor/memory_region.h"
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/stack_frame_info.h"
#include "processor/windows_frame_info.h"
#include "processor/cfi_frame_info.h"
#define ASSERT_TRUE(cond) \
if (!(cond)) { \
@ -51,11 +53,13 @@ namespace {
using std::string;
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CFIFrameInfo;
using google_breakpad::CodeModule;
using google_breakpad::MemoryRegion;
using google_breakpad::StackFrame;
using google_breakpad::WindowsFrameInfo;
using google_breakpad::linked_ptr;
using google_breakpad::scoped_ptr;
using google_breakpad::StackFrame;
using google_breakpad::StackFrameInfo;
class TestCodeModule : public CodeModule {
public:
@ -63,7 +67,7 @@ class TestCodeModule : public CodeModule {
virtual ~TestCodeModule() {}
virtual u_int64_t base_address() const { return 0; }
virtual u_int64_t size() const { return 0x4000; }
virtual u_int64_t size() const { return 0xb000; }
virtual string code_file() const { return code_file_; }
virtual string code_identifier() const { return ""; }
virtual string debug_file() const { return ""; }
@ -77,6 +81,70 @@ class TestCodeModule : public CodeModule {
string code_file_;
};
// A mock memory region object, for use by the STACK CFI tests.
class MockMemoryRegion: public MemoryRegion {
u_int64_t GetBase() const { return 0x10000; }
u_int32_t GetSize() const { return 0x01000; }
bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
*value = address & 0xff;
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
*value = address & 0xffff;
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
switch (address) {
case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
case 0x1000c: *value = 0x878f7524; break; // saved %esi
case 0x10010: *value = 0x6312f9a5; break; // saved %edi
case 0x10014: *value = 0x10038; break; // caller's %ebp
case 0x10018: *value = 0xf6438648; break; // return address
default: *value = 0xdeadbeef; break; // junk
}
return true;
}
bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
*value = address;
return true;
}
};
// Verify that, for every association in ACTUAL, EXPECTED has the same
// association. (That is, ACTUAL's associations should be a subset of
// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
// ".cfa".
static bool VerifyRegisters(
const char *file, int line,
const CFIFrameInfo::RegisterValueMap<u_int32_t> &expected,
const CFIFrameInfo::RegisterValueMap<u_int32_t> &actual) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator a;
a = actual.find(".cfa");
ASSERT_TRUE(a != actual.end());
a = actual.find(".ra");
ASSERT_TRUE(a != actual.end());
for (a = actual.begin(); a != actual.end(); a++) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator e =
expected.find(a->first);
if (e == expected.end()) {
fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
file, line, a->first.c_str(), a->second);
return false;
}
if (e->second != a->second) {
fprintf(stderr,
"%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
file, line, a->first.c_str(), a->second, e->second);
return false;
}
// Don't complain if this doesn't recover all registers. Although
// the DWARF spec says that unmentioned registers are undefined,
// GCC uses omission to mean that they are unchanged.
}
return true;
}
static bool VerifyEmpty(const StackFrame &frame) {
ASSERT_TRUE(frame.function_name.empty());
ASSERT_TRUE(frame.source_file_name.empty());
@ -104,9 +172,11 @@ static bool RunTests() {
TestCodeModule module1("module1");
StackFrame frame;
scoped_ptr<WindowsFrameInfo> windows_frame_info;
scoped_ptr<CFIFrameInfo> cfi_frame_info;
frame.instruction = 0x1000;
frame.module = NULL;
scoped_ptr<StackFrameInfo> frame_info(resolver.FillSourceLineInfo(&frame));
resolver.FillSourceLineInfo(&frame);
ASSERT_FALSE(frame.module);
ASSERT_TRUE(frame.function_name.empty());
ASSERT_EQ(frame.function_base, 0);
@ -115,7 +185,7 @@ static bool RunTests() {
ASSERT_EQ(frame.source_line_base, 0);
frame.module = &module1;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_1");
ASSERT_TRUE(frame.module);
ASSERT_EQ(frame.module->code_file(), "module1");
@ -123,45 +193,152 @@ static bool RunTests() {
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
ASSERT_EQ(frame.source_line, 44);
ASSERT_EQ(frame.source_line_base, 0x1000);
ASSERT_TRUE(frame_info.get());
ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_EQ(frame_info->program_string,
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_EQ(windows_frame_info->program_string,
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
ClearSourceLineInfo(&frame);
frame.instruction = 0x800;
frame.module = &module1;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
resolver.FillSourceLineInfo(&frame);
ASSERT_TRUE(VerifyEmpty(frame));
ASSERT_FALSE(frame_info.get());
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_FALSE(windows_frame_info.get());
frame.instruction = 0x1280;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_3");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
ASSERT_TRUE(frame_info.get());
ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_TRUE(frame_info->program_string.empty());
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_TRUE(windows_frame_info->program_string.empty());
frame.instruction = 0x1380;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function1_4");
ASSERT_TRUE(frame.source_file_name.empty());
ASSERT_EQ(frame.source_line, 0);
ASSERT_TRUE(frame_info.get());
ASSERT_FALSE(frame_info->allocates_base_pointer);
ASSERT_FALSE(frame_info->program_string.empty());
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
ASSERT_FALSE(windows_frame_info->program_string.empty());
frame.instruction = 0x2000;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
ASSERT_FALSE(frame_info.get());
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_FALSE(windows_frame_info.get());
// module1 has STACK CFI records covering 3d40..3def;
// module2 has STACK CFI records covering 3df0..3e9f;
// check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
frame.instruction = 0x3d3f;
frame.module = &module1;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_FALSE(cfi_frame_info.get());
frame.instruction = 0x3e9f;
frame.module = &module1;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_FALSE(cfi_frame_info.get());
CFIFrameInfo::RegisterValueMap<u_int32_t> current_registers;
CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
CFIFrameInfo::RegisterValueMap<u_int32_t> expected_caller_registers;
MockMemoryRegion memory;
// Regardless of which instruction evaluation takes place at, it
// should produce the same values for the caller's registers.
expected_caller_registers[".cfa"] = 0x1001c;
expected_caller_registers[".ra"] = 0xf6438648;
expected_caller_registers["$ebp"] = 0x10038;
expected_caller_registers["$ebx"] = 0x98ecadc3;
expected_caller_registers["$esi"] = 0x878f7524;
expected_caller_registers["$edi"] = 0x6312f9a5;
frame.instruction = 0x3d40;
frame.module = &module1;
current_registers.clear();
current_registers["$esp"] = 0x10018;
current_registers["$ebp"] = 0x10038;
current_registers["$ebx"] = 0x98ecadc3;
current_registers["$esi"] = 0x878f7524;
current_registers["$edi"] = 0x6312f9a5;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d41;
current_registers["$esp"] = 0x10014;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d43;
current_registers["$ebp"] = 0x10014;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d54;
current_registers["$ebx"] = 0x6864f054U;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d5a;
current_registers["$esi"] = 0x6285f79aU;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x3d84;
current_registers["$edi"] = 0x64061449U;
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
ASSERT_TRUE(cfi_frame_info.get());
ASSERT_TRUE(cfi_frame_info.get()
->FindCallerRegs<u_int32_t>(current_registers, memory,
&caller_registers));
VerifyRegisters(__FILE__, __LINE__,
expected_caller_registers, caller_registers);
frame.instruction = 0x2900;
frame.module = &module1;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
frame.instruction = 0x4000;
frame.module = &module1;
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, string("LargeFunction"));
TestCodeModule module2("module2");
frame.instruction = 0x2181;
frame.module = &module2;
frame_info.reset(resolver.FillSourceLineInfo(&frame));
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Function2_2");
ASSERT_EQ(frame.function_base, 0x2170);
ASSERT_TRUE(frame.module);
@ -169,14 +346,13 @@ static bool RunTests() {
ASSERT_EQ(frame.source_file_name, "file2_2.cc");
ASSERT_EQ(frame.source_line, 21);
ASSERT_EQ(frame.source_line_base, 0x2180);
ASSERT_TRUE(frame_info.get());
ASSERT_EQ(frame_info->prolog_size, 1);
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
ASSERT_TRUE(windows_frame_info.get());
ASSERT_EQ(windows_frame_info->prolog_size, 1);
frame.instruction = 0x216f;
StackFrameInfo *s;
s = resolver.FillSourceLineInfo(&frame);
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_1");
delete s;
ClearSourceLineInfo(&frame);
frame.instruction = 0x219f;
@ -186,11 +362,9 @@ static bool RunTests() {
frame.instruction = 0x21a0;
frame.module = &module2;
s = resolver.FillSourceLineInfo(&frame);
resolver.FillSourceLineInfo(&frame);
ASSERT_EQ(frame.function_name, "Public2_2");
delete s;
ASSERT_FALSE(resolver.LoadModule("module3",
testdata_dir + "/module3_bad.out"));
ASSERT_FALSE(resolver.HasModule("module3"));

Просмотреть файл

@ -0,0 +1,119 @@
// -*- mode: C++ -*-
// 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>
// cfi_frame_info-inl.h: Definitions for cfi_frame_info.h inlined functions.
#ifndef PROCESSOR_CFI_FRAME_INFO_INL_H_
#define PROCESSOR_CFI_FRAME_INFO_INL_H_
#include <string.h>
namespace google_breakpad {
template <typename RegisterType, class RawContextType>
bool SimpleCFIWalker<RegisterType, RawContextType>::FindCallerRegisters(
const MemoryRegion &memory,
const CFIFrameInfo &cfi_frame_info,
const RawContextType &callee_context,
int callee_validity,
RawContextType *caller_context,
int *caller_validity) const {
typedef CFIFrameInfo::RegisterValueMap<RegisterType> ValueMap;
ValueMap callee_registers;
ValueMap caller_registers;
// Just for brevity.
typename ValueMap::const_iterator caller_none = caller_registers.end();
// Populate callee_registers with register values from callee_context.
for (size_t i = 0; i < map_size_; i++) {
const RegisterSet &r = register_map_[i];
if (callee_validity & r.validity_flag)
callee_registers[r.name] = callee_context.*r.context_member;
}
// Apply the rules, and see what register values they yield.
if (!cfi_frame_info.FindCallerRegs<RegisterType>(callee_registers, memory,
&caller_registers))
return false;
// Populate *caller_context with the values the rules placed in
// caller_registers.
memset(caller_context, 0xda, sizeof(caller_context));
*caller_validity = 0;
for (size_t i = 0; i < map_size_; i++) {
const RegisterSet &r = register_map_[i];
typename ValueMap::const_iterator caller_entry;
// Did the rules provide a value for this register by its name?
caller_entry = caller_registers.find(r.name);
if (caller_entry != caller_none) {
caller_context->*r.context_member = caller_entry->second;
*caller_validity |= r.validity_flag;
continue;
}
// Did the rules provide a value for this register under its
// alternate name?
if (r.alternate_name) {
caller_entry = caller_registers.find(r.alternate_name);
if (caller_entry != caller_none) {
caller_context->*r.context_member = caller_entry->second;
*caller_validity |= r.validity_flag;
continue;
}
}
// Is this a callee-saves register? The walker assumes that these
// still hold the caller's value if the CFI doesn't mention them.
//
// Note that other frame walkers may fail to recover callee-saves
// registers; for example, the x86 "traditional" strategy only
// recovers %eip, %esp, and %ebp, even though %ebx, %esi, and %edi
// are callee-saves, too. It is not correct to blindly set the
// valid bit for all callee-saves registers, without first
// checking its validity bit in the callee.
if (r.callee_saves && (callee_validity & r.validity_flag) != 0) {
caller_context->*r.context_member = callee_context.*r.context_member;
*caller_validity |= r.validity_flag;
continue;
}
// Otherwise, the register's value is unknown.
}
return true;
}
} // namespace google_breakpad
#endif // PROCESSOR_CFI_FRAME_INFO_INL_H_

Просмотреть файл

@ -0,0 +1,157 @@
// 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>
// cfi_frame_info.cc: Implementation of CFIFrameInfo class.
// See cfi_frame_info.h for details.
#include <cstring>
#include "processor/cfi_frame_info.h"
#include "processor/postfix_evaluator-inl.h"
#include "processor/scoped_ptr.h"
namespace google_breakpad {
template<typename V>
bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap<V> &registers,
const MemoryRegion &memory,
RegisterValueMap<V> *caller_registers) const {
// If there are not rules for both .ra and .cfa in effect at this address,
// don't use this CFI data for stack walking.
if (cfa_rule_.empty() || ra_rule_.empty())
return false;
RegisterValueMap<V> working;
PostfixEvaluator<V> evaluator(&working, &memory);
caller_registers->clear();
// First, compute the CFA.
V cfa;
working = registers;
if (!evaluator.EvaluateForValue(cfa_rule_, &cfa))
return false;
// Then, compute the return address.
V ra;
working = registers;
working[".cfa"] = cfa;
if (!evaluator.EvaluateForValue(ra_rule_, &ra))
return false;
// Now, compute values for all the registers register_rules_ mentions.
for (RuleMap::const_iterator it = register_rules_.begin();
it != register_rules_.end(); it++) {
V value;
working = registers;
working[".cfa"] = cfa;
if (!evaluator.EvaluateForValue(it->second, &value))
return false;
(*caller_registers)[it->first] = value;
}
(*caller_registers)[".ra"] = ra;
(*caller_registers)[".cfa"] = cfa;
return true;
}
// Explicit instantiations for 32-bit and 64-bit architectures.
template bool CFIFrameInfo::FindCallerRegs<u_int32_t>(
const RegisterValueMap<u_int32_t> &registers,
const MemoryRegion &memory,
RegisterValueMap<u_int32_t> *caller_registers) const;
template bool CFIFrameInfo::FindCallerRegs<u_int64_t>(
const RegisterValueMap<u_int64_t> &registers,
const MemoryRegion &memory,
RegisterValueMap<u_int64_t> *caller_registers) const;
bool CFIRuleParser::Parse(const string &rule_set) {
size_t rule_set_len = rule_set.size();
scoped_array<char> working_copy(new char[rule_set_len + 1]);
memcpy(working_copy.get(), rule_set.data(), rule_set_len);
working_copy[rule_set_len] = '\0';
name_.clear();
expression_.clear();
char *cursor;
static const char token_breaks[] = " \t\r\n";
char *token = strtok_r(working_copy.get(), token_breaks, &cursor);
for (;;) {
// End of rule set?
if (!token) return Report();
// Register/pseudoregister name?
size_t token_len = strlen(token);
if (token_len >= 1 && token[token_len - 1] == ':') {
// Names can't be empty.
if (token_len < 2) return false;
// If there is any pending content, report it.
if (!name_.empty() || !expression_.empty()) {
if (!Report()) return false;
}
name_.assign(token, token_len - 1);
expression_.clear();
} else {
// Another expression component.
assert(token_len > 0); // strtok_r guarantees this, I think.
if (!expression_.empty())
expression_ += ' ';
expression_ += token;
}
token = strtok_r(NULL, token_breaks, &cursor);
}
}
bool CFIRuleParser::Report() {
if (name_.empty() || expression_.empty()) return false;
if (name_ == ".cfa") handler_->CFARule(expression_);
else if (name_ == ".ra") handler_->RARule(expression_);
else handler_->RegisterRule(name_, expression_);
return true;
}
void CFIFrameInfoParseHandler::CFARule(const string &expression) {
frame_info_->SetCFARule(expression);
}
void CFIFrameInfoParseHandler::RARule(const string &expression) {
frame_info_->SetRARule(expression);
}
void CFIFrameInfoParseHandler::RegisterRule(const string &name,
const string &expression) {
frame_info_->SetRegisterRule(name, expression);
}
} // namespace google_breakpad

Просмотреть файл

@ -0,0 +1,271 @@
// -*- mode: C++ -*-
// 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>
// cfi_frame_info.h: Define the CFIFrameInfo class, which holds the
// set of 'STACK CFI'-derived register recovery rules that apply at a
// given instruction.
#ifndef PROCESSOR_CFI_FRAME_INFO_H_
#define PROCESSOR_CFI_FRAME_INFO_H_
#include <map>
#include <string>
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
using std::map;
using std::string;
class MemoryRegion;
// A set of rules for recovering the calling frame's registers'
// values, when the PC is at a given address in the current frame's
// function. See the description of 'STACK CFI' records at:
//
// http://code.google.com/p/google-breakpad/wiki/SymbolFiles
//
// To prepare an instance of CFIFrameInfo for use at a given
// instruction, first populate it with the rules from the 'STACK CFI
// INIT' record that covers that instruction, and then apply the
// changes given by the 'STACK CFI' records up to our instruction's
// address. Then, use the FindCallerRegs member function to apply the
// rules to the callee frame's register values, yielding the caller
// frame's register values.
class CFIFrameInfo {
public:
// A map from register names onto values.
template<typename ValueType> class RegisterValueMap:
public map<string, ValueType> { };
// Set the expression for computing a call frame address, return
// address, or register's value. At least the CFA rule and the RA
// rule must be set before calling FindCallerRegs.
void SetCFARule(const string &expression) { cfa_rule_ = expression; }
void SetRARule(const string &expression) { ra_rule_ = expression; }
void SetRegisterRule(const string &register_name, const string &expression) {
register_rules_[register_name] = expression;
}
// Compute the values of the calling frame's registers, according to
// this rule set. Use ValueType in expression evaluation; this
// should be u_int32_t on machines with 32-bit addresses, or
// u_int64_t on machines with 64-bit addresses.
//
// Return true on success, false otherwise.
//
// MEMORY provides access to the contents of the stack. REGISTERS is
// a dictionary mapping the names of registers whose values are
// known in the current frame to their values. CALLER_REGISTERS is
// populated with the values of the recoverable registers in the
// frame that called the current frame.
//
// In addition, CALLER_REGISTERS[".ra"] will be the return address,
// and CALLER_REGISTERS[".cfa"] will be the call frame address.
// These may be helpful in computing the caller's PC and stack
// pointer, if their values are not explicitly specified.
template<typename ValueType>
bool FindCallerRegs(const RegisterValueMap<ValueType> &registers,
const MemoryRegion &memory,
RegisterValueMap<ValueType> *caller_registers) const;
private:
// A map from register names onto evaluation rules.
typedef map<string, string> RuleMap;
// In this type, a "postfix expression" is an expression of the sort
// interpreted by google_breakpad::PostfixEvaluator.
// A postfix expression for computing the current frame's CFA (call
// frame address). The CFA is a reference address for the frame that
// remains unchanged throughout the frame's lifetime. You should
// evaluate this expression with a dictionary initially populated
// with the values of the current frame's known registers.
string cfa_rule_;
// The following expressions should be evaluated with a dictionary
// initially populated with the values of the current frame's known
// registers, and with ".cfa" set to the result of evaluating the
// cfa_rule expression, above.
// A postfix expression for computing the current frame's return
// address.
string ra_rule_;
// For a register named REG, rules[REG] is a postfix expression
// which leaves the value of REG in the calling frame on the top of
// the stack. You should evaluate this expression
RuleMap register_rules_;
};
// A parser for STACK CFI-style rule sets.
// This may seem bureaucratic: there's no legitimate run-time reason
// to use a parser/handler pattern for this, as it's not a likely
// reuse boundary. But doing so makes finer-grained unit testing
// possible.
class CFIRuleParser {
public:
class Handler {
public:
Handler() { }
virtual ~Handler() { }
// The input specifies EXPRESSION as the CFA/RA computation rule.
virtual void CFARule(const string &expression) = 0;
virtual void RARule(const string &expression) = 0;
// The input specifies EXPRESSION as the recovery rule for register NAME.
virtual void RegisterRule(const string &name, const string &expression) = 0;
};
// Construct a parser which feeds its results to HANDLER.
CFIRuleParser(Handler *handler) : handler_(handler) { }
// Parse RULE_SET as a set of CFA computation and RA/register
// recovery rules, as appearing in STACK CFI records. Report the
// results of parsing by making the appropriate calls to handler_.
// Return true if parsing was successful, false otherwise.
bool Parse(const string &rule_set);
private:
// Report any accumulated rule to handler_
bool Report();
// The handler to which the parser reports its findings.
Handler *handler_;
// Working data.
string name_, expression_;
};
// A handler for rule set parsing that populates a CFIFrameInfo with
// the results.
class CFIFrameInfoParseHandler: public CFIRuleParser::Handler {
public:
// Populate FRAME_INFO with the results of parsing.
CFIFrameInfoParseHandler(CFIFrameInfo *frame_info)
: frame_info_(frame_info) { }
void CFARule(const string &expression);
void RARule(const string &expression);
void RegisterRule(const string &name, const string &expression);
private:
CFIFrameInfo *frame_info_;
};
// A utility class template for simple 'STACK CFI'-driven stack walkers.
// Given a CFIFrameInfo instance, a table describing the architecture's
// register set, and a context holding the last frame's registers, an
// instance of this class can populate a new context with the caller's
// registers.
//
// This class template doesn't use any internal knowledge of CFIFrameInfo
// or the other stack walking structures; it just uses the public interface
// of CFIFrameInfo to do the usual things. But the logic it handles should
// be common to many different architectures' stack walkers, so wrapping it
// up in a class should allow the walkers to share code.
//
// RegisterType should be the type of this architecture's registers, either
// u_int32_t or u_int64_t. RawContextType should be the raw context
// structure type for this architecture.
template <typename RegisterType, class RawContextType>
class SimpleCFIWalker {
public:
// A structure describing one architecture register.
struct RegisterSet {
// The register name, as it appears in STACK CFI rules.
const char *name;
// An alternate name that the register's value might be found
// under in a register value dictionary, or NULL. When generating
// names, prefer NAME to this value. It's common to list ".cfa" as
// an alternative name for the stack pointer, and ".ra" as an
// alternative name for the instruction pointer.
const char *alternate_name;
// True if the callee is expected to preserve the value of this
// register. If this flag is true for some register R, and the STACK
// CFI records provide no rule to recover R, then SimpleCFIWalker
// assumes that the callee has not changed R's value, and the caller's
// value for R is that currently in the callee's context.
bool callee_saves;
// The ContextValidity flag representing the register's presence.
int validity_flag;
// A pointer to the RawContextType member that holds the
// register's value.
RegisterType RawContextType::*context_member;
};
// Create a simple CFI-based frame walker, given a description of the
// architecture's register set. REGISTER_MAP is an array of
// RegisterSet structures; MAP_SIZE is the number of elements in the
// array.
SimpleCFIWalker(const RegisterSet *register_map, size_t map_size)
: register_map_(register_map), map_size_(map_size) { }
// Compute the calling frame's raw context given the callee's raw
// context.
//
// Given:
//
// - MEMORY, holding the stack's contents,
// - CFI_FRAME_INFO, describing the called function,
// - CALLEE_CONTEXT, holding the called frame's registers, and
// - CALLEE_VALIDITY, indicating which registers in CALLEE_CONTEXT are valid,
//
// fill in CALLER_CONTEXT with the caller's register values, and set
// CALLER_VALIDITY to indicate which registers are valid in
// CALLER_CONTEXT. Return true on success, or false on failure.
bool FindCallerRegisters(const MemoryRegion &memory,
const CFIFrameInfo &cfi_frame_info,
const RawContextType &callee_context,
int callee_validity,
RawContextType *caller_context,
int *caller_validity) const;
private:
const RegisterSet *register_map_;
size_t map_size_;
};
} // namespace google_breakpad
#include "cfi_frame_info-inl.h"
#endif // PROCESSOR_CFI_FRAME_INFO_H_

Просмотреть файл

@ -0,0 +1,531 @@
// 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>
// cfi_frame_info_unittest.cc: Unit tests for CFIFrameInfo,
// CFIRuleParser, CFIFrameInfoParseHandler, and SimpleCFIWalker.
#include <string.h>
#include "breakpad_googletest_includes.h"
#include "processor/cfi_frame_info.h"
#include "google_breakpad/processor/memory_region.h"
using google_breakpad::CFIFrameInfo;
using google_breakpad::CFIFrameInfoParseHandler;
using google_breakpad::CFIRuleParser;
using google_breakpad::MemoryRegion;
using google_breakpad::SimpleCFIWalker;
using std::string;
using testing::_;
using testing::A;
using testing::AtMost;
using testing::DoAll;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class MockMemoryRegion: public MemoryRegion {
public:
MOCK_CONST_METHOD0(GetBase, u_int64_t());
MOCK_CONST_METHOD0(GetSize, u_int32_t());
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int8_t *));
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int16_t *));
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int32_t *));
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int64_t *));
};
// Handy definitions for all tests.
struct CFIFixture {
// Set up the mock memory object to expect no references.
void ExpectNoMemoryReferences() {
EXPECT_CALL(memory, GetBase()).Times(0);
EXPECT_CALL(memory, GetSize()).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int8_t *>())).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int16_t *>())).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int32_t *>())).Times(0);
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int64_t *>())).Times(0);
}
CFIFrameInfo cfi;
MockMemoryRegion memory;
CFIFrameInfo::RegisterValueMap<u_int64_t> registers, caller_registers;
};
class Simple: public CFIFixture, public Test { };
// FindCallerRegs should fail if no .cfa rule is provided.
TEST_F(Simple, NoCFA) {
ExpectNoMemoryReferences();
cfi.SetRARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// FindCallerRegs should fail if no .ra rule is provided.
TEST_F(Simple, NoRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
TEST_F(Simple, SetCFAAndRARule) {
ExpectNoMemoryReferences();
cfi.SetCFARule("330903416631436410");
cfi.SetRARule("5870666104170902211");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(330903416631436410ULL, caller_registers[".cfa"]);
ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
}
TEST_F(Simple, SetManyRules) {
ExpectNoMemoryReferences();
cfi.SetCFARule("$temp1 68737028 = $temp2 61072337 = $temp1 $temp2 -");
cfi.SetRARule(".cfa 99804755 +");
cfi.SetRegisterRule("register1", ".cfa 54370437 *");
cfi.SetRegisterRule("vodkathumbscrewingly", "24076308 .cfa +");
cfi.SetRegisterRule("pubvexingfjordschmaltzy", ".cfa 29801007 -");
cfi.SetRegisterRule("uncopyrightables", "92642917 .cfa /");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(6U, caller_registers.size());
ASSERT_EQ(7664691U, caller_registers[".cfa"]);
ASSERT_EQ(107469446U, caller_registers[".ra"]);
ASSERT_EQ(416732599139967ULL, caller_registers["register1"]);
ASSERT_EQ(31740999U, caller_registers["vodkathumbscrewingly"]);
ASSERT_EQ(-22136316ULL, caller_registers["pubvexingfjordschmaltzy"]);
ASSERT_EQ(12U, caller_registers["uncopyrightables"]);
}
TEST_F(Simple, RulesOverride) {
ExpectNoMemoryReferences();
cfi.SetCFARule("330903416631436410");
cfi.SetRARule("5870666104170902211");
cfi.SetCFARule("2828089117179001");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(2828089117179001ULL, caller_registers[".cfa"]);
ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
}
class Scope: public CFIFixture, public Test { };
// There should be no value for .cfa in scope when evaluating the CFA rule.
TEST_F(Scope, CFALacksCFA) {
ExpectNoMemoryReferences();
cfi.SetCFARule(".cfa");
cfi.SetRARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// There should be no value for .ra in scope when evaluating the CFA rule.
TEST_F(Scope, CFALacksRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule(".ra");
cfi.SetRARule("0");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// The current frame's registers should be in scope when evaluating
// the CFA rule.
TEST_F(Scope, CFASeesCurrentRegs) {
ExpectNoMemoryReferences();
registers[".baraminology"] = 0x06a7bc63e4f13893ULL;
registers[".ornithorhynchus"] = 0x5e0bf850bafce9d2ULL;
cfi.SetCFARule(".baraminology .ornithorhynchus +");
cfi.SetRARule("0");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(0x06a7bc63e4f13893ULL + 0x5e0bf850bafce9d2ULL,
caller_registers[".cfa"]);
}
// .cfa should be in scope in the return address expression.
TEST_F(Scope, RASeesCFA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("48364076");
cfi.SetRARule(".cfa");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(48364076U, caller_registers[".ra"]);
}
// There should be no value for .ra in scope when evaluating the CFA rule.
TEST_F(Scope, RALacksRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("0");
cfi.SetRARule(".ra");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// The current frame's registers should be in scope in the return
// address expression.
TEST_F(Scope, RASeesCurrentRegs) {
ExpectNoMemoryReferences();
registers["noachian"] = 0x54dc4a5d8e5eb503ULL;
cfi.SetCFARule("10359370");
cfi.SetRARule("noachian");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(2U, caller_registers.size());
ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[".ra"]);
}
// .cfa should be in scope for register rules.
TEST_F(Scope, RegistersSeeCFA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("6515179");
cfi.SetRARule(".cfa");
cfi.SetRegisterRule("rogerian", ".cfa");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(3U, caller_registers.size());
ASSERT_EQ(6515179U, caller_registers["rogerian"]);
}
// The return address should not be in scope for register rules.
TEST_F(Scope, RegsLackRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("42740329");
cfi.SetRARule("27045204");
cfi.SetRegisterRule("$r1", ".ra");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
// Register rules can see the current frame's register values.
TEST_F(Scope, RegsSeeRegs) {
ExpectNoMemoryReferences();
registers["$r1"] = 0x6ed3582c4bedb9adULL;
registers["$r2"] = 0xd27d9e742b8df6d0ULL;
cfi.SetCFARule("88239303");
cfi.SetRARule("30503835");
cfi.SetRegisterRule("$r1", "$r1 42175211 = $r2");
cfi.SetRegisterRule("$r2", "$r2 21357221 = $r1");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(4U, caller_registers.size());
ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers["$r1"]);
ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers["$r2"]);
}
// Each rule's temporaries are separate.
TEST_F(Scope, SeparateTempsRA) {
ExpectNoMemoryReferences();
cfi.SetCFARule("$temp1 76569129 = $temp1");
cfi.SetRARule("0");
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
cfi.SetCFARule("$temp1 76569129 = $temp1");
cfi.SetRARule("$temp1");
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
}
class MockCFIRuleParserHandler: public CFIRuleParser::Handler {
public:
MOCK_METHOD1(CFARule, void(const string &));
MOCK_METHOD1(RARule, void(const string &));
MOCK_METHOD2(RegisterRule, void(const string &, const string &));
};
// A fixture class for testing CFIRuleParser.
class CFIParserFixture {
public:
CFIParserFixture() : parser(&mock_handler) {
// Expect no parsing results to be reported to mock_handler. Individual
// tests can override this.
EXPECT_CALL(mock_handler, CFARule(_)).Times(0);
EXPECT_CALL(mock_handler, RARule(_)).Times(0);
EXPECT_CALL(mock_handler, RegisterRule(_, _)).Times(0);
}
MockCFIRuleParserHandler mock_handler;
CFIRuleParser parser;
};
class Parser: public CFIParserFixture, public Test { };
TEST_F(Parser, Empty) {
EXPECT_FALSE(parser.Parse(""));
}
TEST_F(Parser, LoneColon) {
EXPECT_FALSE(parser.Parse(":"));
}
TEST_F(Parser, CFANoExpr) {
EXPECT_FALSE(parser.Parse(".cfa:"));
}
TEST_F(Parser, CFANoColonNoExpr) {
EXPECT_FALSE(parser.Parse(".cfa"));
}
TEST_F(Parser, RANoExpr) {
EXPECT_FALSE(parser.Parse(".ra:"));
}
TEST_F(Parser, RANoColonNoExpr) {
EXPECT_FALSE(parser.Parse(".ra"));
}
TEST_F(Parser, RegNoExpr) {
EXPECT_FALSE(parser.Parse("reg:"));
}
TEST_F(Parser, NoName) {
EXPECT_FALSE(parser.Parse("expr"));
}
TEST_F(Parser, NoNameTwo) {
EXPECT_FALSE(parser.Parse("expr1 expr2"));
}
TEST_F(Parser, StartsWithExpr) {
EXPECT_FALSE(parser.Parse("expr1 reg: expr2"));
}
TEST_F(Parser, CFA) {
EXPECT_CALL(mock_handler, CFARule("spleen")).WillOnce(Return());
EXPECT_TRUE(parser.Parse(".cfa: spleen"));
}
TEST_F(Parser, RA) {
EXPECT_CALL(mock_handler, RARule("notoriety")).WillOnce(Return());
EXPECT_TRUE(parser.Parse(".ra: notoriety"));
}
TEST_F(Parser, Reg) {
EXPECT_CALL(mock_handler, RegisterRule("nemo", "mellifluous"))
.WillOnce(Return());
EXPECT_TRUE(parser.Parse("nemo: mellifluous"));
}
TEST_F(Parser, CFARARegs) {
EXPECT_CALL(mock_handler, CFARule("cfa expression")).WillOnce(Return());
EXPECT_CALL(mock_handler, RARule("ra expression")).WillOnce(Return());
EXPECT_CALL(mock_handler, RegisterRule("galba", "praetorian"))
.WillOnce(Return());
EXPECT_CALL(mock_handler, RegisterRule("otho", "vitellius"))
.WillOnce(Return());
EXPECT_TRUE(parser.Parse(".cfa: cfa expression .ra: ra expression "
"galba: praetorian otho: vitellius"));
}
TEST_F(Parser, Whitespace) {
EXPECT_CALL(mock_handler, RegisterRule("r1", "r1 expression"))
.WillOnce(Return());
EXPECT_CALL(mock_handler, RegisterRule("r2", "r2 expression"))
.WillOnce(Return());
EXPECT_TRUE(parser.Parse(" r1:\tr1\nexpression \tr2:\t\rr2\r\n "
"expression \n"));
}
TEST_F(Parser, WhitespaceLoneColon) {
EXPECT_FALSE(parser.Parse(" \n:\t "));
}
TEST_F(Parser, EmptyName) {
EXPECT_CALL(mock_handler, RegisterRule("reg", _))
.Times(AtMost(1))
.WillRepeatedly(Return());
EXPECT_FALSE(parser.Parse("reg: expr1 : expr2"));
}
TEST_F(Parser, RuleLoneColon) {
EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
.Times(AtMost(1))
.WillRepeatedly(Return());
EXPECT_FALSE(parser.Parse(" r1: expr :"));
}
TEST_F(Parser, RegNoExprRule) {
EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
.Times(AtMost(1))
.WillRepeatedly(Return());
EXPECT_FALSE(parser.Parse("r0: r1: expr"));
}
class ParseHandlerFixture: public CFIFixture {
public:
ParseHandlerFixture() : CFIFixture(), handler(&cfi) { }
CFIFrameInfoParseHandler handler;
};
class ParseHandler: public ParseHandlerFixture, public Test { };
TEST_F(ParseHandler, CFARARule) {
handler.CFARule("reg-for-cfa");
handler.RARule("reg-for-ra");
registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
}
TEST_F(ParseHandler, RegisterRules) {
handler.CFARule("reg-for-cfa");
handler.RARule("reg-for-ra");
handler.RegisterRule("reg1", "reg-for-reg1");
handler.RegisterRule("reg2", "reg-for-reg2");
registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL;
registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL;
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
&caller_registers));
ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers["reg1"]);
ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers["reg2"]);
}
struct SimpleCFIWalkerFixture {
struct RawContext {
u_int64_t r0, r1, r2, r3, r4, sp, pc;
};
enum Validity {
R0_VALID = 0x01,
R1_VALID = 0x02,
R2_VALID = 0x04,
R3_VALID = 0x08,
R4_VALID = 0x10,
SP_VALID = 0x20,
PC_VALID = 0x40
};
typedef SimpleCFIWalker<u_int64_t, RawContext> CFIWalker;
SimpleCFIWalkerFixture()
: walker(register_map,
sizeof(register_map) / sizeof(register_map[0])) { }
static CFIWalker::RegisterSet register_map[7];
CFIFrameInfo call_frame_info;
CFIWalker walker;
MockMemoryRegion memory;
RawContext callee_context, caller_context;
};
SimpleCFIWalkerFixture::CFIWalker::RegisterSet
SimpleCFIWalkerFixture::register_map[7] = {
{ "r0", NULL, true, R0_VALID, &RawContext::r0 },
{ "r1", NULL, true, R1_VALID, &RawContext::r1 },
{ "r2", NULL, false, R2_VALID, &RawContext::r2 },
{ "r3", NULL, false, R3_VALID, &RawContext::r3 },
{ "r4", NULL, true, R4_VALID, &RawContext::r4 },
{ "sp", ".cfa", true, SP_VALID, &RawContext::sp },
{ "pc", ".ra", true, PC_VALID, &RawContext::pc },
};
class SimpleWalker: public SimpleCFIWalkerFixture, public Test { };
TEST_F(SimpleWalker, Walk) {
// Stack_top is the current stack pointer, pointing to the lowest
// address of a frame that looks like this (all 64-bit words):
//
// sp -> saved r0
// garbage
// return address
// cfa ->
//
// r0 has been saved on the stack.
// r1 has been saved in r2.
// r2 and r3 are not recoverable.
// r4 is not recoverable, even though it is a callee-saves register.
// Some earlier frame's unwinder must have failed to recover it.
u_int64_t stack_top = 0x83254944b20d5512ULL;
// Saved r0.
EXPECT_CALL(memory,
GetMemoryAtAddress(stack_top, A<u_int64_t *>()))
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0xdc1975eba8602302ULL),
Return(true)));
// Saved return address.
EXPECT_CALL(memory,
GetMemoryAtAddress(stack_top + 16, A<u_int64_t *>()))
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0xba5ad6d9acce28deULL),
Return(true)));
call_frame_info.SetCFARule("sp 24 +");
call_frame_info.SetRARule(".cfa 8 - ^");
call_frame_info.SetRegisterRule("r0", ".cfa 24 - ^");
call_frame_info.SetRegisterRule("r1", "r2");
callee_context.r0 = 0x94e030ca79edd119ULL;
callee_context.r1 = 0x937b4d7e95ce52d9ULL;
callee_context.r2 = 0x5fe0027416b8b62aULL; // caller's r1
// callee_context.r3 is not valid in callee.
// callee_context.r4 is not valid in callee.
callee_context.sp = stack_top;
callee_context.pc = 0x25b21b224311d280ULL;
int callee_validity = R0_VALID | R1_VALID | R2_VALID | SP_VALID | PC_VALID;
memset(&caller_context, 0, sizeof(caller_context));
int caller_validity;
EXPECT_TRUE(walker.FindCallerRegisters(memory, call_frame_info,
callee_context, callee_validity,
&caller_context, &caller_validity));
EXPECT_EQ(R0_VALID | R1_VALID | SP_VALID | PC_VALID, caller_validity);
EXPECT_EQ(0xdc1975eba8602302ULL, caller_context.r0);
EXPECT_EQ(0x5fe0027416b8b62aULL, caller_context.r1);
EXPECT_EQ(stack_top + 24, caller_context.sp);
EXPECT_EQ(0xba5ad6d9acce28deULL, caller_context.pc);
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -1063,7 +1063,7 @@ void MinidumpContext::Print() {
for (unsigned int fpe_index = 0;
fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT;
++fpe_index) {
printf(" float_save.extra[%2d] = 0x%" PRIx64 "\n",
printf(" float_save.extra[%2d] = 0x%" PRIx32 "\n",
fpe_index, context_arm->float_save.extra[fpe_index]);
}
@ -1106,7 +1106,7 @@ void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) {
}
const u_int8_t* MinidumpMemoryRegion::GetMemory() {
const u_int8_t* MinidumpMemoryRegion::GetMemory() const {
if (!valid_) {
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory";
return NULL;
@ -1145,7 +1145,7 @@ const u_int8_t* MinidumpMemoryRegion::GetMemory() {
}
u_int64_t MinidumpMemoryRegion::GetBase() {
u_int64_t MinidumpMemoryRegion::GetBase() const {
if (!valid_) {
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase";
return static_cast<u_int64_t>(-1);
@ -1155,7 +1155,7 @@ u_int64_t MinidumpMemoryRegion::GetBase() {
}
u_int32_t MinidumpMemoryRegion::GetSize() {
u_int32_t MinidumpMemoryRegion::GetSize() const {
if (!valid_) {
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize";
return 0;
@ -1173,7 +1173,7 @@ void MinidumpMemoryRegion::FreeMemory() {
template<typename T>
bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(u_int64_t address,
T* value) {
T* value) const {
BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal "
"requires |value|";
assert(value);
@ -1215,25 +1215,25 @@ bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(u_int64_t address,
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
u_int8_t* value) {
u_int8_t* value) const {
return GetMemoryAtAddressInternal(address, value);
}
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
u_int16_t* value) {
u_int16_t* value) const {
return GetMemoryAtAddressInternal(address, value);
}
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
u_int32_t* value) {
u_int32_t* value) const {
return GetMemoryAtAddressInternal(address, value);
}
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
u_int64_t* value) {
u_int64_t* value) const {
return GetMemoryAtAddressInternal(address, value);
}
@ -3747,8 +3747,8 @@ bool Minidump::ReadBytes(void* bytes, size_t count) {
}
stream_->read(static_cast<char*>(bytes), count);
size_t bytes_read = stream_->gcount();
if (static_cast<size_t>(bytes_read) != count) {
if (bytes_read == -1) {
if (bytes_read != count) {
if (bytes_read == size_t(-1)) {
string error_string;
int error_code = ErrnoString(&error_string);
BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string;

Просмотреть файл

@ -34,6 +34,7 @@
#include <string>
#include <iostream>
#include <fstream>
#include <map>
#include "breakpad_googletest_includes.h"
#include "google_breakpad/processor/basic_source_line_resolver.h"
#include "google_breakpad/processor/call_stack.h"
@ -47,6 +48,8 @@
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
using std::map;
namespace google_breakpad {
class MockMinidump : public Minidump {
public:
@ -74,6 +77,10 @@ using google_breakpad::scoped_ptr;
using google_breakpad::SymbolSupplier;
using google_breakpad::SystemInfo;
using std::string;
using ::testing::_;
using ::testing::Mock;
using ::testing::Ne;
using ::testing::Property;
using ::testing::Return;
static const char *kSystemInfoOS = "Windows NT";
@ -155,6 +162,19 @@ SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
return s;
}
// A mock symbol supplier that always returns NOT_FOUND; one current
// use for testing the processor's caching of symbol lookups.
class MockSymbolSupplier : public SymbolSupplier {
public:
MockSymbolSupplier() { }
MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule*,
const SystemInfo*,
string*));
MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule*,
const SystemInfo*,
string*,
string*));
};
class MinidumpProcessorTest : public ::testing::Test {
@ -186,6 +206,43 @@ TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) {
google_breakpad::PROCESS_ERROR_NO_THREAD_LIST);
}
// This test case verifies that the symbol supplier is only consulted
// once per minidump per module.
TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) {
MockSymbolSupplier supplier;
BasicSourceLineResolver resolver;
MinidumpProcessor processor(&supplier, &resolver);
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
"/src/processor/testdata/minidump2.dmp";
ProcessState state;
EXPECT_CALL(supplier, GetSymbolFile(
Property(&google_breakpad::CodeModule::code_file,
"c:\\test_app.exe"),
_, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
EXPECT_CALL(supplier, GetSymbolFile(
Property(&google_breakpad::CodeModule::code_file,
Ne("c:\\test_app.exe")),
_, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&supplier));
// We need to verify that across minidumps, the processor will refetch
// symbol files, even with the same symbol supplier.
EXPECT_CALL(supplier, GetSymbolFile(
Property(&google_breakpad::CodeModule::code_file,
"c:\\test_app.exe"),
_, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
EXPECT_CALL(supplier, GetSymbolFile(
Property(&google_breakpad::CodeModule::code_file,
Ne("c:\\test_app.exe")),
_, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
ASSERT_EQ(processor.Process(minidump_file, &state),
google_breakpad::PROCESS_OK);
}
TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
TestSymbolSupplier supplier;
BasicSourceLineResolver resolver;
@ -204,16 +261,16 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
ASSERT_EQ(state.system_info()->cpu_info, kSystemInfoCPUInfo);
ASSERT_TRUE(state.crashed());
ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
ASSERT_EQ(state.crash_address(), 0x45);
ASSERT_EQ(state.threads()->size(), 1);
ASSERT_EQ(state.crash_address(), 0x45U);
ASSERT_EQ(state.threads()->size(), size_t(1));
ASSERT_EQ(state.requesting_thread(), 0);
CallStack *stack = state.threads()->at(0);
ASSERT_TRUE(stack);
ASSERT_EQ(stack->frames()->size(), 4);
ASSERT_EQ(stack->frames()->size(), 4U);
ASSERT_TRUE(stack->frames()->at(0)->module);
ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000);
ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000U);
ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(0)->function_name,
"`anonymous namespace'::CrashFunction");
@ -221,7 +278,7 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
ASSERT_EQ(stack->frames()->at(0)->source_line, 58);
ASSERT_TRUE(stack->frames()->at(1)->module);
ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000);
ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000U);
ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
@ -229,7 +286,7 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
// This comes from the CRT
ASSERT_TRUE(stack->frames()->at(2)->module);
ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000);
ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000U);
ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "c:\\test_app.exe");
ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup");
ASSERT_EQ(stack->frames()->at(2)->source_file_name,
@ -238,14 +295,14 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
// No debug info available for kernel32.dll
ASSERT_TRUE(stack->frames()->at(3)->module);
ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000);
ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000U);
ASSERT_EQ(stack->frames()->at(3)->module->code_file(),
"C:\\WINDOWS\\system32\\kernel32.dll");
ASSERT_TRUE(stack->frames()->at(3)->function_name.empty());
ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
ASSERT_EQ(state.modules()->module_count(), 13);
ASSERT_EQ(state.modules()->module_count(), 13U);
ASSERT_TRUE(state.modules()->GetMainModule());
ASSERT_EQ(state.modules()->GetMainModule()->code_file(), "c:\\test_app.exe");
ASSERT_FALSE(state.modules()->GetModuleForAddress(0));

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -70,6 +70,7 @@ using google_breakpad::StackFramePPC;
using google_breakpad::StackFrameSPARC;
using google_breakpad::StackFrameX86;
using google_breakpad::StackFrameAMD64;
using google_breakpad::StackFrameARM;
// Separator character for machine readable output.
static const char kOutputSeparator = '|';
@ -162,6 +163,7 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
}
const char *trust_name;
switch (frame_x86->trust) {
default:
case StackFrameX86::FRAME_TRUST_NONE:
trust_name = "unknown";
break;
@ -194,6 +196,16 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
const StackFrameAMD64 *frame_amd64 =
reinterpret_cast<const StackFrameAMD64*>(frame);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
sequence = PrintRegister("rbx", frame_amd64->context.rbx, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
sequence = PrintRegister("r12", frame_amd64->context.r12, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
sequence = PrintRegister("r13", frame_amd64->context.r13, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
sequence = PrintRegister("r14", frame_amd64->context.r14, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
sequence = PrintRegister("r15", frame_amd64->context.r15, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
sequence = PrintRegister("rip", frame_amd64->context.rip, sequence);
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
@ -210,6 +222,35 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
} else if (cpu == "arm") {
const StackFrameARM *frame_arm =
reinterpret_cast<const StackFrameARM*>(frame);
// General-purpose callee-saves registers.
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
// Registers with a dedicated or conventional purpose.
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
}
printf("\n");
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -30,20 +30,39 @@
// Unit test for Minidump. Uses a pre-generated minidump and
// verifies that certain streams are correct.
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <stdlib.h>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/minidump.h"
#include "processor/logging.h"
#include "processor/synth_minidump.h"
namespace {
using google_breakpad::Minidump;
using google_breakpad::MinidumpContext;
using google_breakpad::MinidumpMemoryList;
using google_breakpad::MinidumpMemoryRegion;
using google_breakpad::MinidumpModule;
using google_breakpad::MinidumpModuleList;
using google_breakpad::MinidumpSystemInfo;
using google_breakpad::MinidumpThread;
using google_breakpad::MinidumpThreadList;
using google_breakpad::SynthMinidump::Context;
using google_breakpad::SynthMinidump::Dump;
using google_breakpad::SynthMinidump::Memory;
using google_breakpad::SynthMinidump::Module;
using google_breakpad::SynthMinidump::Stream;
using google_breakpad::SynthMinidump::String;
using google_breakpad::SynthMinidump::SystemInfo;
using google_breakpad::SynthMinidump::Thread;
using google_breakpad::TestAssembler::kBigEndian;
using google_breakpad::TestAssembler::kLittleEndian;
using std::ifstream;
using std::istringstream;
using std::string;
@ -65,7 +84,7 @@ TEST_F(MinidumpTest, TestMinidumpFromFile) {
ASSERT_TRUE(minidump.Read());
const MDRawHeader* header = minidump.header();
ASSERT_NE(header, (MDRawHeader*)NULL);
ASSERT_EQ(header->signature, MD_HEADER_SIGNATURE);
ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
//TODO: add more checks here
}
@ -91,13 +110,425 @@ TEST_F(MinidumpTest, TestMinidumpFromStream) {
ASSERT_TRUE(minidump.Read());
const MDRawHeader* header = minidump.header();
ASSERT_NE(header, (MDRawHeader*)NULL);
ASSERT_EQ(header->signature, MD_HEADER_SIGNATURE);
ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
//TODO: add more checks here
}
} // namespace
int main(int argc, char* argv[]) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
TEST(Dump, ReadBackEmpty) {
Dump dump(0);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream stream(contents);
Minidump minidump(stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}
TEST(Dump, ReadBackEmptyBigEndian) {
Dump big_minidump(0, kBigEndian);
big_minidump.Finish();
string contents;
ASSERT_TRUE(big_minidump.GetContents(&contents));
istringstream stream(contents);
Minidump minidump(stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
}
TEST(Dump, OneStream) {
Dump dump(0, kBigEndian);
Stream stream(dump, 0xfbb7fa2bU);
stream.Append("stream contents");
dump.Add(&stream);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ(0xfbb7fa2bU, dir->stream_type);
u_int32_t stream_length;
ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length));
ASSERT_EQ(15U, stream_length);
char stream_contents[15];
ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents)));
EXPECT_EQ(string("stream contents"),
string(stream_contents, sizeof(stream_contents)));
EXPECT_FALSE(minidump.GetThreadList());
EXPECT_FALSE(minidump.GetModuleList());
EXPECT_FALSE(minidump.GetMemoryList());
EXPECT_FALSE(minidump.GetException());
EXPECT_FALSE(minidump.GetAssertion());
EXPECT_FALSE(minidump.GetSystemInfo());
EXPECT_FALSE(minidump.GetMiscInfo());
EXPECT_FALSE(minidump.GetBreakpadInfo());
}
TEST(Dump, OneMemory) {
Dump dump(0, kBigEndian);
Memory memory(dump, 0x309d68010bd21b2cULL);
memory.Append("memory contents");
dump.Add(&memory);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((u_int32_t) MD_MEMORY_LIST_STREAM, dir->stream_type);
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
ASSERT_TRUE(memory_list != NULL);
ASSERT_EQ(1U, memory_list->region_count());
MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0);
ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase());
ASSERT_EQ(15U, region1->GetSize());
const u_int8_t *region1_bytes = region1->GetMemory();
ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0);
}
// One thread --- and its requisite entourage.
TEST(Dump, OneThread) {
Dump dump(0, kLittleEndian);
Memory stack(dump, 0x2326a0fa);
stack.Append("stack for thread");
MDRawContextX86 raw_context;
raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
raw_context.edi = 0x3ecba80d;
raw_context.esi = 0x382583b9;
raw_context.ebx = 0x7fccc03f;
raw_context.edx = 0xf62f8ec2;
raw_context.ecx = 0x46a6a6a8;
raw_context.eax = 0x6a5025e2;
raw_context.ebp = 0xd9fabb4a;
raw_context.eip = 0x6913f540;
raw_context.cs = 0xbffe6eda;
raw_context.eflags = 0xb2ce1e2d;
raw_context.esp = 0x659caaa4;
raw_context.ss = 0x2e951ef7;
Context context(dump, raw_context);
Thread thread(dump, 0xa898f11b, stack, context,
0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
dump.Add(&stack);
dump.Add(&context);
dump.Add(&thread);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
MinidumpMemoryList *md_memory_list = minidump.GetMemoryList();
ASSERT_TRUE(md_memory_list != NULL);
ASSERT_EQ(1U, md_memory_list->region_count());
MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0);
ASSERT_EQ(0x2326a0faU, md_region->GetBase());
ASSERT_EQ(16U, md_region->GetSize());
const u_int8_t *region_bytes = md_region->GetMemory();
ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0);
MinidumpThreadList *thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(1U, thread_list->thread_count());
MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0);
ASSERT_TRUE(md_thread != NULL);
u_int32_t thread_id;
ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
ASSERT_EQ(0xa898f11bU, thread_id);
MinidumpMemoryRegion *md_stack = md_thread->GetMemory();
ASSERT_TRUE(md_stack != NULL);
ASSERT_EQ(0x2326a0faU, md_stack->GetBase());
ASSERT_EQ(16U, md_stack->GetSize());
const u_int8_t *md_stack_bytes = md_stack->GetMemory();
ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0);
MinidumpContext *md_context = md_thread->GetContext();
ASSERT_TRUE(md_context != NULL);
ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
ASSERT_TRUE(md_raw_context != NULL);
ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
(md_raw_context->context_flags
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
EXPECT_EQ(0x382583b9U, raw_context.esi);
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
EXPECT_EQ(0x6913f540U, raw_context.eip);
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
EXPECT_EQ(0x659caaa4U, raw_context.esp);
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
}
TEST(Dump, OneModule) {
static const MDVSFixedFileInfo fixed_file_info = {
0xb2fba33a, // signature
0x33d7a728, // struct_version
0x31afcb20, // file_version_hi
0xe51cdab1, // file_version_lo
0xd1ea6907, // product_version_hi
0x03032857, // product_version_lo
0x11bf71d7, // file_flags_mask
0x5fb8cdbf, // file_flags
0xe45d0d5d, // file_os
0x107d9562, // file_type
0x5a8844d4, // file_subtype
0xa8d30b20, // file_date_hi
0x651c3e4e // file_date_lo
};
Dump dump(0, kBigEndian);
String module_name(dump, "single module");
Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
module_name,
0xb1054d2a,
0x34571371,
fixed_file_info, // from synth_minidump_unittest_data.h
NULL, NULL);
dump.Add(&module);
dump.Add(&module_name);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((u_int32_t) MD_MODULE_LIST_STREAM, dir->stream_type);
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(1U, md_module_list->module_count());
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
ASSERT_TRUE(md_module != NULL);
ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
ASSERT_EQ(0xada542bd, md_module->size());
ASSERT_EQ("single module", md_module->code_file());
const MDRawModule *md_raw_module = md_module->module();
ASSERT_TRUE(md_raw_module != NULL);
ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
ASSERT_EQ(0x34571371U, md_raw_module->checksum);
ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
sizeof(fixed_file_info)) == 0);
}
TEST(Dump, OneSystemInfo) {
Dump dump(0, kLittleEndian);
String csd_version(dump, "Petulant Pierogi");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
dump.Add(&system_info);
dump.Add(&csd_version);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
ASSERT_TRUE(dir != NULL);
EXPECT_EQ((u_int32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type);
MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo();
ASSERT_TRUE(md_system_info != NULL);
ASSERT_EQ("windows", md_system_info->GetOS());
ASSERT_EQ("x86", md_system_info->GetCPU());
ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion());
ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor());
}
TEST(Dump, BigDump) {
Dump dump(0, kLittleEndian);
// A SystemInfo stream.
String csd_version(dump, "Munificent Macaque");
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
dump.Add(&csd_version);
dump.Add(&system_info);
// Five threads!
Memory stack0(dump, 0x70b9ebfc);
stack0.Append("stack for thread zero");
MDRawContextX86 raw_context0;
raw_context0.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context0.eip = 0xaf0709e4;
Context context0(dump, raw_context0);
Thread thread0(dump, 0xbbef4432, stack0, context0,
0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL);
dump.Add(&stack0);
dump.Add(&context0);
dump.Add(&thread0);
Memory stack1(dump, 0xf988cc45);
stack1.Append("stack for thread one");
MDRawContextX86 raw_context1;
raw_context1.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context1.eip = 0xe4f56f81;
Context context1(dump, raw_context1);
Thread thread1(dump, 0x657c3f58, stack1, context1,
0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL);
dump.Add(&stack1);
dump.Add(&context1);
dump.Add(&thread1);
Memory stack2(dump, 0xc8a92e7c);
stack2.Append("stack for thread two");
MDRawContextX86 raw_context2;
raw_context2.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context2.eip = 0xb336a438;
Context context2(dump, raw_context2);
Thread thread2(dump, 0xdf4b8a71, stack2, context2,
0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL);
dump.Add(&stack2);
dump.Add(&context2);
dump.Add(&thread2);
Memory stack3(dump, 0x36d08e08);
stack3.Append("stack for thread three");
MDRawContextX86 raw_context3;
raw_context3.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context3.eip = 0xdf99a60c;
Context context3(dump, raw_context3);
Thread thread3(dump, 0x86e6c341, stack3, context3,
0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL);
dump.Add(&stack3);
dump.Add(&context3);
dump.Add(&thread3);
Memory stack4(dump, 0x1e0ab4fa);
stack4.Append("stack for thread four");
MDRawContextX86 raw_context4;
raw_context4.context_flags = MD_CONTEXT_X86_INTEGER;
raw_context4.eip = 0xaa646267;
Context context4(dump, raw_context4);
Thread thread4(dump, 0x261a28d4, stack4, context4,
0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL);
dump.Add(&stack4);
dump.Add(&context4);
dump.Add(&thread4);
// Three modules!
String module1_name(dump, "module one");
Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name);
dump.Add(&module1_name);
dump.Add(&module1);
String module2_name(dump, "module two");
Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name);
dump.Add(&module2_name);
dump.Add(&module2);
String module3_name(dump, "module three");
Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name);
dump.Add(&module3_name);
dump.Add(&module3);
// Add one more memory region, on top of the five stacks.
Memory memory5(dump, 0x61979e828040e564ULL);
memory5.Append("contents of memory 5");
dump.Add(&memory5);
dump.Finish();
string contents;
ASSERT_TRUE(dump.GetContents(&contents));
istringstream minidump_stream(contents);
Minidump minidump(minidump_stream);
ASSERT_TRUE(minidump.Read());
ASSERT_EQ(4U, minidump.GetDirectoryEntryCount());
// Check the threads.
MinidumpThreadList *thread_list = minidump.GetThreadList();
ASSERT_TRUE(thread_list != NULL);
ASSERT_EQ(5U, thread_list->thread_count());
u_int32_t thread_id;
ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id));
ASSERT_EQ(0xbbef4432U, thread_id);
ASSERT_EQ(0x70b9ebfcU,
thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase());
ASSERT_EQ(0xaf0709e4U,
thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id));
ASSERT_EQ(0x657c3f58U, thread_id);
ASSERT_EQ(0xf988cc45U,
thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase());
ASSERT_EQ(0xe4f56f81U,
thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id));
ASSERT_EQ(0xdf4b8a71U, thread_id);
ASSERT_EQ(0xc8a92e7cU,
thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase());
ASSERT_EQ(0xb336a438U,
thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id));
ASSERT_EQ(0x86e6c341U, thread_id);
ASSERT_EQ(0x36d08e08U,
thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase());
ASSERT_EQ(0xdf99a60cU,
thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86()
->eip);
ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id));
ASSERT_EQ(0x261a28d4U, thread_id);
ASSERT_EQ(0x1e0ab4faU,
thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase());
ASSERT_EQ(0xaa646267U,
thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86()
->eip);
// Check the modules.
MinidumpModuleList *md_module_list = minidump.GetModuleList();
ASSERT_TRUE(md_module_list != NULL);
ASSERT_EQ(3U, md_module_list->module_count());
EXPECT_EQ(0xeb77da57b5d4cbdaULL,
md_module_list->GetModuleAtIndex(0)->base_address());
EXPECT_EQ(0x8675884adfe5ac90ULL,
md_module_list->GetModuleAtIndex(1)->base_address());
EXPECT_EQ(0x95fc1544da321b6cULL,
md_module_list->GetModuleAtIndex(2)->base_address());
}
} // namespace

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright (c) 2006, Google Inc.
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -64,11 +66,9 @@ class AutoStackClearer {
template<typename ValueType>
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
DictionaryValidityType *assigned) {
// Ensure that the stack is cleared before returning.
AutoStackClearer clearer(&stack_);
bool PostfixEvaluator<ValueType>::EvaluateInternal(
const string &expression,
DictionaryValidityType *assigned) {
// Tokenize, splitting on whitespace.
istringstream stream(expression);
string token;
@ -194,13 +194,46 @@ bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
}
}
return true;
}
template<typename ValueType>
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
DictionaryValidityType *assigned) {
// Ensure that the stack is cleared before returning.
AutoStackClearer clearer(&stack_);
if (!EvaluateInternal(expression, assigned))
return false;
// If there's anything left on the stack, it indicates incomplete execution.
// This is a failure case. If the stack is empty, evalution was complete
// and successful.
BPLOG_IF(ERROR, !stack_.empty()) << "Incomplete execution: " << expression;
return stack_.empty();
if (stack_.empty())
return true;
BPLOG(ERROR) << "Incomplete execution: " << expression;
return false;
}
template<typename ValueType>
bool PostfixEvaluator<ValueType>::EvaluateForValue(const string &expression,
ValueType *result) {
// Ensure that the stack is cleared before returning.
AutoStackClearer clearer(&stack_);
if (!EvaluateInternal(expression, NULL))
return false;
// A successful execution should leave exactly one value on the stack.
if (stack_.size() != 1) {
BPLOG(ERROR) << "Expression yielded bad number of results: "
<< "'" << expression << "'";
return false;
}
return PopValue(result);
}
template<typename ValueType>
typename PostfixEvaluator<ValueType>::PopResult
@ -213,16 +246,30 @@ PostfixEvaluator<ValueType>::PopValueOrIdentifier(
string token = stack_.back();
stack_.pop_back();
// First, try to treat the value as a literal. In order for this to
// succed, the entire string must be parseable as ValueType. If this
// isn't possible, it can't be a literal, so treat it as an identifier
// instead.
// First, try to treat the value as a literal. Literals may have leading
// '-' sign, and the entire remaining string must be parseable as
// ValueType. If this isn't possible, it can't be a literal, so treat it
// as an identifier instead.
//
// Some versions of the libstdc++, the GNU standard C++ library, have
// stream extractors for unsigned integer values that permit a leading
// '-' sign (6.0.13); others do not (6.0.9). Since we require it, we
// handle it explicitly here.
istringstream token_stream(token);
ValueType literal;
bool negative;
if (token_stream.peek() == '-') {
negative = true;
token_stream.get();
} else {
negative = false;
}
if (token_stream >> literal && token_stream.peek() == EOF) {
if (value) {
*value = literal;
}
if (negative)
*value = -*value;
return POP_RESULT_VALUE;
} else {
if (identifier) {

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright (c) 2006, Google Inc.
// -*- mode: C++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -90,18 +92,24 @@ class PostfixEvaluator {
// (^) will not be supported. |dictionary| may be NULL, but evaluation
// will fail in that case unless set_dictionary is used before calling
// Evaluate.
PostfixEvaluator(DictionaryType *dictionary, MemoryRegion *memory)
PostfixEvaluator(DictionaryType *dictionary, const MemoryRegion *memory)
: dictionary_(dictionary), memory_(memory), stack_() {}
// Evaluate the expression. The results of execution will be stored
// in one (or more) variables in the dictionary. Returns false if any
// failures occure during execution, leaving variables in the dictionary
// in an indeterminate state. If assigned is non-NULL, any keys set in
// the dictionary as a result of evaluation will also be set to true in
// assigned, providing a way to determine if an expression modifies any
// of its input variables.
// Evaluate the expression, starting with an empty stack. The results of
// execution will be stored in one (or more) variables in the dictionary.
// Returns false if any failures occur during execution, leaving
// variables in the dictionary in an indeterminate state. If assigned is
// non-NULL, any keys set in the dictionary as a result of evaluation
// will also be set to true in assigned, providing a way to determine if
// an expression modifies any of its input variables.
bool Evaluate(const string &expression, DictionaryValidityType *assigned);
// Like Evaluate, but provides the value left on the stack to the
// caller. If evaluation succeeds and leaves exactly one value on
// the stack, pop that value, store it in *result, and return true.
// Otherwise, return false.
bool EvaluateForValue(const string &expression, ValueType *result);
DictionaryType* dictionary() const { return dictionary_; }
// Reset the dictionary. PostfixEvaluator does not take ownership.
@ -137,6 +145,12 @@ class PostfixEvaluator {
// Pushes a new value onto the stack.
void PushValue(const ValueType &value);
// Evaluate expression, updating *assigned if it is non-zero. Return
// true if evaluation completes successfully. Do not clear the stack
// upon successful evaluation.
bool EvaluateInternal(const string &expression,
DictionaryValidityType *assigned);
// The dictionary mapping constant and variable identifiers (strings) to
// values. Keys beginning with '$' are treated as variable names, and
// PostfixEvaluator is free to create and modify these keys. Weak pointer.
@ -144,7 +158,7 @@ class PostfixEvaluator {
// If non-NULL, the MemoryRegion used for dereference (^) operations.
// If NULL, dereferencing is unsupported and will fail. Weak pointer.
MemoryRegion *memory_;
const MemoryRegion *memory_;
// The stack contains state information as execution progresses. Values
// are pushed on to it as the expression string is read and as operations

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -56,21 +56,21 @@ using google_breakpad::PostfixEvaluator;
// the value.
class FakeMemoryRegion : public MemoryRegion {
public:
virtual u_int64_t GetBase() { return 0; }
virtual u_int32_t GetSize() { return 0; }
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) {
virtual u_int64_t GetBase() const { return 0; }
virtual u_int32_t GetSize() const { return 0; }
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
*value = address + 1;
return true;
}
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) {
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
*value = address + 1;
return true;
}
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) {
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
*value = address + 1;
return true;
}
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) {
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
*value = address + 1;
return true;
}
@ -103,6 +103,18 @@ struct EvaluateTestSet {
};
struct EvaluateForValueTest {
// Expression passed to PostfixEvaluator::Evaluate.
const string expression;
// True if the expression is expected to be evaluable, false if evaluation
// is expected to fail.
bool evaluable;
// If evaluable, the value we expect it to yield.
unsigned int value;
};
static bool RunTests() {
// The first test set checks the basic operations and failure modes.
PostfixEvaluator<unsigned int>::DictionaryType dictionary_0;
@ -289,6 +301,84 @@ static bool RunTests() {
}
}
// EvaluateForValue tests.
PostfixEvaluator<unsigned int>::DictionaryType dictionary_2;
dictionary_2["$ebp"] = 0xbfff0010;
dictionary_2["$eip"] = 0x10000000;
dictionary_2["$esp"] = 0xbfff0000;
dictionary_2[".cbSavedRegs"] = 4;
dictionary_2[".cbParams"] = 4;
dictionary_2[".raSearchStart"] = 0xbfff0020;
const EvaluateForValueTest evaluate_for_value_tests_2[] = {
{ "28907223", true, 28907223 }, // simple constant
{ "89854293 40010015 +", true, 89854293 + 40010015 }, // arithmetic
{ "-870245 8769343 +", true, 7899098 }, // negative constants
{ "$ebp $esp - $eip +", true, 0x10000010 }, // variable references
{ "18929794 34015074", false, 0 }, // too many values
{ "$ebp $ebp 4 - =", false, 0 }, // too few values
{ "$new $eip = $new", true, 0x10000000 }, // make new variable
{ "$new 4 +", true, 0x10000004 }, // see prior assignments
{ ".cfa 42 = 10", false, 0 } // can't set constants
};
const int evaluate_for_value_tests_2_size
= (sizeof (evaluate_for_value_tests_2)
/ sizeof (evaluate_for_value_tests_2[0]));
map<string, unsigned int> validate_data_2;
validate_data_2["$eip"] = 0x10000000;
validate_data_2["$ebp"] = 0xbfff000c;
validate_data_2["$esp"] = 0xbfff0000;
validate_data_2["$new"] = 0x10000000;
validate_data_2[".cbSavedRegs"] = 4;
validate_data_2[".cbParams"] = 4;
validate_data_2[".raSearchStart"] = 0xbfff0020;
postfix_evaluator.set_dictionary(&dictionary_2);
for (int i = 0; i < evaluate_for_value_tests_2_size; i++) {
const EvaluateForValueTest *test = &evaluate_for_value_tests_2[i];
unsigned int result;
if (postfix_evaluator.EvaluateForValue(test->expression, &result)
!= test->evaluable) {
fprintf(stderr, "FAIL: evaluate for value test %d, "
"expected evaluation to %s, but it %s\n",
i, test->evaluable ? "succeed" : "fail",
test->evaluable ? "failed" : "succeeded");
return false;
}
if (test->evaluable && result != test->value) {
fprintf(stderr, "FAIL: evaluate for value test %d, "
"expected value to be 0x%x, but it was 0x%x\n",
i, test->value, result);
return false;
}
}
for (map<string, unsigned int>::iterator v = validate_data_2.begin();
v != validate_data_2.end(); v++) {
map<string, unsigned int>::iterator a = dictionary_2.find(v->first);
if (a == dictionary_2.end()) {
fprintf(stderr, "FAIL: evaluate for value dictionary check: "
"expected dict[\"%s\"] to be 0x%x, but it was unset\n",
v->first.c_str(), v->second);
return false;
} else if (a->second != v->second) {
fprintf(stderr, "FAIL: evaluate for value dictionary check: "
"expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n",
v->first.c_str(), v->second, a->second);
return false;
}
dictionary_2.erase(a);
}
map<string, unsigned int>::iterator remaining = dictionary_2.begin();
if (remaining != dictionary_2.end()) {
fprintf(stderr, "FAIL: evaluation of test expressions put unexpected "
"values in dictionary:\n");
for (; remaining != dictionary_2.end(); remaining++)
fprintf(stderr, " dict[\"%s\"] == 0x%x\n",
remaining->first.c_str(), remaining->second);
return false;
}
return true;
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -156,7 +156,7 @@ bool RangeMap<AddressType, EntryType>::RetrieveNearestRange(
*entry = iterator->second.entry();
if (entry_base)
*entry_base = iterator->first;
*entry_base = iterator->second.base();
if (entry_size)
*entry_size = iterator->first - iterator->second.base() + 1;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -210,10 +210,11 @@ static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
linked_ptr<CountedObject> nearest_object;
AddressType nearest_base;
AddressType nearest_size;
bool retrieved_nearest = range_map->RetrieveNearestRange(address,
&nearest_object,
&nearest_base,
NULL);
&nearest_size);
// When checking one greater than the high side, RetrieveNearestRange
// should usually return the test range. When a different range begins
@ -237,6 +238,22 @@ static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
observed_nearest ? "true" : "false");
return false;
}
// If a range was successfully retrieved, check that the returned
// bounds match the range as stored.
if (expected_nearest &&
(nearest_base != range_test->address ||
nearest_size != range_test->size)) {
fprintf(stderr, "FAILED: "
"RetrieveNearestRange id %d, side %d, offset %d, "
"expected base/size %d/%d, observed %d/%d\n",
range_test->id,
side,
offset,
range_test->address, range_test->size,
nearest_base, nearest_size);
return false;
}
}
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -47,7 +47,6 @@
#include "processor/linked_ptr.h"
#include "processor/logging.h"
#include "processor/scoped_ptr.h"
#include "processor/stack_frame_info.h"
#include "processor/stackwalker_ppc.h"
#include "processor/stackwalker_sparc.h"
#include "processor/stackwalker_x86.h"
@ -65,8 +64,8 @@ Stackwalker::Stackwalker(const SystemInfo *system_info,
: system_info_(system_info),
memory_(memory),
modules_(modules),
supplier_(supplier),
resolver_(resolver) {
resolver_(resolver),
supplier_(supplier) {
}
@ -75,11 +74,6 @@ bool Stackwalker::Walk(CallStack *stack) {
assert(stack);
stack->Clear();
// stack_frame_info parallels the CallStack. The vector is passed to the
// GetCallerFrame function. It contains information that may be helpful
// for stackwalking.
vector< linked_ptr<StackFrameInfo> > stack_frame_info;
// Begin with the context frame, and keep getting callers until there are
// no more.
@ -91,8 +85,6 @@ bool Stackwalker::Walk(CallStack *stack) {
// frame_pointer fields. The frame structure comes from either the
// context frame (above) or a caller frame (below).
linked_ptr<StackFrameInfo> frame_info;
// Resolve the module information, if a module map was provided.
if (modules_) {
const CodeModule *module =
@ -101,6 +93,8 @@ bool Stackwalker::Walk(CallStack *stack) {
frame->module = module;
if (resolver_ &&
!resolver_->HasModule(frame->module->code_file()) &&
no_symbol_modules_.find(
module->code_file()) == no_symbol_modules_.end() &&
supplier_) {
string symbol_data, symbol_file;
SymbolSupplier::SymbolResult symbol_result =
@ -113,12 +107,13 @@ bool Stackwalker::Walk(CallStack *stack) {
symbol_data);
break;
case SymbolSupplier::NOT_FOUND:
no_symbol_modules_.insert(module->code_file());
break; // nothing to do
case SymbolSupplier::INTERRUPT:
return false;
}
}
frame_info.reset(resolver_->FillSourceLineInfo(frame.get()));
resolver_->FillSourceLineInfo(frame.get());
}
}
@ -126,12 +121,8 @@ bool Stackwalker::Walk(CallStack *stack) {
// over the frame, because the stack now owns it.
stack->frames_.push_back(frame.release());
// Add the frame info to the parallel stack.
stack_frame_info.push_back(frame_info);
frame_info.reset(NULL);
// Get the next frame and take ownership.
frame.reset(GetCallerFrame(stack, stack_frame_info));
frame.reset(GetCallerFrame(stack));
}
return true;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2007, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -34,15 +34,61 @@
// Author: Mark Mentovai, Ted Mielczarek
#include "processor/stackwalker_amd64.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/scoped_ptr.h"
#include "processor/stackwalker_amd64.h"
namespace google_breakpad {
const StackwalkerAMD64::CFIWalker::RegisterSet
StackwalkerAMD64::cfi_register_map_[] = {
// It may seem like $rip and $rsp are callee-saves, because the callee is
// responsible for having them restored upon return. But the callee_saves
// flags here really means that the walker should assume they're
// unchanged if the CFI doesn't mention them --- clearly wrong for $rip
// and $rsp.
{ "$rax", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RAX, &MDRawContextAMD64::rax },
{ "$rdx", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RDX, &MDRawContextAMD64::rdx },
{ "$rcx", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RCX, &MDRawContextAMD64::rcx },
{ "$rbx", NULL, true,
StackFrameAMD64::CONTEXT_VALID_RBX, &MDRawContextAMD64::rbx },
{ "$rsi", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RSI, &MDRawContextAMD64::rsi },
{ "$rdi", NULL, false,
StackFrameAMD64::CONTEXT_VALID_RDI, &MDRawContextAMD64::rdi },
{ "$rbp", NULL, true,
StackFrameAMD64::CONTEXT_VALID_RBP, &MDRawContextAMD64::rbp },
{ "$rsp", ".cfa", false,
StackFrameAMD64::CONTEXT_VALID_RSP, &MDRawContextAMD64::rsp },
{ "$r8", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R8, &MDRawContextAMD64::r8 },
{ "$r9", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R9, &MDRawContextAMD64::r9 },
{ "$r10", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R10, &MDRawContextAMD64::r10 },
{ "$r11", NULL, false,
StackFrameAMD64::CONTEXT_VALID_R11, &MDRawContextAMD64::r11 },
{ "$r12", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R12, &MDRawContextAMD64::r12 },
{ "$r13", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R13, &MDRawContextAMD64::r13 },
{ "$r14", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R14, &MDRawContextAMD64::r14 },
{ "$r15", NULL, true,
StackFrameAMD64::CONTEXT_VALID_R15, &MDRawContextAMD64::r15 },
{ "$rip", ".ra", false,
StackFrameAMD64::CONTEXT_VALID_RIP, &MDRawContextAMD64::rip },
};
StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info,
const MDRawContextAMD64 *context,
MemoryRegion *memory,
@ -50,7 +96,9 @@ StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context) {
context_(context),
cfi_walker_(cfi_register_map_,
(sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) {
}
@ -71,60 +119,70 @@ StackFrame* StackwalkerAMD64::GetContextFrame() {
return frame;
}
StackFrameAMD64 *StackwalkerAMD64::GetCallerByCFIFrameInfo(
const vector<StackFrame *> &frames,
CFIFrameInfo *cfi_frame_info) {
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64*>(frames.back());
StackFrame* StackwalkerAMD64::GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
scoped_ptr<StackFrameAMD64> frame(new StackFrameAMD64());
if (!cfi_walker_
.FindCallerRegisters(*memory_, *cfi_frame_info,
last_frame->context, last_frame->context_validity,
&frame->context, &frame->context_validity))
return NULL;
// Make sure we recovered all the essentials.
static const int essentials = (StackFrameAMD64::CONTEXT_VALID_RIP
| StackFrameAMD64::CONTEXT_VALID_RSP);
if ((frame->context_validity & essentials) != essentials)
return NULL;
return frame.release();
}
StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64*>(
stack->frames()->back());
const vector<StackFrame *> &frames = *stack->frames();
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64 *>(frames.back());
scoped_ptr<StackFrameAMD64> new_frame;
//FIXME: this pretty much doesn't work at all due to FPO
// being enabled by default.
// Brain-dead stackwalking:
// %rip_new = *(%rbp_old + 8)
// %rsp_new = %rbp_old + 16
// %rbp_new = *(%rbp_old)
// A caller frame must reside higher in memory than its callee frames.
// Anything else is an error, or an indication that we've reached the
// end of the stack.
u_int64_t stack_pointer = last_frame->context.rbp + 16;
if (stack_pointer <= last_frame->context.rsp) {
return NULL;
// If we have DWARF CFI information, use it.
if (!new_frame.get()) {
scoped_ptr<CFIFrameInfo> cfi_frame_info(resolver_
->FindCFIFrameInfo(last_frame));
if (cfi_frame_info.get())
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
}
u_int64_t instruction;
if (!memory_->GetMemoryAtAddress(last_frame->context.rbp + 8,
&instruction) ||
instruction <= 1) {
// If nothing worked, tell the caller.
if (!new_frame.get())
return NULL;
}
u_int64_t stack_base;
if (!memory_->GetMemoryAtAddress(last_frame->context.rbp,
&stack_base) ||
stack_base <= 1) {
// Treat an instruction address of 0 as end-of-stack.
if (new_frame->context.rip == 0)
return NULL;
}
StackFrameAMD64 *frame = new StackFrameAMD64();
// 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 (new_frame->context.rsp <= last_frame->context.rsp)
return NULL;
frame->context = last_frame->context;
frame->context.rip = instruction;
frame->context.rsp = stack_pointer;
frame->context.rbp = stack_base;
frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP |
StackFrameAMD64::CONTEXT_VALID_RBP;
// new_frame->context.rip is the return address, which is one instruction
// past the CALL that caused us to arrive at the callee. Set
// new_frame->instruction to one less than that. This won't reference the
// beginning of the CALL instruction, but it's guaranteed to be within
// the CALL, which is sufficient to get the source line information to
// match up with the line that contains a function call. Callers that
// require the exact return address value may access the context.rip
// field of StackFrameAMD64.
new_frame->instruction = new_frame->context.rip - 1;
frame->instruction = frame->context.rip - 1;
return frame;
return new_frame.release();
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2007, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -42,6 +42,8 @@
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/cfi_frame_info.h"
namespace google_breakpad {
@ -61,16 +63,29 @@ class StackwalkerAMD64 : public Stackwalker {
SourceLineResolverInterface *resolver);
private:
// A STACK CFI-driven frame walker for the AMD64
typedef SimpleCFIWalker<u_int64_t, MDRawContextAMD64> CFIWalker;
// Implementation of Stackwalker, using amd64 context (stack pointer in %rsp,
// stack base in %rbp) and stack conventions (saved stack pointer at 0(%rbp))
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// 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.
StackFrameAMD64 *GetCallerByCFIFrameInfo(const vector<StackFrame *> &frames,
CFIFrameInfo *cfi_frame_info);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.
const MDRawContextAMD64 *context_;
// Our register map, for cfi_walker_.
static const CFIWalker::RegisterSet cfi_register_map_[];
// Our CFI frame walker.
const CFIWalker cfi_walker_;
};

Просмотреть файл

@ -0,0 +1,334 @@
// 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_amd64_unittest.cc: Unit tests for StackwalkerAMD64 class.
#include <string>
#include <string.h>
#include <vector>
#include "breakpad_googletest_includes.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/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/stackwalker_unittest_utils.h"
#include "processor/stackwalker_amd64.h"
#include "processor/test_assembler.h"
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::StackFrame;
using google_breakpad::StackFrameAMD64;
using google_breakpad::StackwalkerAMD64;
using google_breakpad::SystemInfo;
using google_breakpad::TestAssembler::kLittleEndian;
using google_breakpad::TestAssembler::Label;
using google_breakpad::TestAssembler::Section;
using std::string;
using std::vector;
using testing::_;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class StackwalkerAMD64Fixture {
public:
StackwalkerAMD64Fixture()
: stack_section(kLittleEndian),
// Give the two modules reasonable standard locations and names
// for tests to play with.
module1(0x40000000c0000000ULL, 0x10000, "module1", "version1"),
module2(0x50000000b0000000ULL, 0x10000, "module2", "version2") {
// Identify the system as a Linux system.
system_info.os = "Linux";
system_info.os_short = "linux";
system_info.os_version = "Horrendous Hippo";
system_info.cpu = "x86";
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, GetSymbolFile(_, _, _, _))
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
}
// Set the Breakpad symbol information that supplier should return for
// MODULE to INFO.
void SetModuleSymbols(MockCodeModule *module, const string &info) {
EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<3>(info),
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(MDRawContextAMD64 *raw_context) {
u_int8_t x = 173;
for (size_t i = 0; i < sizeof(*raw_context); i++)
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
}
SystemInfo system_info;
MDRawContextAMD64 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 GetContextFrame: public StackwalkerAMD64Fixture, public Test { };
TEST_F(GetContextFrame, Simple) {
// There should be no references to the stack in this walk: we don't
// provide any call frame information, so trying to reconstruct the
// context frame's caller should fail. So there's no need for us to
// provide stack contents.
raw_context.rip = 0x40000000c0000200ULL;
raw_context.rbp = 0x8000000080000000ULL;
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_GE(1U, frames->size());
StackFrameAMD64 *frame = static_cast<StackFrameAMD64 *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0);
}
struct CFIFixture: public StackwalkerAMD64Fixture {
CFIFixture() {
// Provide a bunch of STACK CFI records; we'll walk to the caller
// from every point in this series, expecting to find the same set
// of register values.
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 4000 1000 10 enchiridion\n"
// Initially, just a return address.
"STACK CFI INIT 4000 100 .cfa: $rsp 8 + .ra: .cfa 8 - ^\n"
// Push %rbx.
"STACK CFI 4001 .cfa: $rsp 16 + $rbx: .cfa 16 - ^\n"
// Save %r12 in %rbx. Weird, but permitted.
"STACK CFI 4002 $r12: $rbx\n"
// Allocate frame space, and save %r13.
"STACK CFI 4003 .cfa: $rsp 40 + $r13: .cfa 32 - ^\n"
// Put the return address in %r13.
"STACK CFI 4005 .ra: $r13\n"
// Save %rbp, and use it as a frame pointer.
"STACK CFI 4006 .cfa: $rbp 16 + $rbp: .cfa 24 - ^\n"
// The calling function.
"FUNC 5000 1000 10 epictetus\n"
// Mark it as end of stack.
"STACK CFI INIT 5000 1000 .cfa: $rsp .ra 0\n");
// Provide some distinctive values for the caller's registers.
expected.rsp = 0x8000000080000000ULL;
expected.rip = 0x40000000c0005510ULL;
expected.rbp = 0x68995b1de4700266ULL;
expected.rbx = 0x5a5beeb38de23be8ULL;
expected.r12 = 0xed1b02e8cc0fc79cULL;
expected.r13 = 0x1d20ad8acacbe930ULL;
expected.r14 = 0xe94cffc2f7adaa28ULL;
expected.r15 = 0xb638d17d8da413b5ULL;
// By default, registers are unchanged.
raw_context = expected;
}
// Walk the stack, using stack_section as the contents of the stack
// and raw_context as the current register values. (Set
// raw_context.rsp to the stack's starting address.) Expect two
// stack frames; in the older frame, expect the callee-saves
// registers to have values matching those in 'expected'.
void CheckWalk() {
RegionFromSection();
raw_context.rsp = stack_section.start().Value();
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameAMD64 *frame0 = static_cast<StackFrameAMD64 *>(frames->at(0));
ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity);
EXPECT_EQ("enchiridion", frame0->function_name);
EXPECT_EQ(0x40000000c0004000ULL, frame0->function_base);
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
StackFrameAMD64::CONTEXT_VALID_RSP |
StackFrameAMD64::CONTEXT_VALID_RBP |
StackFrameAMD64::CONTEXT_VALID_RBX |
StackFrameAMD64::CONTEXT_VALID_R12 |
StackFrameAMD64::CONTEXT_VALID_R13 |
StackFrameAMD64::CONTEXT_VALID_R14 |
StackFrameAMD64::CONTEXT_VALID_R15),
frame1->context_validity);
EXPECT_EQ(expected.rip, frame1->context.rip);
EXPECT_EQ(expected.rsp, frame1->context.rsp);
EXPECT_EQ(expected.rbp, frame1->context.rbp);
EXPECT_EQ(expected.rbx, frame1->context.rbx);
EXPECT_EQ(expected.r12, frame1->context.r12);
EXPECT_EQ(expected.r13, frame1->context.r13);
EXPECT_EQ(expected.r14, frame1->context.r14);
EXPECT_EQ(expected.r15, frame1->context.r15);
EXPECT_EQ("epictetus", frame1->function_name);
}
// The values we expect to find for the caller's registers.
MDRawContextAMD64 expected;
};
class CFI: public CFIFixture, public Test { };
TEST_F(CFI, At4000) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004000ULL;
CheckWalk();
}
TEST_F(CFI, At4001) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004001ULL;
raw_context.rbx = 0xbe0487d2f9eafe29ULL; // callee's (distinct) %rbx value
CheckWalk();
}
TEST_F(CFI, At4002) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004002ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0xb0118de918a4bceaULL; // callee's (distinct) %r12 value
CheckWalk();
}
TEST_F(CFI, At4003) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x0e023828dffd4d81ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0x319e68b49e3ace0fULL) // garbage
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004003ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12
raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13
CheckWalk();
}
// The results here should be the same as those at module offset 0x4003.
TEST_F(CFI, At4004) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x0e023828dffd4d81ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0x319e68b49e3ace0fULL) // garbage
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0x40000000c0005510ULL) // return address
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004004ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12
raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13
CheckWalk();
}
TEST_F(CFI, At4005) {
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x4b516dd035745953ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0xa6d445e16ae3d872ULL) // garbage
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0xaa95fa054aedfbaeULL) // garbage
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004005ULL;
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x46b1b8868891b34aULL; // callee's %r12
raw_context.r13 = 0x40000000c0005510ULL; // return address
CheckWalk();
}
TEST_F(CFI, At4006) {
Label frame0_rbp;
Label frame1_rsp = expected.rsp;
stack_section
.D64(0x043c6dfceb91aa34ULL) // garbage
.D64(0x1d20ad8acacbe930ULL) // saved %r13
.D64(0x68995b1de4700266ULL) // saved %rbp
.Mark(&frame0_rbp) // frame pointer points here
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
.D64(0xf015ee516ad89eabULL) // garbage
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
raw_context.rip = 0x40000000c0004006ULL;
raw_context.rbp = frame0_rbp.Value();
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
raw_context.r12 = 0x26e007b341acfebdULL; // callee's %r12
raw_context.r13 = 0x40000000c0005510ULL; // return address
CheckWalk();
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2009, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -31,14 +31,17 @@
//
// See stackwalker_arm.h for documentation.
//
// Author: Mark Mentovai, Ted Mielczarek
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
#include "processor/stackwalker_arm.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/scoped_ptr.h"
#include "processor/stackwalker_arm.h"
namespace google_breakpad {
@ -50,8 +53,8 @@ StackwalkerARM::StackwalkerARM(const SystemInfo *system_info,
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver)
: Stackwalker(system_info, memory, modules, supplier, resolver),
context_(context) {
}
context_(context),
context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
StackFrame* StackwalkerARM::GetContextFrame() {
@ -65,27 +68,117 @@ StackFrame* StackwalkerARM::GetContextFrame() {
// The instruction pointer is stored directly in a register (r15), so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = StackFrameARM::CONTEXT_VALID_ALL;
frame->context_validity = context_frame_validity_;
frame->instruction = frame->context.iregs[15];
return frame;
}
StackFrame* StackwalkerARM::GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
StackFrameARM *last_frame = static_cast<StackFrameARM*>(
stack->frames()->back());
const vector<StackFrame *> &frames = *stack->frames();
StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
// TODO: Can't actually walk the stack on ARM without the CFI data.
// Implement this when the CFI symbol dumper changes have landed.
return NULL;
// See if we have DWARF call frame information covering this address.
scoped_ptr<CFIFrameInfo> cfi_frame_info(resolver_
->FindCFIFrameInfo(last_frame));
if (cfi_frame_info == NULL)
// Unfortunately, CFI is our only option on the ARM for now. If we
// add a second strategy, we should put each one in its own function.
return NULL;
static const char *register_names[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"fps", "cpsr",
NULL
};
// Populate a dictionary with the valid register values in last_frame.
CFIFrameInfo::RegisterValueMap<u_int32_t> callee_registers;
for (int i = 0; register_names[i]; i++)
if (last_frame->context_validity & StackFrameARM::RegisterValidFlag(i))
callee_registers[register_names[i]] = last_frame->context.iregs[i];
// Use the STACK CFI data to recover the caller's register values.
CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
&caller_registers))
return NULL;
// Construct a new stack frame given the values the CFI recovered.
scoped_ptr<StackFrameARM> frame(new StackFrameARM());
for (int i = 0; register_names[i]; i++) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
caller_registers.find(register_names[i]);
if (entry != caller_registers.end()) {
// We recovered the value of this register; fill the context with the
// value from caller_registers.
frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
frame->context.iregs[i] = entry->second;
} else if (4 <= i && i <= 11 && (last_frame->context_validity &
StackFrameARM::RegisterValidFlag(i))) {
// If the STACK CFI data doesn't mention some callee-saves register, and
// it is valid in the callee, assume the callee has not yet changed it.
// Registers r4 through r11 are callee-saves, according to the Procedure
// Call Standard for the ARM Architecture, which the Linux ABI follows.
frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
frame->context.iregs[i] = last_frame->context.iregs[i];
}
}
// If the CFI doesn't recover the PC explicitly, then use .ra.
if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_PC)) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
caller_registers.find(".ra");
if (entry != caller_registers.end()) {
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
}
}
// If the CFI doesn't recover the SP explicitly, then use .cfa.
if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) {
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
caller_registers.find(".cfa");
if (entry != caller_registers.end()) {
frame->context_validity |= StackFrameARM::CONTEXT_VALID_SP;
frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = entry->second;
}
}
// If we didn't recover the PC and the SP, then the frame isn't very useful.
static const int essentials = (StackFrameARM::CONTEXT_VALID_SP
| StackFrameARM::CONTEXT_VALID_PC);
if ((frame->context_validity & essentials) != essentials)
return NULL;
// An instruction address of zero marks the end of the stack.
if (frame->context.iregs[MD_CONTEXT_ARM_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_ARM_REG_SP]
< last_frame->context.iregs[MD_CONTEXT_ARM_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. Set new_frame->instruction to one less than the PC. This won't
// reference the beginning of the call instruction, but it's at least
// within it, which is sufficient to get the source line information to
// match up with the line that contains the function call. Callers that
// require the exact return address value may access
// frame->context.iregs[MD_CONTEXT_ARM_REG_PC].
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 1;
return frame.release();
}

Просмотреть файл

@ -1,4 +1,6 @@
// Copyright (c) 2009, Google Inc.
// -*- mode: C++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -60,17 +62,25 @@ class StackwalkerARM : public Stackwalker {
SymbolSupplier *supplier,
SourceLineResolverInterface *resolver);
// 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 arm context and stack conventions.
// TODO: currently stubbed out, needs CFI symbol dumper support
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Stores the CPU context corresponding to the innermost stack frame to
// Stores the CPU context corresponding to the youngest stack frame, to
// be returned by GetContextFrame.
const MDRawContextARM *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.
int context_frame_validity_;
};

Просмотреть файл

@ -0,0 +1,440 @@
// 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_arm_unittest.cc: Unit tests for StackwalkerARM class.
#include <string>
#include <string.h>
#include <vector>
#include "breakpad_googletest_includes.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/source_line_resolver_interface.h"
#include "google_breakpad/processor/stack_frame_cpu.h"
#include "processor/stackwalker_unittest_utils.h"
#include "processor/stackwalker_arm.h"
#include "processor/test_assembler.h"
#include "processor/windows_frame_info.h"
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::StackFrame;
using google_breakpad::StackFrameARM;
using google_breakpad::StackwalkerARM;
using google_breakpad::SystemInfo;
using google_breakpad::WindowsFrameInfo;
using google_breakpad::TestAssembler::kLittleEndian;
using google_breakpad::TestAssembler::Label;
using google_breakpad::TestAssembler::Section;
using std::string;
using std::vector;
using testing::_;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class StackwalkerARMFixture {
public:
StackwalkerARMFixture()
: 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 a Linux system.
system_info.os = "Linux";
system_info.os_short = "linux";
system_info.os_version = "Lugubrious Labrador";
system_info.cpu = "arm";
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, GetSymbolFile(_, _, _, _))
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
}
// Set the Breakpad symbol information that supplier should return for
// MODULE to INFO.
void SetModuleSymbols(MockCodeModule *module, const string &info) {
EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<3>(info),
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(MDRawContextARM *raw_context) {
u_int8_t x = 173;
for (size_t i = 0; i < sizeof(*raw_context); i++)
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
}
SystemInfo system_info;
MDRawContextARM 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 GetContextFrame: public StackwalkerARMFixture, public Test { };
TEST_F(GetContextFrame, Simple) {
// Since we have no call frame information, and all unwinding
// requires call frame information, the stack walk will end after
// the first frame.
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0);
}
struct CFIFixture: public StackwalkerARMFixture {
CFIFixture() {
// Provide a bunch of STACK CFI records; we'll walk to the caller
// from every point in this series, expecting to find the same set
// of register values.
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 4000 1000 10 enchiridion\n"
// Initially, nothing has been pushed on the stack,
// and the return address is still in the link register.
"STACK CFI INIT 4000 100 .cfa: sp .ra: lr\n"
// Push r4, the frame pointer, and the link register.
"STACK CFI 4001 .cfa: sp 12 + r4: .cfa 12 - ^"
" r11: .cfa 8 - ^ .ra: .cfa 4 - ^\n"
// Save r4..r7 in r0..r3: verify that we populate
// the youngest frame with all the values we have.
"STACK CFI 4002 r4: r0 r5: r1 r6: r2 r7: r3\n"
// Restore r4..r7. Save the non-callee-saves register r1.
"STACK CFI 4003 .cfa: sp 16 + r1: .cfa 16 - ^"
" r4: r4 r5: r5 r6: r6 r7: r7\n"
// Move the .cfa back four bytes, to point at the return
// address, and restore the sp explicitly.
"STACK CFI 4005 .cfa: sp 12 + r1: .cfa 12 - ^"
" r11: .cfa 4 - ^ .ra: .cfa ^ sp: .cfa 4 +\n"
// Recover the PC explicitly from a new stack slot;
// provide garbage for the .ra.
"STACK CFI 4006 .cfa: sp 16 + pc: .cfa 16 - ^\n"
// The calling function.
"FUNC 5000 1000 10 epictetus\n"
// Mark it as end of stack.
"STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n"
// A function whose CFI makes the stack pointer
// go backwards.
"FUNC 6000 1000 20 palinal\n"
"STACK CFI INIT 6000 1000 .cfa: sp 4 - .ra: lr\n"
// A function with CFI expressions that can't be
// evaluated.
"FUNC 7000 1000 20 rhetorical\n"
"STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n");
// Provide some distinctive values for the caller's registers.
expected.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
expected.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
expected.iregs[4] = 0xb5d55e68;
expected.iregs[5] = 0xebd134f3;
expected.iregs[6] = 0xa31e74bc;
expected.iregs[7] = 0x2dcb16b3;
expected.iregs[8] = 0x2ada2137;
expected.iregs[9] = 0xbbbb557d;
expected.iregs[10] = 0x48bf8ca7;
expected.iregs[MD_CONTEXT_ARM_REG_FP] = 0x8112e110;
// Expect CFI to recover all callee-saves registers. Since CFI is the
// only stack frame construction technique we have, aside from the
// context frame itself, there's no way for us to have a set of valid
// registers smaller than this.
expected_validity = (StackFrameARM::CONTEXT_VALID_PC |
StackFrameARM::CONTEXT_VALID_SP |
StackFrameARM::CONTEXT_VALID_R4 |
StackFrameARM::CONTEXT_VALID_R5 |
StackFrameARM::CONTEXT_VALID_R6 |
StackFrameARM::CONTEXT_VALID_R7 |
StackFrameARM::CONTEXT_VALID_R8 |
StackFrameARM::CONTEXT_VALID_R9 |
StackFrameARM::CONTEXT_VALID_R10 |
StackFrameARM::CONTEXT_VALID_FP);
// By default, context frames provide all registers, as normal.
context_frame_validity = StackFrameARM::CONTEXT_VALID_ALL;
// By default, registers are unchanged.
raw_context = expected;
}
// Walk the stack, using stack_section as the contents of the stack
// and raw_context as the current register values. (Set the stack
// pointer to the stack's starting address.) Expect two stack
// frames; in the older frame, expect the callee-saves registers to
// have values matching those in 'expected'.
void CheckWalk() {
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
walker.SetContextFrameValidity(context_frame_validity);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
ASSERT_EQ(context_frame_validity, frame0->context_validity);
EXPECT_EQ("enchiridion", frame0->function_name);
EXPECT_EQ(0x40004000U, frame0->function_base);
StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
ASSERT_EQ(expected_validity, frame1->context_validity);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R1)
EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R4)
EXPECT_EQ(expected.iregs[4], frame1->context.iregs[4]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R5)
EXPECT_EQ(expected.iregs[5], frame1->context.iregs[5]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R6)
EXPECT_EQ(expected.iregs[6], frame1->context.iregs[6]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R7)
EXPECT_EQ(expected.iregs[7], frame1->context.iregs[7]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R8)
EXPECT_EQ(expected.iregs[8], frame1->context.iregs[8]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R9)
EXPECT_EQ(expected.iregs[9], frame1->context.iregs[9]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_R10)
EXPECT_EQ(expected.iregs[10], frame1->context.iregs[10]);
if (expected_validity & StackFrameARM::CONTEXT_VALID_FP)
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_FP],
frame1->context.iregs[MD_CONTEXT_ARM_REG_FP]);
// We would never have gotten a frame in the first place if the SP
// and PC weren't valid or ->instruction weren't set.
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_SP],
frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
frame1->instruction + 1);
EXPECT_EQ("epictetus", frame1->function_name);
}
// The values we expect to find for the caller's registers.
MDRawContextARM expected;
// The validity mask for expected.
int expected_validity;
// The validity mask to impose on the context frame.
int context_frame_validity;
};
class CFI: public CFIFixture, public Test { };
TEST_F(CFI, At4000) {
stack_section.start() = expected.iregs[MD_CONTEXT_ARM_REG_SP];
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004000;
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
CheckWalk();
}
TEST_F(CFI, At4001) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0xb5d55e68) // saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
raw_context.iregs[4] = 0x635adc9f; // distinct callee r4
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
CheckWalk();
}
// As above, but unwind from a context that has only the PC and SP.
TEST_F(CFI, At4001LimitedValidity) {
context_frame_validity =
StackFrameARM::CONTEXT_VALID_PC | StackFrameARM::CONTEXT_VALID_SP;
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0xb5d55e68) // saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
expected_validity = (StackFrameARM::CONTEXT_VALID_PC
| StackFrameARM::CONTEXT_VALID_SP
| StackFrameARM::CONTEXT_VALID_FP
| StackFrameARM::CONTEXT_VALID_R4);
CheckWalk();
}
TEST_F(CFI, At4002) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0xfb81ff3d) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004002;
raw_context.iregs[0] = 0xb5d55e68; // saved r4
raw_context.iregs[1] = 0xebd134f3; // saved r5
raw_context.iregs[2] = 0xa31e74bc; // saved r6
raw_context.iregs[3] = 0x2dcb16b3; // saved r7
raw_context.iregs[4] = 0xfdd35466; // distinct callee r4
raw_context.iregs[5] = 0xf18c946c; // distinct callee r5
raw_context.iregs[6] = 0xac2079e8; // distinct callee r6
raw_context.iregs[7] = 0xa449829f; // distinct callee r7
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
CheckWalk();
}
TEST_F(CFI, At4003) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xcb78040e) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004003;
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0x0a2857ea; // distinct callee fp
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// We have no new rule at module offset 0x4004, so the results here should
// be the same as those at module offset 0x4003.
TEST_F(CFI, At4004) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xcb78040e) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004004;
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// Here we move the .cfa, but provide an explicit rule to recover the SP,
// so again there should be no change in the registers recovered.
TEST_F(CFI, At4005) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xf013f841) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0x40005510) // return address
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004005;
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// Here we provide an explicit rule for the PC, and have the saved .ra be
// bogus.
TEST_F(CFI, At4006) {
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
stack_section
.D32(0x40005510) // saved pc
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
.D32(0xf013f841) // no longer saved r4
.D32(0x8112e110) // saved fp
.D32(0xf8d15783) // .ra rule recovers this, which is garbage
.Mark(&frame1_sp); // This effectively sets stack_section.start().
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004006;
raw_context.iregs[1] = 0xfb756319; // callee's r1, different from caller's
expected.iregs[1] = 0x48c8dd5a; // caller's r1
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
CheckWalk();
}
// Check that we reject rules that would cause the stack pointer to
// move in the wrong direction.
TEST_F(CFI, RejectBackwards) {
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000;
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
}
// Check that we reject rules whose expressions' evaluation fails.
TEST_F(CFI, RejectBadExpressions) {
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
&supplier, &resolver);
ASSERT_TRUE(walker.Walk(&call_stack));
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
}

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -81,9 +81,7 @@ StackFrame* StackwalkerPPC::GetContextFrame() {
}
StackFrame* StackwalkerPPC::GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2006, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -65,9 +65,7 @@ class StackwalkerPPC : public Stackwalker {
// saved program counter in %srr0) and stack conventions (saved stack
// pointer at 0(%r1), return address at 8(0(%r1)).
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2007, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -72,9 +72,7 @@ StackFrame* StackwalkerSPARC::GetContextFrame() {
}
StackFrame* StackwalkerSPARC::GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
StackFrame* StackwalkerSPARC::GetCallerFrame(const CallStack *stack) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;

Просмотреть файл

@ -1,4 +1,4 @@
// Copyright (c) 2007, Google Inc.
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
@ -61,18 +61,10 @@ class StackwalkerSPARC : public Stackwalker {
SourceLineResolverInterface *resolver);
private:
// Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
// alternate conventions as guided by stack_frame_info_).
// Implementation of Stackwalker, using ppc context (stack pointer in %r1,
// saved program counter in %srr0) and stack conventions (saved stack
// pointer at 0(%r1), return address at 8(0(%r1)).
// Implementation of Stackwalker, using sparc context (%fp, %sp, %pc) and
// stack conventions (saved %sp at)
// stack conventions
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(
const CallStack *stack,
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
virtual StackFrame* GetCallerFrame(const CallStack *stack);
// Stores the CPU context corresponding to the innermost stack frame to
// be returned by GetContextFrame.

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше