bug 567424 - sync to Breakpad revision 619 to pick up OS X symbol dumping changes (64-bit support + DWARF CFI support)

We still have a few local patches that have not yet been upstreamed, but they're all up in the Breakpad issue tracker now:
--
Bug 544936, part 1: Add the ability to generate a minidump of a child process at any time (linux). r=ted
Bug 555309, part 2: Allow a particular subprocess thread to be "blamed" in an OOP minidump. r=bsmedberg
Bug 555309, part 4: Insert an artificial exception stream into non-crash browser-side minidumps. r=bsmedberg
Bug 544936, part 2: Add the ability to generate a minidump of a child process at any time (windows). r=ted
Bug 555309, part 2: Allow a particular subprocess thread to be blamed in an OOP minidump. (windows bits) r=bsmedberg
Bug 555309, part 4: Insert an artificial exception stream into non-crash browser-side minidumps. r=bsmedberg (win32)

Windows changes: http://breakpad.appspot.com/115002/show
Linux changes: http://breakpad.appspot.com/123001/show
--
bug 557113 - sort out crash report certificate issues on Maemo. r=mfinkle,johnath

http://breakpad.appspot.com/121002/show
--
Plus one more I discovered while testing this patch on 64-bit OS X:

http://breakpad.appspot.com/124001/show

--HG--
extra : rebase_source : f0df7e87d26822884401b8cb1a5633c958e92c93
This commit is contained in:
Ted Mielczarek 2010-06-30 10:30:12 -04:00
parent 10e99fe287
commit 62c53fae00
176 changed files with 20026 additions and 4395 deletions

View File

@ -0,0 +1,44 @@
# 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.
# We only use this file to ease the steps of generating projects after
# syncing, if we use gclient. All dependencies are svn:externals instead.
# If you're not using gclient, you need to run the gyp python script to
# generate the projects.
# This can be done by the following command (assuming current directory):
# src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp
hooks = [
{
# A change to a .gyp, .gypi, or to GYP itself should run the generator.
"pattern": ".",
"action": ["python",
"src/src/tools/gyp/gyp",
"src/src/client/windows/breakpad_client.gyp"],
},
]

View File

@ -74,6 +74,7 @@ src_libbreakpad_la_SOURCES = \
src/google_breakpad/processor/memory_region.h \
src/google_breakpad/processor/minidump.h \
src/google_breakpad/processor/minidump_processor.h \
src/google-breakpad/processor/network_source_line_resolver.h \
src/google_breakpad/processor/process_state.h \
src/google_breakpad/processor/source_line_resolver_interface.h \
src/google_breakpad/processor/stack_frame.h \
@ -87,6 +88,8 @@ src_libbreakpad_la_SOURCES = \
src/processor/basic_code_modules.cc \
src/processor/basic_code_modules.h \
src/processor/basic_source_line_resolver.cc \
src/processor/binarystream.h \
src/processor/binarystream.cc \
src/processor/call_stack.cc \
src/processor/cfi_frame_info.cc \
src/processor/cfi_frame_info.h \
@ -97,6 +100,10 @@ src_libbreakpad_la_SOURCES = \
src/processor/logging.cc \
src/processor/minidump.cc \
src/processor/minidump_processor.cc \
src/processor/network_interface.h \
src/processor/network_source_line_resolver.cc \
src/processor/network_source_line_server.cc \
src/processor/network_source_line_server.h \
src/processor/pathname_stripper.cc \
src/processor/pathname_stripper.h \
src/processor/postfix_evaluator-inl.h \
@ -118,32 +125,41 @@ src_libbreakpad_la_SOURCES = \
src/processor/stackwalker_sparc.cc \
src/processor/stackwalker_sparc.h \
src/processor/stackwalker_x86.cc \
src/processor/stackwalker_x86.h
src/processor/stackwalker_x86.h \
src/processor/tokenize.cc \
src/processor/tokenize.h \
src/processor/udp_network.cc \
src/processor/udp_network.h
## Programs
bin_PROGRAMS = \
src/client/linux/linux_dumper_unittest_helper \
src/processor/minidump_dump \
src/processor/minidump_stackwalk
src/processor/minidump_stackwalk \
src/processor/source_daemon
## Tests
check_PROGRAMS = \
src/client/linux/linux_client_unittest \
src/common/test_assembler_unittest \
src/processor/address_map_unittest \
src/processor/binarystream_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/network_source_line_resolver_unittest \
src/processor/network_source_line_resolver_server_unittest \
src/processor/network_source_line_server_unittest \
src/processor/pathname_stripper_unittest \
src/processor/postfix_evaluator_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
src/processor/synth_minidump_unittest
if SELFTEST
check_PROGRAMS += \
@ -198,13 +214,33 @@ src_processor_address_map_unittest_LDADD = \
src/processor/logging.lo \
src/processor/pathname_stripper.lo
src_processor_binarystream_unittest_SOURCES = \
src/processor/binarystream_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_binarystream_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_binarystream_unittest_LDADD = \
src/processor/binarystream.lo
src_processor_basic_source_line_resolver_unittest_SOURCES = \
src/processor/basic_source_line_resolver_unittest.cc
src/processor/basic_source_line_resolver_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_basic_source_line_resolver_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_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/logging.lo \
src/processor/tokenize.lo
src_processor_cfi_frame_info_unittest_SOURCES = \
src/processor/cfi_frame_info_unittest.cc \
@ -251,12 +287,13 @@ src_processor_minidump_processor_unittest_LDADD = \
src/processor/stackwalker_arm.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo
src/processor/stackwalker_x86.lo \
src/processor/tokenize.lo
src_processor_minidump_unittest_SOURCES = \
src/common/test_assembler.cc \
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
@ -271,6 +308,74 @@ src_processor_minidump_unittest_LDADD = \
src/processor/minidump.lo \
src/processor/pathname_stripper.lo
src_processor_network_source_line_resolver_unittest_SOURCES = \
src/processor/network_source_line_resolver_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_network_source_line_resolver_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_network_source_line_resolver_unittest_LDADD = \
src/processor/basic_code_modules.lo \
src/processor/binarystream.lo \
src/processor/cfi_frame_info.lo \
src/processor/logging.lo \
src/processor/network_source_line_resolver.lo \
src/processor/pathname_stripper.lo \
src/processor/tokenize.lo \
src/processor/udp_network.lo
src_processor_network_source_line_resolver_server_unittest_SOURCES = \
src/processor/network_source_line_resolver_server_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_network_source_line_resolver_server_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_network_source_line_resolver_server_unittest_LDADD = \
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/binarystream.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 \
src/processor/network_source_line_resolver.lo \
src/processor/network_source_line_server.lo \
src/processor/pathname_stripper.lo \
src/processor/process_state.lo \
src/processor/simple_symbol_supplier.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_amd64.lo \
src/processor/stackwalker_arm.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo \
src/processor/tokenize.lo \
src/processor/udp_network.lo
src_processor_network_source_line_server_unittest_SOURCES = \
src/processor/network_source_line_server_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/src/gmock-all.cc
src_processor_network_source_line_server_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_network_source_line_server_unittest_LDADD = \
src/processor/binarystream.lo \
src/processor/cfi_frame_info.lo \
src/processor/logging.lo \
src/processor/network_source_line_server.lo \
src/processor/pathname_stripper.lo \
src/processor/udp_network.lo
src_processor_pathname_stripper_unittest_SOURCES = \
src/processor/pathname_stripper_unittest.cc
src_processor_pathname_stripper_unittest_LDADD = \
@ -302,11 +407,12 @@ src_processor_stackwalker_selftest_LDADD = \
src/processor/stackwalker_arm.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo
src/processor/stackwalker_x86.lo \
src/processor/tokenize.lo
src_processor_stackwalker_amd64_unittest_SOURCES = \
src/common/test_assembler.cc \
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
@ -319,8 +425,8 @@ src_processor_stackwalker_amd64_unittest_CPPFLAGS = \
-I$(top_srcdir)/src/testing
src_processor_stackwalker_arm_unittest_SOURCES = \
src/common/test_assembler.cc \
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
@ -333,8 +439,8 @@ src_processor_stackwalker_arm_unittest_CPPFLAGS = \
-I$(top_srcdir)/src/testing
src_processor_stackwalker_x86_unittest_SOURCES = \
src/common/test_assembler.cc \
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
@ -347,28 +453,28 @@ src_processor_stackwalker_x86_unittest_CPPFLAGS = \
-I$(top_srcdir)/src/testing
src_processor_synth_minidump_unittest_SOURCES = \
src/common/test_assembler.cc \
src/common/test_assembler.h \
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.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_common_test_assembler_unittest_SOURCES = \
src/common/test_assembler.cc \
src/common/test_assembler.h \
src/common/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 = \
src/testing/src/gmock-all.cc
src_common_test_assembler_unittest_CPPFLAGS = \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
@ -391,6 +497,7 @@ src_processor_minidump_stackwalk_SOURCES = \
src_processor_minidump_stackwalk_LDADD = \
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/binarystream.lo \
src/processor/call_stack.lo \
src/processor/cfi_frame_info.lo \
src/processor/logging.lo \
@ -398,14 +505,30 @@ src_processor_minidump_stackwalk_LDADD = \
src/processor/minidump_processor.lo \
src/processor/pathname_stripper.lo \
src/processor/process_state.lo \
src/processor/network_source_line_resolver.lo \
src/processor/simple_symbol_supplier.lo \
src/processor/stackwalker.lo \
src/processor/stackwalker_amd64.lo \
src/processor/stackwalker_arm.lo \
src/processor/stackwalker_ppc.lo \
src/processor/stackwalker_sparc.lo \
src/processor/stackwalker_x86.lo
src/processor/stackwalker_x86.lo \
src/processor/tokenize.lo \
src/processor/udp_network.lo
src_processor_source_daemon_SOURCES = \
src/processor/source_daemon.cc
src_processor_source_daemon_LDADD = \
src/processor/basic_code_modules.lo \
src/processor/basic_source_line_resolver.lo \
src/processor/binarystream.lo \
src/processor/cfi_frame_info.lo \
src/processor/logging.lo \
src/processor/network_source_line_server.lo \
src/processor/pathname_stripper.lo \
src/processor/simple_symbol_supplier.lo \
src/processor/tokenize.lo \
src/processor/udp_network.lo
## Additional files to be included in a source distribution
##

View File

@ -2460,7 +2460,7 @@ linux*oldld* | linux*aout* | linux*coff*)
;;
# This must be Linux ELF.
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
version_type=linux
need_lib_prefix=no
need_version=no
@ -2500,6 +2500,18 @@ linux* | k*bsd*-gnu)
dynamic_linker='GNU/Linux ld.so'
;;
netbsdelf*-gnu)
version_type=linux
need_lib_prefix=no
need_version=no
library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
soname_spec='${libname}${release}${shared_ext}$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='NetBSD ld.elf_so'
;;
netbsd*)
version_type=sunos
need_lib_prefix=no
@ -3087,11 +3099,11 @@ irix5* | irix6* | nonstopux*)
;;
# This must be Linux ELF.
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
lt_cv_deplibs_check_method=pass_all
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
else
@ -3708,7 +3720,7 @@ m4_if([$1], [CXX], [
;;
esac
;;
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
case $cc_basename in
KCC*)
# KAI C++ Compiler
@ -3772,7 +3784,7 @@ m4_if([$1], [CXX], [
;;
esac
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
;;
*qnx* | *nto*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
@ -3992,7 +4004,7 @@ m4_if([$1], [CXX], [
_LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
;;
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
case $cc_basename in
# old Intel for x86_64 which still supported -KPIC.
ecc*)
@ -4197,6 +4209,9 @@ m4_if([$1], [CXX], [
cygwin* | mingw* | cegcc*)
_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
;;
linux* | k*bsd*-gnu)
_LT_TAGVAR(link_all_deplibs, $1)=no
;;
*)
_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
;;
@ -4261,6 +4276,9 @@ dnl Note also adjust exclude_expsyms for C++ above.
openbsd*)
with_gnu_ld=no
;;
linux* | k*bsd*-gnu)
_LT_TAGVAR(link_all_deplibs, $1)=no
;;
esac
_LT_TAGVAR(ld_shlibs, $1)=yes
@ -4282,6 +4300,7 @@ dnl Note also adjust exclude_expsyms for C++ above.
fi
supports_anon_versioning=no
case `$LD -v 2>&1` in
*GNU\ gold*) supports_anon_versioning=yes ;;
*\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
*\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
*\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
@ -4373,7 +4392,7 @@ _LT_EOF
_LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
gnu* | linux* | tpf* | k*bsd*-gnu)
gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
tmp_diet=no
if test "$host_os" = linux-dietlibc; then
case $cc_basename in
@ -4443,7 +4462,7 @@ _LT_EOF
fi
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
wlarc=
@ -4618,6 +4637,7 @@ _LT_EOF
if test "$aix_use_runtimelinking" = yes; then
shared_flag="$shared_flag "'${wl}-G'
fi
_LT_TAGVAR(link_all_deplibs, $1)=no
else
# not using gcc
if test "$host_cpu" = ia64; then
@ -4856,7 +4876,7 @@ _LT_EOF
_LT_TAGVAR(link_all_deplibs, $1)=yes
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
else
@ -5852,7 +5872,7 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(inherit_rpath, $1)=yes
;;
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
case $cc_basename in
KCC*)
# Kuck and Associates, Inc. (KAI) C++ Compiler

View File

@ -0,0 +1,5 @@
# This file is used by gcl to get repository specific information.
CODE_REVIEW_SERVER: breakpad.appspot.com
CC_LIST: google-breakpad-dev@googlegroups.com
TRY_ON_UPLOAD: False
VIEW_VC: http://code.google.com/p/google-breakpad/source/detail?r=

View File

@ -2003,6 +2003,63 @@ fi
as_fn_set_status $ac_retval
} # ac_fn_cxx_try_link
# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
# ----------------------------------------------------
# Tries to find if the field MEMBER exists in type AGGR, after including
# INCLUDES, setting cache variable VAR accordingly.
ac_fn_c_check_member ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
$as_echo_n "checking for $2.$3... " >&6; }
if { as_var=$4; eval "test \"\${$as_var+set}\" = set"; }; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
main ()
{
static $2 ac_aggr;
if (ac_aggr.$3)
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$4=yes"
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
main ()
{
static $2 ac_aggr;
if (sizeof ac_aggr.$3)
return 0;
;
return 0;
}
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
eval "$4=yes"
else
eval "$4=no"
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
fi
eval ac_res=\$$4
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
} # ac_fn_c_check_member
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
@ -5123,13 +5180,13 @@ if test "${lt_cv_nm_interface+set}" = set; then :
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:5126: $ac_compile\"" >&5)
(eval echo "\"\$as_me:5183: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:5129: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval echo "\"\$as_me:5186: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:5132: output\"" >&5)
(eval echo "\"\$as_me:5189: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@ -5595,11 +5652,11 @@ irix5* | irix6* | nonstopux*)
;;
# This must be Linux ELF.
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
lt_cv_deplibs_check_method=pass_all
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
else
@ -6335,7 +6392,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 6338 "configure"' > conftest.$ac_ext
echo '#line 6395 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@ -8251,11 +8308,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8254: $lt_compile\"" >&5)
(eval echo "\"\$as_me:8311: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:8258: \$? = $ac_status" >&5
echo "$as_me:8315: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -8420,7 +8477,7 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; }
lt_prog_compiler_static='-non_shared'
;;
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
case $cc_basename in
# old Intel for x86_64 which still supported -KPIC.
ecc*)
@ -8590,11 +8647,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8593: $lt_compile\"" >&5)
(eval echo "\"\$as_me:8650: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:8597: \$? = $ac_status" >&5
echo "$as_me:8654: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -8695,11 +8752,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8698: $lt_compile\"" >&5)
(eval echo "\"\$as_me:8755: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:8702: \$? = $ac_status" >&5
echo "$as_me:8759: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -8750,11 +8807,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:8753: $lt_compile\"" >&5)
(eval echo "\"\$as_me:8810: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:8757: \$? = $ac_status" >&5
echo "$as_me:8814: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -8869,6 +8926,9 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
openbsd*)
with_gnu_ld=no
;;
linux* | k*bsd*-gnu)
link_all_deplibs=no
;;
esac
ld_shlibs=yes
@ -8890,6 +8950,7 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
fi
supports_anon_versioning=no
case `$LD -v 2>&1` in
*GNU\ gold*) supports_anon_versioning=yes ;;
*\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
*\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
*\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ...
@ -8981,7 +9042,7 @@ _LT_EOF
archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
gnu* | linux* | tpf* | k*bsd*-gnu)
gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
tmp_diet=no
if test "$host_os" = linux-dietlibc; then
case $cc_basename in
@ -9051,7 +9112,7 @@ _LT_EOF
fi
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
wlarc=
@ -9226,6 +9287,7 @@ _LT_EOF
if test "$aix_use_runtimelinking" = yes; then
shared_flag="$shared_flag "'${wl}-G'
fi
link_all_deplibs=no
else
# not using gcc
if test "$host_cpu" = ia64; then
@ -9552,7 +9614,7 @@ rm -f core conftest.err conftest.$ac_objext \
link_all_deplibs=yes
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
else
@ -10442,7 +10504,7 @@ linux*oldld* | linux*aout* | linux*coff*)
;;
# This must be Linux ELF.
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
version_type=linux
need_lib_prefix=no
need_version=no
@ -10497,6 +10559,18 @@ rm -f core conftest.err conftest.$ac_objext \
dynamic_linker='GNU/Linux ld.so'
;;
netbsdelf*-gnu)
version_type=linux
need_lib_prefix=no
need_version=no
library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
soname_spec='${libname}${release}${shared_ext}$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='NetBSD ld.elf_so'
;;
netbsd*)
version_type=sunos
need_lib_prefix=no
@ -11117,7 +11191,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11120 "configure"
#line 11194 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -11213,7 +11287,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 11216 "configure"
#line 11290 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -12153,7 +12227,7 @@ if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi
inherit_rpath_CXX=yes
;;
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
case $cc_basename in
KCC*)
# Kuck and Associates, Inc. (KAI) C++ Compiler
@ -12981,7 +13055,7 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; }
;;
esac
;;
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
case $cc_basename in
KCC*)
# KAI C++ Compiler
@ -13045,7 +13119,7 @@ $as_echo_n "checking for $compiler option to produce PIC... " >&6; }
;;
esac
;;
netbsd*)
netbsd* | netbsdelf*-gnu)
;;
*qnx* | *nto*)
# QNX uses GNU C++, but need to define -shared option too, otherwise
@ -13169,11 +13243,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:13172: $lt_compile\"" >&5)
(eval echo "\"\$as_me:13246: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:13176: \$? = $ac_status" >&5
echo "$as_me:13250: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -13268,11 +13342,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:13271: $lt_compile\"" >&5)
(eval echo "\"\$as_me:13345: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:13275: \$? = $ac_status" >&5
echo "$as_me:13349: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -13320,11 +13394,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:13323: $lt_compile\"" >&5)
(eval echo "\"\$as_me:13397: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:13327: \$? = $ac_status" >&5
echo "$as_me:13401: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -13396,6 +13470,9 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
cygwin* | mingw* | cegcc*)
export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;/^.*[ ]__nm__/s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols'
;;
linux* | k*bsd*-gnu)
link_all_deplibs_CXX=no
;;
*)
export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
;;
@ -13895,7 +13972,7 @@ linux*oldld* | linux*aout* | linux*coff*)
;;
# This must be Linux ELF.
linux* | k*bsd*-gnu)
linux* | k*bsd*-gnu | kopensolaris*-gnu)
version_type=linux
need_lib_prefix=no
need_version=no
@ -13950,6 +14027,18 @@ rm -f core conftest.err conftest.$ac_objext \
dynamic_linker='GNU/Linux ld.so'
;;
netbsdelf*-gnu)
version_type=linux
need_lib_prefix=no
need_version=no
library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
soname_spec='${libname}${release}${shared_ext}$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='NetBSD ld.elf_so'
;;
netbsd*)
version_type=sunos
need_lib_prefix=no
@ -14859,6 +14948,20 @@ else
fi
ac_fn_c_check_member "$LINENO" "struct sockaddr" "sa_len" "ac_cv_member_struct_sockaddr_sa_len" "#include <sys/socket.h>
"
if test "x$ac_cv_member_struct_sockaddr_sa_len" = x""yes; then :
$as_echo "#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_len)" >>confdefs.h
else
$as_echo "#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : \\
((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr))" >>confdefs.h
fi
# Check whether --enable-selftest was given.
if test "${enable_selftest+set}" = set; then :
enableval=$enable_selftest; case "${enableval}" in

View File

@ -67,6 +67,15 @@ AC_ARG_ENABLE(m32,
esac],
[usem32=false])
AC_CHECK_MEMBER(struct sockaddr.sa_len,
[AC_DEFINE([GET_SA_LEN(X)],[(((struct sockaddr*)&(X))->sa_len)],
[actual length of specific struct sockaddr])],
[AC_DEFINE([GET_SA_LEN(X)],
[(((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : \
((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr))],
[actual length of specific struct sockaddr])],
[#include <sys/socket.h>])
AC_ARG_ENABLE(selftest,
AS_HELP_STRING([--enable-selftest],
[Run extra tests with "make check" ]

View File

@ -64,7 +64,8 @@ CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
hdr->cmsg_level = SOL_SOCKET;
hdr->cmsg_type = SCM_RIGHTS;
hdr->cmsg_len = CMSG_LEN(sizeof(int));
*((int*) CMSG_DATA(hdr)) = fds[1];
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
*p = fds[1];
HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
sys_close(fds[1]);

View File

@ -59,6 +59,8 @@
#include "common/linux/linux_libc_support.h"
#include "common/linux/linux_syscall_support.h"
static const char kMappedFileUnsafePrefix[] = "/dev/";
namespace google_breakpad {
bool AttachThread(pid_t pid) {
@ -81,6 +83,17 @@ bool DetachThread(pid_t pid) {
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
}
inline bool IsMappedFileOpenUnsafe(
const google_breakpad::MappingInfo* mapping) {
// It is unsafe to attempt to open a mapped file that lives under /dev,
// because the semantics of the open may be driver-specific so we'd risk
// hanging the crash dumper. And a file in /dev/ almost certainly has no
// ELF file identifier anyways.
return my_strncmp(mapping->name,
kMappedFileUnsafePrefix,
sizeof(kMappedFileUnsafePrefix) - 1) == 0;
}
bool GetThreadRegisters(ThreadInfo* info) {
pid_t tid = info->tid;
@ -193,7 +206,11 @@ LinuxDumper::ElfFileIdentifierForMapping(unsigned int mapping_id,
uint8_t identifier[sizeof(MDGUID)])
{
assert(mapping_id < mappings_.size());
my_memset(identifier, 0, sizeof(MDGUID));
const MappingInfo* mapping = mappings_[mapping_id];
if (IsMappedFileOpenUnsafe(mapping)) {
return false;
}
int fd = sys_open(mapping->name, O_RDONLY, 0);
if (fd < 0)
return false;

View File

@ -108,16 +108,16 @@ TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
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.
// In the helper program, we stored a pointer to the thread id in a
// specific register. Check that we can recover its value.
#if defined(__ARM_EABI__)
void* process_tid_location = (void *)(one_thread.regs.uregs[11] - 8);
pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]);
#elif defined(__i386)
void* process_tid_location = (void *)(one_thread.regs.ebp - 4);
pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx);
#elif defined(__x86_64)
void* process_tid_location = (void *)(one_thread.regs.rbp - 4);
pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx);
#else
#error Platform not supported!
#error This test has not been ported to this platform.
#endif
pid_t one_thread_id;
dumper.CopyFromProcess(&one_thread_id,

View File

@ -37,13 +37,22 @@
#include <sys/syscall.h>
#include <unistd.h>
#pragma GCC optimize ("O0")
void *thread_function(void *data) __attribute__((noinline, optimize("O2")));
#if defined(__ARM_EABI__)
#define TID_PTR_REGISTER "r3"
#elif defined(__i386)
#define TID_PTR_REGISTER "ecx"
#elif defined(__x86_64)
#define TID_PTR_REGISTER "rcx"
#else
#error This test has not been ported to this platform.
#endif
void *thread_function(void *data) {
pid_t thread_id = syscall(SYS_gettid);
while (true) ;
asm("");
volatile pid_t thread_id = syscall(SYS_gettid);
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
while (true)
asm volatile ("" : : "r" (thread_id_ptr));
return NULL;
}
int main(int argc, char *argv[]) {

View File

@ -28,9 +28,11 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/linux/google_crashdump_uploader.h"
#include "third_party/linux/include/glog/logging.h"
#include "third_party/linux/include/gflags/gflags.h"
#include <string>
#include <iostream>
using std::string;
DEFINE_string(crash_server, "http://clients2.google.com/cr",
"The crash server to upload minidumps to.");
@ -75,7 +77,7 @@ bool CheckForRequiredFlagsOrDie() {
}
if (!error_text.empty()) {
LOG(ERROR) << error_text;
std::cout << error_text;
return false;
}
return true;

View File

@ -29,10 +29,13 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */; };
3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */; };
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; };
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; };
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
D2A5DD401188640400081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; };
F91AF5D00FD60393009D8BE2 /* BreakpadFramework_Test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */; };
F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */; };
@ -98,7 +101,7 @@
F9C44DB60EF07288003AEBAA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB00EF07288003AEBAA /* main.m */; };
F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB10EF07288003AEBAA /* TestClass.mm */; };
F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */; };
F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */; };
F9C44DBD0EF072A0003AEBAA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.xib */; };
F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; };
F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */ = {isa = PBXBuildFile; fileRef = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; };
@ -199,49 +202,49 @@
isa = PBXContainerItemProxy;
containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* crash_report */;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E;
remoteInfo = crash_report;
};
F95BB891101F94AC00AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* dump_syms */;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E;
remoteInfo = dump_syms;
};
F95BB89E101F94C000AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* symupload */;
remoteGlobalIDString = 8DD76FA10486AA7600D96B5E;
remoteInfo = symupload;
};
F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 9BD835FB0B0544950055103E /* minidump_upload */;
remoteGlobalIDString = 9BD835FB0B0544950055103E;
remoteInfo = minidump_upload;
};
F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* dump_syms */;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E;
remoteInfo = dump_syms;
};
F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* symupload */;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E;
remoteInfo = symupload;
};
F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* crash_report */;
remoteGlobalIDString = 8DD76F960486AA7600D96B5E;
remoteInfo = crash_report;
};
F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = {
@ -279,7 +282,7 @@
0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = "<absolute>"; };
1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = "<absolute>"; };
32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = "<group>"; };
3329D4EC0FA16D820007BBC5 /* Breakpad.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Breakpad.nib; path = sender/Breakpad.nib; sourceTree = "<group>"; };
3329D4EC0FA16D820007BBC5 /* Breakpad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Breakpad.xib; path = sender/Breakpad.xib; sourceTree = "<group>"; };
33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@ -353,7 +356,7 @@
F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = "<group>"; };
F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = "<group>"; };
F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = testapp/English.lproj/MainMenu.nib; sourceTree = "<group>"; };
F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = testapp/English.lproj/MainMenu.xib; sourceTree = "<group>"; };
F9C44DBF0EF0778F003AEBAA /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = testapp/Controller.h; sourceTree = "<group>"; };
F9C44DC00EF0778F003AEBAA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClass.h; path = testapp/TestClass.h; sourceTree = "<group>"; };
F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = "<absolute>"; };
@ -598,7 +601,7 @@
F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */,
F945849C0F280E3C009A47BF /* Localizable.strings */,
33880C7E0F9E097100817F82 /* InfoPlist.strings */,
3329D4EC0FA16D820007BBC5 /* Breakpad.nib */,
3329D4EC0FA16D820007BBC5 /* Breakpad.xib */,
4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */,
F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */,
);
@ -646,7 +649,7 @@
F9C44DBF0EF0778F003AEBAA /* Controller.h */,
F9C44DC00EF0778F003AEBAA /* TestClass.h */,
F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */,
F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */,
F9C44DBA0EF072A0003AEBAA /* MainMenu.xib */,
F9C44DAC0EF07288003AEBAA /* Controller.m */,
F9C44DAD0EF07288003AEBAA /* crashduringload */,
F9C44DAE0EF07288003AEBAA /* crashInMain */,
@ -951,7 +954,7 @@
F945849E0F280E3C009A47BF /* Localizable.strings in Resources */,
4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */,
33880C800F9E097100817F82 /* InfoPlist.strings in Resources */,
3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */,
3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */,
F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -963,7 +966,7 @@
F9C44DB30EF07288003AEBAA /* crashduringload in Resources */,
F9C44DB40EF07288003AEBAA /* crashInMain in Resources */,
F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */,
F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */,
F9C44DBD0EF072A0003AEBAA /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1045,6 +1048,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */,
F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */,
F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */,
F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */,
@ -1074,6 +1078,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D2A5DD401188640400081F03 /* breakpad_nlist_64.cc in Sources */,
F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */,
F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */,
F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */,
@ -1104,6 +1109,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */,
F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */,
F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */,
F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */,
@ -1255,12 +1261,12 @@
name = InfoPlist.strings;
sourceTree = "<group>";
};
F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */ = {
F9C44DBA0EF072A0003AEBAA /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
F9C44DBB0EF072A0003AEBAA /* English */,
);
name = MainMenu.nib;
name = MainMenu.xib;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
@ -1314,7 +1320,7 @@
GCC_OPTIMIZATION_LEVEL = 0;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH)";
ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH_ACTUAL)";
PREBINDING = NO;
SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
};

View File

@ -36,7 +36,6 @@
#include <mach-o/loader.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#include <mach/mach_vm.h>
#include <CoreFoundation/CoreFoundation.h>
@ -50,6 +49,12 @@ using MacStringUtils::IntegerValueAtIndex;
namespace google_breakpad {
#if __LP64__
#define LC_SEGMENT_ARCH LC_SEGMENT_64
#else
#define LC_SEGMENT_ARCH LC_SEGMENT
#endif
// constructor when generating from within the crashed process
MinidumpGenerator::MinidumpGenerator()
: exception_type_(0),
@ -615,8 +620,10 @@ bool MinidumpGenerator::WriteSystemInfoStream(
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC;
break;
case CPU_TYPE_I386:
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
case CPU_TYPE_X86_64:
// hw.cputype is currently always I386 even on an x86-64 system
#ifdef __i386__
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86;
// ebx is used for PIC code, so we need
// to preserve it.
#define cpuid(op,eax,ebx,ecx,edx) \
@ -629,6 +636,18 @@ bool MinidumpGenerator::WriteSystemInfoStream(
"=c" (ecx), \
"=d" (edx) \
: "0" (op))
#elif defined(__x86_64__)
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64;
#define cpuid(op,eax,ebx,ecx,edx) \
asm ("cpuid \n\t" \
: "=a" (eax), \
"=b" (ebx), \
"=c" (ecx), \
"=d" (edx) \
: "0" (op))
#endif
#if defined(__i386__) || defined(__x86_64__)
int unused, unused2;
// get vendor id
cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0],
@ -659,7 +678,7 @@ bool MinidumpGenerator::WriteSystemInfoStream(
((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20);
}
#endif // __i386__
#endif // __i386__ || __x86_64_
break;
default:
info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN;
@ -763,7 +782,7 @@ bool MinidumpGenerator::WriteModuleStream(unsigned int index,
memset(module, 0, sizeof(MDRawModule));
for (unsigned int i = 0; cmd && (i < header->ncmds); i++) {
if (cmd->cmd == LC_SEGMENT) {
if (cmd->cmd == LC_SEGMENT_ARCH) {
const breakpad_mach_segment_command *seg =
reinterpret_cast<const breakpad_mach_segment_command *>(cmd);

View File

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>CLASS</key>
<string>LengthLimitingTextField</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSTextField</string>
</dict>
<dict>
<key>ACTIONS</key>
<dict>
<key>cancel</key>
<string>id</string>
<key>sendReport</key>
<string>id</string>
<key>showPrivacyPolicy</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Reporter</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>alertWindow_</key>
<string>NSWindow</string>
<key>cancelButton_</key>
<string>NSButton</string>
<key>commentMessage_</key>
<string>NSTextField</string>
<key>commentsEntryField_</key>
<string>LengthLimitingTextField</string>
<key>countdownLabel_</key>
<string>NSTextField</string>
<key>dialogTitle_</key>
<string>NSTextField</string>
<key>emailEntryField_</key>
<string>LengthLimitingTextField</string>
<key>emailLabel_</key>
<string>NSTextField</string>
<key>emailMessage_</key>
<string>NSTextField</string>
<key>emailSectionBox_</key>
<string>NSBox</string>
<key>headerBox_</key>
<string>NSBox</string>
<key>preEmailBox_</key>
<string>NSBox</string>
<key>privacyLinkArrow_</key>
<string>NSView</string>
<key>privacyLinkLabel_</key>
<string>NSTextField</string>
<key>sendButton_</key>
<string>NSButton</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>676</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../Breakpad.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>132</integer>
</array>
<key>IBSystem Version</key>
<string>9J61</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

File diff suppressed because it is too large Load Diff

View File

@ -1,47 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBClasses</key>
<array>
<dict>
<key>ACTIONS</key>
<dict>
<key>crash</key>
<string>id</string>
<key>forkTestGo</key>
<string>id</string>
<key>forkTestOptions</key>
<string>id</string>
<key>generateReportWithoutCrash</key>
<string>id</string>
<key>showForkTestWindow</key>
<string>id</string>
</dict>
<key>CLASS</key>
<string>Controller</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>OUTLETS</key>
<dict>
<key>forkTestOptions_</key>
<string>NSWindow</string>
<key>window_</key>
<string>NSWindow</string>
</dict>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
<dict>
<key>CLASS</key>
<string>FirstResponder</string>
<key>LANGUAGE</key>
<string>ObjC</string>
<key>SUPERCLASS</key>
<string>NSObject</string>
</dict>
</array>
<key>IBVersion</key>
<string>1</string>
</dict>
</plist>

View File

@ -1,22 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IBFramework Version</key>
<string>670</string>
<key>IBLastKnownRelativeProjectPath</key>
<string>../GoogleBreakpadTest.xcodeproj</string>
<key>IBOldestOS</key>
<integer>5</integer>
<key>IBOpenObjects</key>
<array>
<integer>221</integer>
<integer>29</integer>
<integer>2</integer>
</array>
<key>IBSystem Version</key>
<string>9F33</string>
<key>targetFramework</key>
<string>IBCocoaFramework</string>
</dict>
</plist>

View File

@ -68,7 +68,9 @@ bool MinidumpFileWriter::Close() {
bool result = true;
if (file_ != -1) {
ftruncate(file_, position_);
if (-1 == ftruncate(file_, position_)) {
return false;
}
#if __linux__
result = (sys_close(file_) == 0);
#else

View File

@ -0,0 +1,65 @@
# 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.
{
'includes': [
'build/common.gypi',
],
'targets': [
{
'target_name': 'build_all',
'type': 'none',
'dependencies': [
'./crash_generation/crash_generation.gyp:*',
'./handler/exception_handler.gyp:*',
'./sender/crash_report_sender.gyp:*',
'./unittests/client_tests.gyp:*',
'./unittests/gtest.gyp:*',
]
},
{
'target_name': 'common',
'type': 'static_library',
'include_dirs': [
'<(DEPTH)',
],
'direct_dependent_settings': {
'include_dirs': [
'<(DEPTH)',
]
},
'sources': [
'<(DEPTH)/common/windows/guid_string.cc',
'<(DEPTH)/common/windows/guid_string.h',
'<(DEPTH)/common/windows/http_upload.cc',
'<(DEPTH)/common/windows/http_upload.h',
]
}
]
}

View File

@ -1,59 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler", "handler\exception_handler.vcproj", "{B55CA863-B374-4BAF-95AC-539E4FA4C90C}"
ProjectSection(ProjectDependencies) = postProject
{A820AF62-6239-4693-8430-4F516C1838F4} = {A820AF62-6239-4693-8430-4F516C1838F4}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_report_sender", "sender\crash_report_sender.vcproj", "{9946A048-043B-4F8F-9E07-9297B204714C}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_generation", "crash_generation\crash_generation.vcproj", "{A820AF62-6239-4693-8430-4F516C1838F4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler_test", "handler\exception_handler_test\exception_handler_test.vcproj", "{89094A11-CF25-4037-AF43-EACFA751405E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
DebugStaticCRT|Win32 = DebugStaticCRT|Win32
Release|Win32 = Release|Win32
ReleaseStaticCRT|Win32 = ReleaseStaticCRT|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Debug|Win32.ActiveCfg = Debug|Win32
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Debug|Win32.Build.0 = Debug|Win32
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.DebugStaticCRT|Win32.ActiveCfg = DebugStaticCRT|Win32
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.DebugStaticCRT|Win32.Build.0 = DebugStaticCRT|Win32
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Release|Win32.ActiveCfg = Release|Win32
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.Release|Win32.Build.0 = Release|Win32
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32
{B55CA863-B374-4BAF-95AC-539E4FA4C90C}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.Debug|Win32.ActiveCfg = Debug|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.Debug|Win32.Build.0 = Debug|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.DebugStaticCRT|Win32.ActiveCfg = DebugStaticCRT|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.DebugStaticCRT|Win32.Build.0 = DebugStaticCRT|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.Release|Win32.ActiveCfg = Release|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.Release|Win32.Build.0 = Release|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32
{9946A048-043B-4F8F-9E07-9297B204714C}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.Debug|Win32.ActiveCfg = Debug|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.Debug|Win32.Build.0 = Debug|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.DebugStaticCRT|Win32.ActiveCfg = DebugStaticCRT|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.DebugStaticCRT|Win32.Build.0 = DebugStaticCRT|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.ActiveCfg = Release|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.Build.0 = Release|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32
{A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.ActiveCfg = Debug|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.Build.0 = Debug|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.ActiveCfg = Debug|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.Build.0 = Debug|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.ActiveCfg = Release|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.Build.0 = Release|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.ActiveCfg = Release|Win32
{89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
# 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.
{
'conditions': [
[ 'OS=="linux"', {
'target_defaults': {
'cflags!': [
'-Wall',
'-Werror',
],
},
}],
[ 'OS=="win"', {
'target_defaults': {
'defines': [
'_CRT_SECURE_NO_DEPRECATE',
'_CRT_NONSTDC_NO_WARNINGS',
'_CRT_NONSTDC_NO_DEPRECATE',
'_SCL_SECURE_NO_DEPRECATE',
],
'msvs_disabled_warnings': [4800],
'msvs_settings': {
'VCCLCompilerTool': {
'WarnAsError': 'false',
'Detect64BitPortabilityProblems': 'false',
},
},
},
}],
[ 'OS=="mac"', {
'target_defaults': {
'xcode_settings': {
'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO',
'WARNING_CFLAGS!': ['-Wall'],
},
},
}],
],
}
# Local Variables:
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:

View File

@ -0,0 +1,15 @@
{
'msvs_settings': {
'VCCLCompilerTool': {
'Optimizations': '2',
'StringPooling': 'true',
'OmitFramePointers': 'true',
},
'VCLinkerTool': {
'LinkIncremental': '1',
'OptimizeReferences': '2',
'EnableCOMDATFolding': '2',
'OptimizeForWindows98': '1',
},
},
}

View File

@ -0,0 +1,3 @@
{
'includes': ['release_defaults.gypi'],
}

View File

@ -0,0 +1,21 @@
{
'includes': ['release_defaults.gypi'],
'defines': ['OFFICIAL_BUILD'],
'msvs_settings': {
'VCCLCompilerTool': {
'Optimization': '3',
'InlineFunctionExpansion': '2',
'EnableIntrinsicFunctions': 'true',
'FavorSizeOrSpeed': '2',
'OmitFramePointers': 'true',
'EnableFiberSafeOptimizations': 'true',
'WholeProgramOptimization': 'true',
},
'VCLibrarianTool': {
'AdditionalOptions': ['/ltcg', '/expectedoutputsize:120000000'],
},
'VCLinkerTool': {
'LinkTimeCodeGeneration': '1',
},
},
}

View File

@ -0,0 +1,19 @@
{
'conditions': [
# Handle build types.
['buildtype=="Dev"', {
'includes': ['internal/release_impl.gypi'],
}],
['buildtype=="Official"', {
'includes': ['internal/release_impl_official.gypi'],
}],
# TODO(bradnelson): may also need:
# checksenabled
# coverage
# dom_stats
# pgo_instrument
# pgo_optimize
# purify
],
}

View File

@ -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.
{
'includes': [
'../build/common.gypi',
],
'targets': [
{
'target_name': 'crash_generation_server',
'type': '<(library)',
'sources': [
'client_info.cc',
'crash_generation_server.cc',
'minidump_generator.cc',
'client_info.h',
'crash_generation_client.h',
'crash_generation_server.h',
'minidump_generator.h',
],
'dependencies': [
'../breakpad_client.gyp:common'
],
},
{
'target_name': 'crash_generation_client',
'type': '<(library)',
'include_dirs': [
'<(DEPTH)',
],
'sources': [
'crash_generation_client.h',
'crash_generation_client.cc',
'crash_generation_server.h',
],
},
],
}

View File

@ -1,347 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="crash_generation"
ProjectGUID="{A820AF62-6239-4693-8430-4F516C1838F4}"
RootNamespace="CrashGenerationServer"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="DebugStaticCRT|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="ReleaseStaticCRT|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\client_info.cc"
>
</File>
<File
RelativePath=".\crash_generation_client.cc"
>
</File>
<File
RelativePath=".\crash_generation_server.cc"
>
</File>
<File
RelativePath="..\..\..\common\windows\guid_string.cc"
>
</File>
<File
RelativePath=".\minidump_generator.cc"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\common\auto_critical_section.h"
>
</File>
<File
RelativePath=".\client_info.h"
>
</File>
<File
RelativePath=".\crash_generation_client.h"
>
</File>
<File
RelativePath=".\crash_generation_server.h"
>
</File>
<File
RelativePath="..\common\ipc_protocol.h"
>
</File>
<File
RelativePath="..\..\..\google_breakpad\common\minidump_format.h"
>
</File>
<File
RelativePath=".\minidump_generator.h"
>
</File>
<File
RelativePath="..\..\..\common\windows\string_utils-inl.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<File
RelativePath=".\ReadMe.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -271,7 +271,8 @@ bool CrashGenerationClient::IsRegistered() const {
return crash_event_ != NULL;
}
bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info,
MDRawAssertionInfo* assert_info) {
if (!IsRegistered()) {
return false;
}
@ -279,33 +280,23 @@ bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
exception_pointers_ = ex_info;
thread_id_ = GetCurrentThreadId();
assert_info_.line = 0;
assert_info_.type = 0;
assert_info_.expression[0] = 0;
assert_info_.file[0] = 0;
assert_info_.function[0] = 0;
return SignalCrashEventAndWait();
}
bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
if (!IsRegistered()) {
return false;
}
exception_pointers_ = NULL;
if (assert_info) {
memcpy(&assert_info_, assert_info, sizeof(assert_info_));
} else {
memset(&assert_info_, 0, sizeof(assert_info_));
}
thread_id_ = GetCurrentThreadId();
return SignalCrashEventAndWait();
}
bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
return RequestDump(ex_info, NULL);
}
bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
return RequestDump(NULL, assert_info);
}
bool CrashGenerationClient::SignalCrashEventAndWait() {
assert(crash_event_);
assert(crash_generated_);

View File

@ -27,8 +27,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.
#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__
#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__
#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#include <windows.h>
#include <dbghelp.h>
@ -73,6 +73,9 @@ class CrashGenerationClient {
// Returns true if the registration is successful; false otherwise.
bool Register();
bool RequestDump(EXCEPTION_POINTERS* ex_info,
MDRawAssertionInfo* assert_info);
// Requests the crash server to generate a dump with the given
// exception information.
//
@ -156,4 +159,4 @@ class CrashGenerationClient {
} // namespace google_breakpad
#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H__
#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_

View File

@ -68,6 +68,23 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
MINIDUMP_TYPE dump_type,
bool is_client_pointers,
wstring* dump_path) {
// Just call the full WriteMinidump with NULL as the full_dump_path.
return this->WriteMinidump(process_handle, process_id, thread_id,
requesting_thread_id, exception_pointers,
assert_info, dump_type, is_client_pointers,
dump_path, NULL);
}
bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
DWORD process_id,
DWORD thread_id,
DWORD requesting_thread_id,
EXCEPTION_POINTERS* exception_pointers,
MDRawAssertionInfo* assert_info,
MINIDUMP_TYPE dump_type,
bool is_client_pointers,
wstring* dump_path,
wstring* full_dump_path) {
MiniDumpWriteDumpType write_dump = GetWriteDump();
if (!write_dump) {
return false;
@ -223,6 +240,9 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
if (result && dump_path) {
*dump_path = dump_file_path;
}
if (result && full_memory_dump && full_dump_path) {
*full_dump_path = full_dump_file_path;
}
return result;
}
@ -275,7 +295,7 @@ bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
UUID id = {0};
UuidCreateType create_uuid = GetCreateUuid();
if(!create_uuid) {
if (!create_uuid) {
return false;
}

View File

@ -27,8 +27,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.
#ifndef CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__
#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__
#ifndef CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_
#include <windows.h>
#include <dbghelp.h>
@ -61,6 +61,20 @@ class MinidumpGenerator {
bool is_client_pointers,
std::wstring* dump_path);
// Writes the minidump with the given parameters. Stores the dump file
// path in the dump_path (and full_dump_path) parameter if dump
// generation succeeds. full_dump_path and dump_path can be NULL.
bool WriteMinidump(HANDLE process_handle,
DWORD process_id,
DWORD thread_id,
DWORD requesting_thread_id,
EXCEPTION_POINTERS* exception_pointers,
MDRawAssertionInfo* assert_info,
MINIDUMP_TYPE dump_type,
bool is_client_pointers,
std::wstring* dump_path,
std::wstring* full_dump_path);
private:
// Function pointer type for MiniDumpWriteDump, which is looked up
// dynamically.
@ -118,4 +132,4 @@ class MinidumpGenerator {
} // namespace google_breakpad
#endif // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATION_H__
#endif // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_

View File

@ -27,12 +27,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.
#include <assert.h>
#include <ObjBase.h>
#include <winternl.h>
#include <psapi.h>
#include <cassert>
#include <cstdio>
#include <stdio.h>
#include <winternl.h>
#include "common/windows/string_utils-inl.h"
@ -430,7 +429,7 @@ class AutoExceptionHandler {
AutoExceptionHandler() {
// Increment handler_stack_index_ so that if another Breakpad handler is
// registered using this same HandleException function, and it needs to be
// called while this handler is running (either becaause this handler
// called while this handler is running (either because this handler
// declines to handle the exception, or an exception occurs during
// handling), HandleException will find the appropriate ExceptionHandler
// object in handler_stack_ to deliver the exception to.
@ -448,7 +447,6 @@ class AutoExceptionHandler {
handler_ = ExceptionHandler::handler_stack_->at(
ExceptionHandler::handler_stack_->size() -
++ExceptionHandler::handler_stack_index_);
LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
// In case another exception occurs while this handler is doing its thing,
// it should be delivered to the previous filter.
@ -467,7 +465,6 @@ class AutoExceptionHandler {
#endif // _MSC_VER >= 1400
_set_purecall_handler(ExceptionHandler::HandlePureVirtualCall);
EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
--ExceptionHandler::handler_stack_index_;
LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_);
}
@ -567,16 +564,37 @@ void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression,
assertion.line = line;
assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER;
// Make up an exception record for the current thread and CPU context
// to make it possible for the crash processor to classify these
// as do regular crashes, and to make it humane for developers to
// analyze them.
EXCEPTION_RECORD exception_record = {};
CONTEXT exception_context = {};
EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
RtlCaptureContext(&exception_context);
exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
// We store pointers to the the expression and function strings,
// and the line as exception parameters to make them easy to
// access by the developer on the far side.
exception_record.NumberParameters = 3;
exception_record.ExceptionInformation[0] =
reinterpret_cast<ULONG_PTR>(&assertion.expression);
exception_record.ExceptionInformation[1] =
reinterpret_cast<ULONG_PTR>(&assertion.file);
exception_record.ExceptionInformation[2] = assertion.line;
bool success = false;
// In case of out-of-process dump generation, directly call
// WriteMinidumpWithException since there is no separate thread running.
if (current_handler->IsOutOfProcess()) {
success = current_handler->WriteMinidumpWithException(
GetCurrentThreadId(),
NULL,
&exception_ptrs,
&assertion);
} else {
success = current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion);
success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs,
&assertion);
}
if (!success) {
@ -615,6 +633,8 @@ void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression,
// static
void ExceptionHandler::HandlePureVirtualCall() {
// This is an pure virtual funciton call, not an exception. It's safe to
// play with sprintf here.
AutoExceptionHandler auto_exception_handler;
ExceptionHandler* current_handler = auto_exception_handler.get_handler();
@ -622,6 +642,26 @@ void ExceptionHandler::HandlePureVirtualCall() {
memset(&assertion, 0, sizeof(assertion));
assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL;
// Make up an exception record for the current thread and CPU context
// to make it possible for the crash processor to classify these
// as do regular crashes, and to make it humane for developers to
// analyze them.
EXCEPTION_RECORD exception_record = {};
CONTEXT exception_context = {};
EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context };
RtlCaptureContext(&exception_context);
exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION;
// We store pointers to the the expression and function strings,
// and the line as exception parameters to make them easy to
// access by the developer on the far side.
exception_record.NumberParameters = 3;
exception_record.ExceptionInformation[0] =
reinterpret_cast<ULONG_PTR>(&assertion.expression);
exception_record.ExceptionInformation[1] =
reinterpret_cast<ULONG_PTR>(&assertion.file);
exception_record.ExceptionInformation[2] = assertion.line;
bool success = false;
// In case of out-of-process dump generation, directly call
// WriteMinidumpWithException since there is no separate thread running.
@ -629,10 +669,11 @@ void ExceptionHandler::HandlePureVirtualCall() {
if (current_handler->IsOutOfProcess()) {
success = current_handler->WriteMinidumpWithException(
GetCurrentThreadId(),
NULL,
&exception_ptrs,
&assertion);
} else {
success = current_handler->WriteMinidumpOnHandlerThread(NULL, &assertion);
success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs,
&assertion);
}
if (!success) {
@ -823,13 +864,7 @@ bool ExceptionHandler::WriteMinidumpWithException(
bool success = false;
if (IsOutOfProcess()) {
// Use the EXCEPTION_POINTERS overload for RequestDump if
// both exinfo and assertion are NULL.
if (!assertion) {
success = crash_generation_client_->RequestDump(exinfo);
} else {
success = crash_generation_client_->RequestDump(assertion);
}
success = crash_generation_client_->RequestDump(exinfo, assertion);
} else {
success = WriteMinidumpWithExceptionForProcess(requesting_thread_id,
exinfo,

View File

@ -0,0 +1,48 @@
# 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.
{
'includes': [
'../build/common.gypi',
],
'targets': [
{
'target_name': 'exception_handler',
'type': '<(library)',
'sources': [
"exception_handler.cc",
"exception_handler.h",
],
'dependencies': [
'../breakpad_client.gyp:common',
'../crash_generation/crash_generation.gyp:crash_generation_server',
]
},
],
}

View File

@ -1,323 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="exception_handler"
ProjectGUID="{B55CA863-B374-4BAF-95AC-539E4FA4C90C}"
RootNamespace="exception_handler"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
AdditionalDependencies="crash_generation.lib"
AdditionalLibraryDirectories="..\$(ConfigurationName)"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
AdditionalDependencies="crash_generation.lib"
AdditionalLibraryDirectories="..\$(ConfigurationName)"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="DebugStaticCRT|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
AdditionalDependencies="crash_generation.lib"
AdditionalLibraryDirectories="..\$(ConfigurationName)"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="ReleaseStaticCRT|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
AdditionalDependencies="crash_generation.lib"
AdditionalLibraryDirectories="..\$(ConfigurationName)"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\exception_handler.cc"
>
</File>
<File
RelativePath="..\..\..\common\windows\guid_string.cc"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\exception_handler.h"
>
</File>
<File
RelativePath="..\..\..\common\windows\guid_string.h"
>
</File>
<File
RelativePath="..\..\..\google_breakpad\common\minidump_format.h"
>
</File>
<File
RelativePath="..\..\..\common\windows\string_utils-inl.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -1,266 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="exception_handler_test"
ProjectGUID="{89094A11-CF25-4037-AF43-EACFA751405E}"
RootNamespace="exception_handler_test"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
Description=""
CommandLine=""
Outputs=""
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\..\..\testing;..\..\..\..\testing\include;..\..\..\..\testing\gtest;..\..\..\..\testing\gtest\include;..\..\..\..\"
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Running tests"
CommandLine="$(OutDir)\$(ProjectName).exe"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\..\..\testing;..\..\..\..\testing\include;..\..\..\..\testing\gtest;..\..\..\..\testing\gtest\include;..\..\..\..\"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0500"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCWebDeploymentTool"
/>
<Tool
Name="VCPostBuildEventTool"
Description="Running tests"
CommandLine="$(OutDir)\$(ProjectName).exe"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath="..\..\crash_generation\client_info.cc"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_client.cc"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_server.cc"
>
</File>
<File
RelativePath="..\exception_handler.cc"
>
</File>
<File
RelativePath=".\exception_handler_test.cc"
>
</File>
<File
RelativePath="..\..\..\..\testing\gtest\src\gtest-all.cc"
>
</File>
<File
RelativePath="..\..\..\..\testing\gtest\src\gtest_main.cc"
>
</File>
<File
RelativePath="..\..\..\..\common\windows\guid_string.cc"
>
</File>
<File
RelativePath="..\..\crash_generation\minidump_generator.cc"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\crash_generation\client_info.h"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_client.h"
>
</File>
<File
RelativePath="..\..\crash_generation\crash_generation_server.h"
>
</File>
<File
RelativePath="..\exception_handler.h"
>
</File>
<File
RelativePath="..\..\..\..\common\windows\guid_string.h"
>
</File>
<File
RelativePath="..\..\crash_generation\minidump_generator.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
<File
RelativePath=".\ReadMe.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,47 @@
# 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.
{
'includes': [
'../build/common.gypi',
],
'targets': [
{
'target_name': 'crash_report_sender',
'type': '<(library)',
'sources': [
'crash_report_sender.cc',
'crash_report_sender.h',
],
'dependencies': [
'../breakpad_client.gyp:common'
],
},
],
}

View File

@ -1,307 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="8.00"
Name="crash_report_sender"
ProjectGUID="{9946A048-043B-4F8F-9E07-9297B204714C}"
RootNamespace="crash_report_sender"
Keyword="Win32Proj"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
UsePrecompiledHeader="0"
WarningLevel="4"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN"
RuntimeLibrary="2"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="DebugStaticCRT|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="ReleaseStaticCRT|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="4"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="..\..\.."
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN"
RuntimeLibrary="0"
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="true"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLibrarianTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\crash_report_sender.cc"
>
</File>
<File
RelativePath="..\..\..\common\windows\http_upload.cc"
>
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath=".\crash_report_sender.h"
>
</File>
<File
RelativePath="..\..\..\common\windows\http_upload.h"
>
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
>
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View File

@ -0,0 +1,54 @@
# 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.
{
'includes': [
'../build/common.gypi',
],
'targets': [
{
'target_name': 'client_tests',
'type': 'executable',
'sources': [
'exception_handler_test.cc',
'exception_handler_death_test.cc',
'minidump_test.cc',
'dump_analysis.cc',
'dump_analysis.h',
],
'dependencies': [
'gtest.gyp:gtest',
'../breakpad_client.gyp:common',
'../crash_generation/crash_generation.gyp:crash_generation_server',
'../crash_generation/crash_generation.gyp:crash_generation_client',
'../handler/exception_handler.gyp:exception_handler',
]
},
],
}

View File

@ -0,0 +1,184 @@
// 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.
#include <windows.h>
#include <objbase.h>
#include <dbghelp.h>
#include "dump_analysis.h" // NOLINT
#include "gtest/gtest.h"
DumpAnalysis::~DumpAnalysis() {
if (dump_file_view_ != NULL) {
EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_));
::CloseHandle(dump_file_mapping_);
dump_file_mapping_ = NULL;
}
if (dump_file_handle_ != NULL) {
::CloseHandle(dump_file_handle_);
dump_file_handle_ = NULL;
}
}
void DumpAnalysis::EnsureDumpMapped() {
if (dump_file_view_ == NULL) {
dump_file_handle_ = ::CreateFile(dump_file_.c_str(),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
ASSERT_TRUE(dump_file_handle_ != NULL);
ASSERT_TRUE(dump_file_mapping_ == NULL);
dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_,
NULL,
PAGE_READONLY,
0,
0,
NULL);
ASSERT_TRUE(dump_file_mapping_ != NULL);
dump_file_view_ = ::MapViewOfFile(dump_file_mapping_,
FILE_MAP_READ,
0,
0,
0);
ASSERT_TRUE(dump_file_view_ != NULL);
}
}
bool DumpAnalysis::HasTebs() const {
MINIDUMP_THREAD_LIST* thread_list = NULL;
size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
if (thread_list_size > 0 && thread_list != NULL) {
for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) {
if (!HasMemory(thread_list->Threads[i].Teb))
return false;
}
return true;
}
// No thread list, no TEB info.
return false;
}
bool DumpAnalysis::HasPeb() const {
MINIDUMP_THREAD_LIST* thread_list = NULL;
size_t thread_list_size = GetStream(ThreadListStream, &thread_list);
if (thread_list_size > 0 && thread_list != NULL &&
thread_list->NumberOfThreads > 0) {
FakeTEB* teb = NULL;
if (!HasMemory(thread_list->Threads[0].Teb, &teb))
return false;
return HasMemory(teb->peb);
}
return false;
}
bool DumpAnalysis::HasStream(ULONG stream_number) const {
void* stream = NULL;
size_t stream_size = GetStreamImpl(stream_number, &stream);
return stream_size > 0 && stream != NULL;
}
size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const {
MINIDUMP_DIRECTORY* directory = NULL;
ULONG memory_list_size = 0;
BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_,
stream_number,
&directory,
stream,
&memory_list_size);
return ret ? memory_list_size : 0;
}
bool DumpAnalysis::HasMemoryImpl(const void *addr_in, size_t structuresize,
void **structure) const {
uintptr_t address = reinterpret_cast<uintptr_t>(addr_in);
MINIDUMP_MEMORY_LIST* memory_list = NULL;
size_t memory_list_size = GetStream(MemoryListStream, &memory_list);
if (memory_list_size > 0 && memory_list != NULL) {
for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) {
MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i];
const uintptr_t range_start =
static_cast<uintptr_t>(descr.StartOfMemoryRange);
uintptr_t range_end = range_start + descr.Memory.DataSize;
if (address >= range_start &&
address + structuresize < range_end) {
// The start address falls in the range, and the end address is
// in bounds, return a pointer to the structure if requested.
if (structure != NULL)
*structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva);
return true;
}
}
}
// We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this
// is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the
// end of the dump file.
MINIDUMP_MEMORY64_LIST* memory64_list = NULL;
memory_list_size = GetStream(Memory64ListStream, &memory64_list);
if (memory_list_size > 0 && memory64_list != NULL) {
// Keep track of where the current descriptor maps to.
RVA64 curr_rva = memory64_list->BaseRva;
for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) {
MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i];
uintptr_t range_start =
static_cast<uintptr_t>(descr.StartOfMemoryRange);
uintptr_t range_end = range_start + static_cast<size_t>(descr.DataSize);
if (address >= range_start &&
address + structuresize < range_end) {
// The start address falls in the range, and the end address is
// in bounds, return a pointer to the structure if requested.
if (structure != NULL)
*structure = RVA_TO_ADDR(dump_file_view_, curr_rva);
return true;
}
// Advance the current RVA.
curr_rva += descr.DataSize;
}
}
return false;
}

View File

@ -0,0 +1,102 @@
// 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.
#ifndef CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
#define CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_
#include "../crash_generation/minidump_generator.h"
// Convenience to get to the PEB pointer in a TEB.
struct FakeTEB {
char dummy[0x30];
void* peb;
};
class DumpAnalysis {
public:
explicit DumpAnalysis(const std::wstring& file_path)
: dump_file_(file_path), dump_file_view_(NULL), dump_file_mapping_(NULL),
dump_file_handle_(NULL) {
EnsureDumpMapped();
}
~DumpAnalysis();
bool HasStream(ULONG stream_number) const;
// This is template to keep type safety in the front, but we end up casting
// to void** inside the implementation to pass the pointer to Win32. So
// casting here is considered safe.
template <class StreamType>
size_t GetStream(ULONG stream_number, StreamType** stream) const {
return GetStreamImpl(stream_number, reinterpret_cast<void**>(stream));
}
bool HasTebs() const;
bool HasPeb() const;
bool HasMemory(ULONG64 address) const {
return HasMemory<BYTE>(address, NULL);
}
bool HasMemory(const void* address) const {
return HasMemory<BYTE>(address, NULL);
}
template <class StructureType>
bool HasMemory(ULONG64 address, StructureType** structure = NULL) const {
// We can't cope with 64 bit addresses for now.
if (address > 0xFFFFFFFFUL)
return false;
return HasMemory(reinterpret_cast<void*>(address), structure);
}
template <class StructureType>
bool HasMemory(const void* addr_in, StructureType** structure = NULL) const {
return HasMemoryImpl(addr_in, sizeof(StructureType),
reinterpret_cast<void**>(structure));
}
protected:
void EnsureDumpMapped();
HANDLE dump_file_mapping_;
HANDLE dump_file_handle_;
void* dump_file_view_;
std::wstring dump_file_;
private:
// This is the implementation of GetStream<>.
size_t GetStreamImpl(ULONG stream_number, void** stream) const;
// This is the implementation of HasMemory<>.
bool HasMemoryImpl(const void* addr_in, size_t pointersize,
void** structure) const;
};
#endif // CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_

View File

@ -27,15 +27,16 @@
// (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 "breakpad_googletest_includes.h"
#include "client/windows/crash_generation/crash_generation_server.h"
#include "client/windows/handler/exception_handler.h"
#include <windows.h>
#include <dbghelp.h>
#include <strsafe.h>
#include <objbase.h>
#include <shellapi.h>
#include "../../../breakpad_googletest_includes.h"
#include "../crash_generation/crash_generation_server.h"
#include "../handler/exception_handler.h"
namespace {
const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
const char kSuccessIndicator[] = "success";
@ -45,14 +46,15 @@ const char kFailureIndicator[] = "failure";
BOOL DoesPathExist(const TCHAR *path_name);
class ExceptionHandlerDeathTest : public ::testing::Test {
protected:
protected:
// Member variable for each test that they can use
// for temporary storage.
TCHAR temp_path_[MAX_PATH];
// Actually constructs a temp path name.
virtual void SetUp();
// A helper method that tests can use to crash.
void DoCrash();
void DoCrashAccessViolation();
void DoCrashPureVirtualCall();
};
void ExceptionHandlerDeathTest::SetUp() {
@ -61,12 +63,14 @@ void ExceptionHandlerDeathTest::SetUp() {
TCHAR temp_path[MAX_PATH] = { '\0' };
TCHAR test_name_wide[MAX_PATH] = { '\0' };
// We want the temporary directory to be what the OS returns
// to us, + the test case name.
// to us, + the test case name.
GetTempPath(MAX_PATH, temp_path);
// THe test case name is exposed to use as a c-style string,
// But we might be working in UNICODE here on Windows.
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
(int)strlen(test_info->name()), test_name_wide, MAX_PATH);
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
strlen(test_info->name()),
test_name_wide,
MAX_PATH);
if (!dwRet) {
assert(false);
}
@ -82,7 +86,7 @@ BOOL DoesPathExist(const TCHAR *path_name) {
return TRUE;
}
bool MinidumpWrittenCallback(const wchar_t* dump_path,
bool MinidumpWrittenCallback(const wchar_t* dump_path,
const wchar_t* minidump_id,
void* context,
EXCEPTION_POINTERS* exinfo,
@ -106,11 +110,11 @@ TEST_F(ExceptionHandlerDeathTest, InProcTest) {
// the semantics of the exception handler being inherited/not
// inherited across CreateProcess().
ASSERT_TRUE(DoesPathExist(temp_path_));
google_breakpad::ExceptionHandler *exc =
google_breakpad::ExceptionHandler *exc =
new google_breakpad::ExceptionHandler(
temp_path_, NULL, &MinidumpWrittenCallback, NULL,
temp_path_, NULL, &MinidumpWrittenCallback, NULL,
google_breakpad::ExceptionHandler::HANDLER_ALL);
int *i = NULL;
int *i = NULL;
ASSERT_DEATH((*i)++, kSuccessIndicator);
delete exc;
}
@ -119,19 +123,18 @@ static bool gDumpCallbackCalled = false;
void clientDumpCallback(void *dump_context,
const google_breakpad::ClientInfo *client_info,
const std::wstring *dump_path){
const std::wstring *dump_path) {
gDumpCallbackCalled = true;
}
void ExceptionHandlerDeathTest::DoCrash() {
google_breakpad::ExceptionHandler *exc =
void ExceptionHandlerDeathTest::DoCrashAccessViolation() {
google_breakpad::ExceptionHandler *exc =
new google_breakpad::ExceptionHandler(
temp_path_, NULL, NULL, NULL,
google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName,
NULL);
// Although this is executing in the child process of the death test,
// if it's not true we'll still get an error rather than the crash
// if it's not true we'll still get an error rather than the crash
// being expected.
ASSERT_TRUE(exc->IsOutOfProcess());
int *i = NULL;
@ -141,7 +144,7 @@ void ExceptionHandlerDeathTest::DoCrash() {
TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) {
// We can take advantage of a detail of google test here to save some
// complexity in testing: when you do a death test, it actually forks.
// So we can make the main test harness the crash generation server,
// So we can make the main test harness the crash generation server,
// and call ASSERT_DEATH on a NULL dereference, it to expecting test
// the out of process scenario, since it's happening in a different
// process! This is different from the above because, above, we pass
@ -152,13 +155,61 @@ TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) {
google_breakpad::CrashGenerationServer server(
kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true,
&dump_path);
// This HAS to be EXPECT_, because when this test case is executed in the
// child process, the server registration will fail due to the named pipe
// being the same.
EXPECT_TRUE(server.Start());
EXPECT_FALSE(gDumpCallbackCalled);
ASSERT_DEATH(this->DoCrash(), "");
ASSERT_DEATH(this->DoCrashAccessViolation(), "");
EXPECT_TRUE(gDumpCallbackCalled);
}
}
TEST_F(ExceptionHandlerDeathTest, InvalidParameterTest) {
using google_breakpad::ExceptionHandler;
ASSERT_TRUE(DoesPathExist(temp_path_));
ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
ExceptionHandler::HANDLER_INVALID_PARAMETER);
// Disable the message box for assertions
_CrtSetReportMode(_CRT_ASSERT, 0);
// Call with a bad argument. The invalid parameter will be swallowed
// and a dump will be generated, the process will exit(0).
ASSERT_EXIT(printf(NULL), ::testing::ExitedWithCode(0), "");
}
struct PureVirtualCallBase {
PureVirtualCallBase() {
// We have to reinterpret so the linker doesn't get confused because the
// method isn't defined.
reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
}
virtual ~PureVirtualCallBase() {}
virtual void PureFunction() const = 0;
};
struct PureVirtualCall : public PureVirtualCallBase {
PureVirtualCall() { PureFunction(); }
virtual void PureFunction() const {}
};
void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() {
PureVirtualCall instance;
}
TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) {
using google_breakpad::ExceptionHandler;
ASSERT_TRUE(DoesPathExist(temp_path_));
ExceptionHandler handler(temp_path_, NULL, NULL, NULL,
ExceptionHandler::HANDLER_PURECALL);
// Disable the message box for assertions
_CrtSetReportMode(_CRT_ASSERT, 0);
// Calls a pure virtual function.
EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
}
}

View File

@ -0,0 +1,325 @@
// Copyright 2009, 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.
#include <windows.h>
#include <dbghelp.h>
#include <strsafe.h>
#include <objbase.h>
#include <shellapi.h>
#include "../../../breakpad_googletest_includes.h"
#include "../crash_generation/crash_generation_server.h"
#include "../handler/exception_handler.h"
#include "dump_analysis.h" // NOLINT
namespace {
const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer";
const char kSuccessIndicator[] = "success";
const char kFailureIndicator[] = "failure";
const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
MiniDumpWithFullMemory | // Full memory from process.
MiniDumpWithProcessThreadData | // Get PEB and TEB.
MiniDumpWithHandleData); // Get all handle information.
class ExceptionHandlerTest : public ::testing::Test {
protected:
// Member variable for each test that they can use
// for temporary storage.
TCHAR temp_path_[MAX_PATH];
// Actually constructs a temp path name.
virtual void SetUp();
// Deletes temporary files.
virtual void TearDown();
void DoCrashInvalidParameter();
void DoCrashPureVirtualCall();
// Utility function to test for a path's existence.
static BOOL DoesPathExist(const TCHAR *path_name);
// Client callback.
static void ClientDumpCallback(
void *dump_context,
const google_breakpad::ClientInfo *client_info,
const std::wstring *dump_path);
static std::wstring dump_file;
static std::wstring full_dump_file;
};
std::wstring ExceptionHandlerTest::dump_file;
std::wstring ExceptionHandlerTest::full_dump_file;
void ExceptionHandlerTest::SetUp() {
const ::testing::TestInfo* const test_info =
::testing::UnitTest::GetInstance()->current_test_info();
TCHAR temp_path[MAX_PATH] = { '\0' };
TCHAR test_name_wide[MAX_PATH] = { '\0' };
// We want the temporary directory to be what the OS returns
// to us, + the test case name.
GetTempPath(MAX_PATH, temp_path);
// THe test case name is exposed to use as a c-style string,
// But we might be working in UNICODE here on Windows.
int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(),
strlen(test_info->name()),
test_name_wide,
MAX_PATH);
if (!dwRet) {
assert(false);
}
StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide);
CreateDirectory(temp_path_, NULL);
}
void ExceptionHandlerTest::TearDown() {
if (!dump_file.empty()) {
::DeleteFile(dump_file.c_str());
dump_file = L"";
}
if (!full_dump_file.empty()) {
::DeleteFile(full_dump_file.c_str());
full_dump_file = L"";
}
}
BOOL ExceptionHandlerTest::DoesPathExist(const TCHAR *path_name) {
DWORD flags = GetFileAttributes(path_name);
if (flags == INVALID_FILE_ATTRIBUTES) {
return FALSE;
}
return TRUE;
}
void ExceptionHandlerTest::ClientDumpCallback(
void *dump_context,
const google_breakpad::ClientInfo *client_info,
const std::wstring *dump_path) {
dump_file = *dump_path;
// Create the full dump file name from the dump path.
full_dump_file = dump_file.substr(0, dump_file.length() - 4) + L"-full.dmp";
}
void ExceptionHandlerTest::DoCrashInvalidParameter() {
google_breakpad::ExceptionHandler *exc =
new google_breakpad::ExceptionHandler(
temp_path_, NULL, NULL, NULL,
google_breakpad::ExceptionHandler::HANDLER_INVALID_PARAMETER,
kFullDumpType, kPipeName, NULL);
// Disable the message box for assertions
_CrtSetReportMode(_CRT_ASSERT, 0);
// Although this is executing in the child process of the death test,
// if it's not true we'll still get an error rather than the crash
// being expected.
ASSERT_TRUE(exc->IsOutOfProcess());
printf(NULL);
}
struct PureVirtualCallBase {
PureVirtualCallBase() {
// We have to reinterpret so the linker doesn't get confused because the
// method isn't defined.
reinterpret_cast<PureVirtualCallBase*>(this)->PureFunction();
}
virtual ~PureVirtualCallBase() {}
virtual void PureFunction() const = 0;
};
struct PureVirtualCall : public PureVirtualCallBase {
PureVirtualCall() { PureFunction(); }
virtual void PureFunction() const {}
};
void ExceptionHandlerTest::DoCrashPureVirtualCall() {
google_breakpad::ExceptionHandler *exc =
new google_breakpad::ExceptionHandler(
temp_path_, NULL, NULL, NULL,
google_breakpad::ExceptionHandler::HANDLER_PURECALL,
kFullDumpType, kPipeName, NULL);
// Disable the message box for assertions
_CrtSetReportMode(_CRT_ASSERT, 0);
// Although this is executing in the child process of the death test,
// if it's not true we'll still get an error rather than the crash
// being expected.
ASSERT_TRUE(exc->IsOutOfProcess());
// Create a new frame to ensure PureVirtualCall is not optimized to some
// other line in this function.
{
PureVirtualCall instance;
}
}
// This test validates that the minidump is written correctly.
TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) {
ASSERT_TRUE(DoesPathExist(temp_path_));
// Call with a bad argument
ASSERT_TRUE(DoesPathExist(temp_path_));
std::wstring dump_path(temp_path_);
google_breakpad::CrashGenerationServer server(
kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, true,
&dump_path);
ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
// This HAS to be EXPECT_, because when this test case is executed in the
// child process, the server registration will fail due to the named pipe
// being the same.
EXPECT_TRUE(server.Start());
EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), "");
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
// Verify the dump for infos.
DumpAnalysis mini(dump_file);
DumpAnalysis full(full_dump_file);
// The dump should have all of these streams.
EXPECT_TRUE(mini.HasStream(ThreadListStream));
EXPECT_TRUE(full.HasStream(ThreadListStream));
EXPECT_TRUE(mini.HasStream(ModuleListStream));
EXPECT_TRUE(full.HasStream(ModuleListStream));
EXPECT_TRUE(mini.HasStream(ExceptionStream));
EXPECT_TRUE(full.HasStream(ExceptionStream));
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
EXPECT_TRUE(full.HasStream(SystemInfoStream));
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
EXPECT_TRUE(full.HasStream(MiscInfoStream));
EXPECT_TRUE(mini.HasStream(HandleDataStream));
EXPECT_TRUE(full.HasStream(HandleDataStream));
// We expect PEB and TEBs in this dump.
EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
// Minidump should have a memory listing, but no 64-bit memory.
EXPECT_TRUE(mini.HasStream(MemoryListStream));
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
EXPECT_FALSE(full.HasStream(MemoryListStream));
EXPECT_TRUE(full.HasStream(Memory64ListStream));
// This is the only place we don't use OR because we want both not
// to have the streams.
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
EXPECT_FALSE(full.HasStream(ThreadExListStream));
EXPECT_FALSE(mini.HasStream(CommentStreamA));
EXPECT_FALSE(full.HasStream(CommentStreamA));
EXPECT_FALSE(mini.HasStream(CommentStreamW));
EXPECT_FALSE(full.HasStream(CommentStreamW));
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
EXPECT_FALSE(full.HasStream(FunctionTableStream));
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
EXPECT_FALSE(full.HasStream(HandleOperationListStream));
EXPECT_FALSE(mini.HasStream(TokenStream));
EXPECT_FALSE(full.HasStream(TokenStream));
}
// This test validates that the minidump is written correctly.
TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) {
ASSERT_TRUE(DoesPathExist(temp_path_));
// Call with a bad argument
ASSERT_TRUE(DoesPathExist(temp_path_));
std::wstring dump_path(temp_path_);
google_breakpad::CrashGenerationServer server(
kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, true,
&dump_path);
ASSERT_TRUE(dump_file.empty() && full_dump_file.empty());
// This HAS to be EXPECT_, because when this test case is executed in the
// child process, the server registration will fail due to the named pipe
// being the same.
EXPECT_TRUE(server.Start());
EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), "");
ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty());
ASSERT_TRUE(DoesPathExist(dump_file.c_str()));
// Verify the dump for infos.
DumpAnalysis mini(dump_file);
DumpAnalysis full(full_dump_file);
// The dump should have all of these streams.
EXPECT_TRUE(mini.HasStream(ThreadListStream));
EXPECT_TRUE(full.HasStream(ThreadListStream));
EXPECT_TRUE(mini.HasStream(ModuleListStream));
EXPECT_TRUE(full.HasStream(ModuleListStream));
EXPECT_TRUE(mini.HasStream(ExceptionStream));
EXPECT_TRUE(full.HasStream(ExceptionStream));
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
EXPECT_TRUE(full.HasStream(SystemInfoStream));
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
EXPECT_TRUE(full.HasStream(MiscInfoStream));
EXPECT_TRUE(mini.HasStream(HandleDataStream));
EXPECT_TRUE(full.HasStream(HandleDataStream));
// We expect PEB and TEBs in this dump.
EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
// Minidump should have a memory listing, but no 64-bit memory.
EXPECT_TRUE(mini.HasStream(MemoryListStream));
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
EXPECT_FALSE(full.HasStream(MemoryListStream));
EXPECT_TRUE(full.HasStream(Memory64ListStream));
// This is the only place we don't use OR because we want both not
// to have the streams.
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
EXPECT_FALSE(full.HasStream(ThreadExListStream));
EXPECT_FALSE(mini.HasStream(CommentStreamA));
EXPECT_FALSE(full.HasStream(CommentStreamA));
EXPECT_FALSE(mini.HasStream(CommentStreamW));
EXPECT_FALSE(full.HasStream(CommentStreamW));
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
EXPECT_FALSE(full.HasStream(FunctionTableStream));
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
EXPECT_FALSE(full.HasStream(HandleOperationListStream));
EXPECT_FALSE(mini.HasStream(TokenStream));
EXPECT_FALSE(full.HasStream(TokenStream));
}
}

View File

@ -0,0 +1,57 @@
# 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.
{
'includes': [
'../build/common.gypi',
],
'target_defaults': {
},
'targets': [
{
'target_name': 'gtest',
'type': '<(library)',
'include_dirs': [
'<(DEPTH)/testing/include',
'<(DEPTH)/testing/gtest',
'<(DEPTH)/testing/gtest/include',
],
'sources': [
'<(DEPTH)/testing/gtest/src/gtest-all.cc',
'<(DEPTH)/testing/gtest/src/gtest_main.cc',
],
'direct_dependent_settings': {
'include_dirs': [
'<(DEPTH)/testing/include',
'<(DEPTH)/testing/gtest/include',
]
},
},
],
}

View File

@ -0,0 +1,332 @@
// 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.
#include <windows.h>
#include <objbase.h>
#include <dbghelp.h>
#include "../crash_generation/minidump_generator.h"
#include "dump_analysis.h" // NOLINT
#include "gtest/gtest.h"
namespace {
// Minidump with stacks, PEB, TEB, and unloaded module list.
const MINIDUMP_TYPE kSmallDumpType = static_cast<MINIDUMP_TYPE>(
MiniDumpWithProcessThreadData | // Get PEB and TEB.
MiniDumpWithUnloadedModules); // Get unloaded modules when available.
// Minidump with all of the above, plus memory referenced from stack.
const MINIDUMP_TYPE kLargerDumpType = static_cast<MINIDUMP_TYPE>(
MiniDumpWithProcessThreadData | // Get PEB and TEB.
MiniDumpWithUnloadedModules | // Get unloaded modules when available.
MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack.
// Large dump with all process memory.
const MINIDUMP_TYPE kFullDumpType = static_cast<MINIDUMP_TYPE>(
MiniDumpWithFullMemory | // Full memory from process.
MiniDumpWithProcessThreadData | // Get PEB and TEB.
MiniDumpWithHandleData | // Get all handle information.
MiniDumpWithUnloadedModules); // Get unloaded modules when available.
class MinidumpTest: public testing::Test {
public:
MinidumpTest() {
wchar_t temp_dir_path[ MAX_PATH ] = {0};
::GetTempPath(MAX_PATH, temp_dir_path);
dump_path_ = temp_dir_path;
}
virtual void SetUp() {
// Make sure URLMon isn't loaded into our process.
ASSERT_EQ(NULL, ::GetModuleHandle(L"urlmon.dll"));
// Then load and unload it to ensure we have something to
// stock the unloaded module list with.
HMODULE urlmon = ::LoadLibrary(L"urlmon.dll");
ASSERT_TRUE(urlmon != NULL);
ASSERT_TRUE(::FreeLibrary(urlmon));
}
virtual void TearDown() {
if (!dump_file_.empty()) {
::DeleteFile(dump_file_.c_str());
dump_file_ = L"";
}
if (!full_dump_file_.empty()) {
::DeleteFile(full_dump_file_.c_str());
full_dump_file_ = L"";
}
}
bool WriteDump(ULONG flags) {
using google_breakpad::MinidumpGenerator;
// Fake exception is access violation on write to this.
EXCEPTION_RECORD ex_record = {
STATUS_ACCESS_VIOLATION, // ExceptionCode
0, // ExceptionFlags
NULL, // ExceptionRecord;
reinterpret_cast<void*>(0xCAFEBABE), // ExceptionAddress;
2, // NumberParameters;
{ EXCEPTION_WRITE_FAULT, reinterpret_cast<ULONG_PTR>(this) }
};
CONTEXT ctx_record = {};
EXCEPTION_POINTERS ex_ptrs = {
&ex_record,
&ctx_record,
};
MinidumpGenerator generator(dump_path_);
// And write a dump
bool result = generator.WriteMinidump(::GetCurrentProcess(),
::GetCurrentProcessId(),
::GetCurrentThreadId(),
::GetCurrentThreadId(),
&ex_ptrs,
NULL,
static_cast<MINIDUMP_TYPE>(flags),
TRUE,
&dump_file_,
&full_dump_file_);
return result == TRUE;
}
protected:
std::wstring dump_file_;
std::wstring full_dump_file_;
std::wstring dump_path_;
};
// We need to be able to get file information from Windows
bool HasFileInfo(const std::wstring& file_path) {
DWORD dummy;
const wchar_t* path = file_path.c_str();
DWORD length = ::GetFileVersionInfoSize(path, &dummy);
if (length == 0)
return NULL;
void* data = calloc(length, 1);
if (!data)
return false;
if (!::GetFileVersionInfo(path, dummy, length, data)) {
free(data);
return false;
}
void* translate = NULL;
UINT page_count;
BOOL query_result = VerQueryValue(
data,
L"\\VarFileInfo\\Translation",
static_cast<void**>(&translate),
&page_count);
free(data);
if (query_result && translate) {
return true;
} else {
return false;
}
}
TEST_F(MinidumpTest, Version) {
API_VERSION* version = ::ImagehlpApiVersion();
HMODULE dbg_help = ::GetModuleHandle(L"dbghelp.dll");
ASSERT_TRUE(dbg_help != NULL);
wchar_t dbg_help_file[1024] = {};
ASSERT_TRUE(::GetModuleFileName(dbg_help,
dbg_help_file,
sizeof(dbg_help_file) /
sizeof(*dbg_help_file)));
ASSERT_TRUE(HasFileInfo(std::wstring(dbg_help_file)) != NULL);
// LOG(INFO) << "DbgHelp.dll version: " << file_info->file_version();
}
TEST_F(MinidumpTest, Normal) {
EXPECT_TRUE(WriteDump(MiniDumpNormal));
DumpAnalysis mini(dump_file_);
// We expect threads, modules and some memory.
EXPECT_TRUE(mini.HasStream(ThreadListStream));
EXPECT_TRUE(mini.HasStream(ModuleListStream));
EXPECT_TRUE(mini.HasStream(MemoryListStream));
EXPECT_TRUE(mini.HasStream(ExceptionStream));
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
EXPECT_FALSE(mini.HasStream(CommentStreamA));
EXPECT_FALSE(mini.HasStream(CommentStreamW));
EXPECT_FALSE(mini.HasStream(HandleDataStream));
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
EXPECT_FALSE(mini.HasStream(UnloadedModuleListStream));
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
EXPECT_FALSE(mini.HasStream(TokenStream));
// We expect no PEB nor TEBs in this dump.
EXPECT_FALSE(mini.HasTebs());
EXPECT_FALSE(mini.HasPeb());
// We expect no off-stack memory in this dump.
EXPECT_FALSE(mini.HasMemory(this));
}
TEST_F(MinidumpTest, SmallDump) {
ASSERT_TRUE(WriteDump(kSmallDumpType));
DumpAnalysis mini(dump_file_);
EXPECT_TRUE(mini.HasStream(ThreadListStream));
EXPECT_TRUE(mini.HasStream(ModuleListStream));
EXPECT_TRUE(mini.HasStream(MemoryListStream));
EXPECT_TRUE(mini.HasStream(ExceptionStream));
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
// We expect PEB and TEBs in this dump.
EXPECT_TRUE(mini.HasTebs());
EXPECT_TRUE(mini.HasPeb());
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
EXPECT_FALSE(mini.HasStream(CommentStreamA));
EXPECT_FALSE(mini.HasStream(CommentStreamW));
EXPECT_FALSE(mini.HasStream(HandleDataStream));
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
EXPECT_FALSE(mini.HasStream(TokenStream));
// We expect no off-stack memory in this dump.
EXPECT_FALSE(mini.HasMemory(this));
}
TEST_F(MinidumpTest, LargerDump) {
ASSERT_TRUE(WriteDump(kLargerDumpType));
DumpAnalysis mini(dump_file_);
// The dump should have all of these streams.
EXPECT_TRUE(mini.HasStream(ThreadListStream));
EXPECT_TRUE(mini.HasStream(ModuleListStream));
EXPECT_TRUE(mini.HasStream(MemoryListStream));
EXPECT_TRUE(mini.HasStream(ExceptionStream));
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
// We expect memory referenced by stack in this dump.
EXPECT_TRUE(mini.HasMemory(this));
// We expect PEB and TEBs in this dump.
EXPECT_TRUE(mini.HasTebs());
EXPECT_TRUE(mini.HasPeb());
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
EXPECT_FALSE(mini.HasStream(CommentStreamA));
EXPECT_FALSE(mini.HasStream(CommentStreamW));
EXPECT_FALSE(mini.HasStream(HandleDataStream));
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
EXPECT_FALSE(mini.HasStream(TokenStream));
}
TEST_F(MinidumpTest, FullDump) {
ASSERT_TRUE(WriteDump(kFullDumpType));
ASSERT_TRUE(dump_file_ != L"");
ASSERT_TRUE(full_dump_file_ != L"");
DumpAnalysis mini(dump_file_);
DumpAnalysis full(full_dump_file_);
// Either dumps can contain part of the information.
// The dump should have all of these streams.
EXPECT_TRUE(mini.HasStream(ThreadListStream));
EXPECT_TRUE(full.HasStream(ThreadListStream));
EXPECT_TRUE(mini.HasStream(ModuleListStream));
EXPECT_TRUE(full.HasStream(ModuleListStream));
EXPECT_TRUE(mini.HasStream(ExceptionStream));
EXPECT_TRUE(full.HasStream(ExceptionStream));
EXPECT_TRUE(mini.HasStream(SystemInfoStream));
EXPECT_TRUE(full.HasStream(SystemInfoStream));
EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream));
EXPECT_TRUE(full.HasStream(UnloadedModuleListStream));
EXPECT_TRUE(mini.HasStream(MiscInfoStream));
EXPECT_TRUE(full.HasStream(MiscInfoStream));
EXPECT_TRUE(mini.HasStream(HandleDataStream));
EXPECT_TRUE(full.HasStream(HandleDataStream));
// We expect memory referenced by stack in this dump.
EXPECT_FALSE(mini.HasMemory(this));
EXPECT_TRUE(full.HasMemory(this));
// We expect PEB and TEBs in this dump.
EXPECT_TRUE(mini.HasTebs() || full.HasTebs());
EXPECT_TRUE(mini.HasPeb() || full.HasPeb());
EXPECT_TRUE(mini.HasStream(MemoryListStream));
EXPECT_TRUE(full.HasStream(Memory64ListStream));
EXPECT_FALSE(mini.HasStream(Memory64ListStream));
EXPECT_FALSE(full.HasStream(MemoryListStream));
// This is the only place we don't use OR because we want both not
// to have the streams.
EXPECT_FALSE(mini.HasStream(ThreadExListStream));
EXPECT_FALSE(full.HasStream(ThreadExListStream));
EXPECT_FALSE(mini.HasStream(CommentStreamA));
EXPECT_FALSE(full.HasStream(CommentStreamA));
EXPECT_FALSE(mini.HasStream(CommentStreamW));
EXPECT_FALSE(full.HasStream(CommentStreamW));
EXPECT_FALSE(mini.HasStream(FunctionTableStream));
EXPECT_FALSE(full.HasStream(FunctionTableStream));
EXPECT_FALSE(mini.HasStream(MemoryInfoListStream));
EXPECT_FALSE(full.HasStream(MemoryInfoListStream));
EXPECT_FALSE(mini.HasStream(ThreadInfoListStream));
EXPECT_FALSE(full.HasStream(ThreadInfoListStream));
EXPECT_FALSE(mini.HasStream(HandleOperationListStream));
EXPECT_FALSE(full.HasStream(HandleOperationListStream));
EXPECT_FALSE(mini.HasStream(TokenStream));
EXPECT_FALSE(full.HasStream(TokenStream));
}
} // namespace

View File

@ -0,0 +1,263 @@
// -*- 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>
// byte_cursor.h: Classes for parsing values from a buffer of bytes.
// The ByteCursor class provides a convenient interface for reading
// fixed-size integers of arbitrary endianness, being thorough about
// checking for buffer overruns.
#ifndef COMMON_BYTE_CURSOR_H_
#define COMMON_BYTE_CURSOR_H_
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string>
namespace google_breakpad {
// A buffer holding a series of bytes.
struct ByteBuffer {
ByteBuffer() : start(0), end(0) { }
ByteBuffer(const uint8_t *set_start, size_t set_size)
: start(set_start), end(set_start + set_size) { }
~ByteBuffer() { };
// Equality operators. Useful in unit tests, and when we're using
// ByteBuffers to refer to regions of a larger buffer.
bool operator==(const ByteBuffer &that) const {
return start == that.start && end == that.end;
}
bool operator!=(const ByteBuffer &that) const {
return start != that.start || end != that.end;
}
// Not C++ style guide compliant, but this definitely belongs here.
size_t Size() const {
assert(start <= end);
return end - start;
}
const uint8_t *start, *end;
};
// A cursor pointing into a ByteBuffer that can parse numbers of various
// widths and representations, strings, and data blocks, advancing through
// the buffer as it goes. All ByteCursor operations check that accesses
// haven't gone beyond the end of the enclosing ByteBuffer.
class ByteCursor {
public:
// Create a cursor reading bytes from the start of BUFFER. By default, the
// cursor reads multi-byte values in little-endian form.
ByteCursor(const ByteBuffer *buffer, bool big_endian = false)
: buffer_(buffer), here_(buffer->start),
big_endian_(big_endian), complete_(true) { }
// Accessor and setter for this cursor's endianness flag.
bool big_endian() const { return big_endian_; }
void set_big_endian(bool big_endian) { big_endian_ = big_endian; }
// Accessor and setter for this cursor's current position. The setter
// returns a reference to this cursor.
const uint8_t *here() const { return here_; }
ByteCursor &set_here(const uint8_t *here) {
assert(buffer_->start <= here && here <= buffer_->end);
here_ = here;
return *this;
}
// Return the number of bytes available to read at the cursor.
size_t Available() const { return size_t(buffer_->end - here_); }
// Return true if this cursor is at the end of its buffer.
bool AtEnd() const { return Available() == 0; }
// When used as a boolean value this cursor converts to true if all
// prior reads have been completed, or false if we ran off the end
// of the buffer.
operator bool() const { return complete_; }
// Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true,
// unsigned otherwise, using the cursor's established endianness, and set
// *RESULT to the number. If we read off the end of our buffer, clear
// this cursor's complete_ flag, and store a dummy value in *RESULT.
// Return a reference to this cursor.
template<typename T>
ByteCursor &Read(size_t size, bool is_signed, T *result) {
if (CheckAvailable(size)) {
T v = 0;
if (big_endian_) {
for (size_t i = 0; i < size; i++)
v = (v << 8) + here_[i];
} else {
// This loop condition looks weird, but size_t is unsigned, so
// decrementing i after it is zero yields the largest size_t value.
for (size_t i = size - 1; i < size; i--)
v = (v << 8) + here_[i];
}
if (is_signed && size < sizeof(T)) {
size_t sign_bit = (T)1 << (size * 8 - 1);
v = (v ^ sign_bit) - sign_bit;
}
here_ += size;
*result = v;
} else {
*result = (T) 0xdeadbeef;
}
return *this;
}
// Read an integer, using the cursor's established endianness and
// *RESULT's size and signedness, and set *RESULT to the number. If we
// read off the end of our buffer, clear this cursor's complete_ flag.
// Return a reference to this cursor.
template<typename T>
ByteCursor &operator>>(T &result) {
bool T_is_signed = (T)-1 < 0;
return Read(sizeof(T), T_is_signed, &result);
}
// Copy the SIZE bytes at the cursor to BUFFER, and advance this
// cursor to the end of them. If we read off the end of our buffer,
// clear this cursor's complete_ flag, and set *POINTER to NULL.
// Return a reference to this cursor.
ByteCursor &Read(uint8_t *buffer, size_t size) {
if (CheckAvailable(size)) {
memcpy(buffer, here_, size);
here_ += size;
}
return *this;
}
// Set STR to a copy of the '\0'-terminated string at the cursor. If the
// byte buffer does not contain a terminating zero, clear this cursor's
// complete_ flag, and set STR to the empty string. Return a reference to
// this cursor.
ByteCursor &CString(std::string *str) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
if (end) {
str->assign(reinterpret_cast<const char *>(here_), end - here_);
here_ = end + 1;
} else {
str->clear();
here_ = buffer_->end;
complete_ = false;
}
return *this;
}
// Like CString(STR), but extract the string from a fixed-width buffer
// LIMIT bytes long, which may or may not contain a terminating '\0'
// byte. Specifically:
//
// - If there are not LIMIT bytes available at the cursor, clear the
// cursor's complete_ flag and set STR to the empty string.
//
// - Otherwise, if the LIMIT bytes at the cursor contain any '\0'
// characters, set *STR to a copy of the bytes before the first '\0',
// and advance the cursor by LIMIT bytes.
//
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
// cursor by LIMIT bytes.
ByteCursor &CString(std::string *str, size_t limit) {
if (CheckAvailable(limit)) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));
if (end)
str->assign(reinterpret_cast<const char *>(here_), end - here_);
else
str->assign(reinterpret_cast<const char *>(here_), limit);
here_ += limit;
} else {
str->clear();
}
return *this;
}
// Set *POINTER to point to the SIZE bytes at the cursor, and advance
// this cursor to the end of them. If SIZE is omitted, don't move the
// cursor. If we read off the end of our buffer, clear this cursor's
// complete_ flag, and set *POINTER to NULL. Return a reference to this
// cursor.
ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) {
if (CheckAvailable(size)) {
*pointer = here_;
here_ += size;
} else {
*pointer = NULL;
}
return *this;
}
// Skip SIZE bytes at the cursor. If doing so would advance us off
// the end of our buffer, clear this cursor's complete_ flag, and
// set *POINTER to NULL. Return a reference to this cursor.
ByteCursor &Skip(size_t size) {
if (CheckAvailable(size))
here_ += size;
return *this;
}
private:
// If there are at least SIZE bytes available to read from the buffer,
// return true. Otherwise, set here_ to the end of the buffer, set
// complete_ to false, and return false.
bool CheckAvailable(size_t size) {
if (Available() >= size) {
return true;
} else {
here_ = buffer_->end;
complete_ = false;
return false;
}
}
// The buffer we're reading bytes from.
const ByteBuffer *buffer_;
// The next byte within buffer_ that we'll read.
const uint8_t *here_;
// True if we should read numbers in big-endian form; false if we
// should read in little-endian form.
bool big_endian_;
// True if we've been able to read all we've been asked to.
bool complete_;
};
} // namespace google_breakpad
#endif // COMMON_BYTE_CURSOR_H_

View File

@ -0,0 +1,776 @@
// 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>
// byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer
// and google_breakpad::ByteCursor.
#include <string>
#include <string.h>
#include "common/byte_cursor.h"
#include "breakpad_googletest_includes.h"
using google_breakpad::ByteBuffer;
using google_breakpad::ByteCursor;
using std::string;
TEST(Buffer, SizeOfNothing) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
EXPECT_EQ(0U, buffer.Size());
}
TEST(Buffer, SizeOfSomething) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
EXPECT_EQ(10U, buffer.Size());
}
TEST(Extent, AvailableEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_EQ(0U, cursor.Available());
}
TEST(Extent, AtEndEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor.AtEnd());
}
TEST(Extent, AsBoolEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor);
}
TEST(Extent, AvailableSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_EQ(10U, cursor.Available());
}
TEST(Extent, AtEndSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_FALSE(cursor.AtEnd());
EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd());
}
TEST(Extent, AsBoolSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor);
EXPECT_TRUE(cursor.Skip(sizeof(data)));
EXPECT_FALSE(cursor.Skip(1));
}
TEST(Extent, Cursor) {
uint8_t data[] = { 0xf7,
0x9f, 0xbe,
0x67, 0xfb, 0xd3, 0x58,
0x6f, 0x36, 0xde, 0xd1,
0x2a, 0x2a, 0x2a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a;
uint16_t b;
uint32_t c;
uint32_t d;
uint8_t stars[3];
EXPECT_EQ(data + 0U, cursor.here());
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(data + 1U, cursor.here());
EXPECT_TRUE(cursor >> b);
EXPECT_EQ(data + 3U, cursor.here());
EXPECT_TRUE(cursor >> c);
EXPECT_EQ(data + 7U, cursor.here());
EXPECT_TRUE(cursor.Skip(4));
EXPECT_EQ(data + 11U, cursor.here());
EXPECT_TRUE(cursor.Read(stars, 3));
EXPECT_EQ(data + 14U, cursor.here());
EXPECT_FALSE(cursor >> d);
EXPECT_EQ(data + 14U, cursor.here());
}
TEST(Extent, SetOffset) {
uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a, b, c, d, e;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0x5cU, a);
EXPECT_EQ(data + 1U, cursor.here());
EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1)
>> d >> e);
EXPECT_EQ(0x79U, b);
EXPECT_EQ(0xd5U, c);
EXPECT_EQ(0x79U, d);
EXPECT_EQ(0x8cU, e);
EXPECT_EQ(data + 3U, cursor.here());
}
TEST(BigEndian, Signed1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, true, &a)
.Read(1, true, &b)
.Read(1, true, &c)
.Read(1, true, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(-0x80, c);
EXPECT_EQ(-1, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, true, &e));
}
TEST(BigEndian, Signed2) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, true, &a)
.Read(2, true, &b)
.Read(2, true, &c)
.Read(2, true, &d)
.Read(2, true, &e)
.Read(2, true, &f)
.Read(2, true, &g)
.Read(2, true, &h)
.Read(2, true, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(-0x8000, d);
EXPECT_EQ(-0x7f80, e);
EXPECT_EQ(-1, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(-0x7544, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, true, &j));
}
TEST(BigEndian, Signed4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0xb6, 0xb1, 0xff, 0xef,
0x19, 0x6a, 0xca, 0x46 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, true, &a)
.Read(4, true, &b)
.Read(4, true, &c)
.Read(4, true, &d)
.Read(4, true, &e)
.Read(4, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(-0x80000000LL, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int32_t) 0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, true, &g));
}
TEST(BigEndian, Signed8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, true, &a)
.Read(8, true, &b)
.Read(8, true, &c)
.Read(8, true, &d)
.Read(8, true, &e)
.Read(8, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffffffffffffLL, b);
EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4LL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, true, &g));
}
TEST(BigEndian, Unsigned1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, false, &a)
.Read(1, false, &b)
.Read(1, false, &c)
.Read(1, false, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(0x80, c);
EXPECT_EQ(0xff, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, false, &e));
}
TEST(BigEndian, Unsigned2) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int64_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, false, &a)
.Read(2, false, &b)
.Read(2, false, &c)
.Read(2, false, &d)
.Read(2, false, &e)
.Read(2, false, &f)
.Read(2, false, &g)
.Read(2, false, &h)
.Read(2, false, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(0x8000, d);
EXPECT_EQ(0x8080, e);
EXPECT_EQ(0xffff, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(0x8abc, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, false, &j));
}
TEST(BigEndian, Unsigned4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0xb6, 0xb1, 0xff, 0xef,
0x19, 0x6a, 0xca, 0x46 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, false, &a)
.Read(4, false, &b)
.Read(4, false, &c)
.Read(4, false, &d)
.Read(4, false, &e)
.Read(4, false, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(0x80000000, c);
EXPECT_EQ(0xffffffff, d);
EXPECT_EQ(0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, false, &g));
}
TEST(BigEndian, Unsigned8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
uint64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, false, &a)
.Read(8, false, &b)
.Read(8, false, &c)
.Read(8, false, &d)
.Read(8, false, &e)
.Read(8, false, &f));
EXPECT_EQ(0U, a);
EXPECT_EQ(0x7fffffffffffffffULL, b);
EXPECT_EQ(0x8000000000000000ULL, c);
EXPECT_EQ(0xffffffffffffffffULL, d);
EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, false, &g));
}
TEST(LittleEndian, Signed1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, true, &a)
.Read(1, true, &b)
.Read(1, true, &c)
.Read(1, true, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(-0x80, c);
EXPECT_EQ(-1, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, true, &e));
}
TEST(LittleEndian, Signed2) {
uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, false);
int32_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, true, &a)
.Read(2, true, &b)
.Read(2, true, &c)
.Read(2, true, &d)
.Read(2, true, &e)
.Read(2, true, &f)
.Read(2, true, &g)
.Read(2, true, &h)
.Read(2, true, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(-0x8000, d);
EXPECT_EQ(-0x7f80, e);
EXPECT_EQ(-1, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(-0x7544, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, true, &j));
}
TEST(LittleEndian, Signed4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff,
0xef, 0xff, 0xb1, 0xb6,
0x46, 0xca, 0x6a, 0x19 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, true, &a)
.Read(4, true, &b)
.Read(4, true, &c)
.Read(4, true, &d)
.Read(4, true, &e)
.Read(4, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(-0x80000000LL, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int32_t) 0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, true, &g));
}
TEST(LittleEndian, Signed8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, false);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, true, &a)
.Read(8, true, &b)
.Read(8, true, &c)
.Read(8, true, &d)
.Read(8, true, &e)
.Read(8, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffffffffffffLL, b);
EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4LL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, true, &g));
}
TEST(LittleEndian, Unsigned1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, false, &a)
.Read(1, false, &b)
.Read(1, false, &c)
.Read(1, false, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(0x80, c);
EXPECT_EQ(0xff, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, false, &e));
}
TEST(LittleEndian, Unsigned2) {
uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, false, &a)
.Read(2, false, &b)
.Read(2, false, &c)
.Read(2, false, &d)
.Read(2, false, &e)
.Read(2, false, &f)
.Read(2, false, &g)
.Read(2, false, &h)
.Read(2, false, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(0x8000, d);
EXPECT_EQ(0x8080, e);
EXPECT_EQ(0xffff, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(0x8abc, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, false, &j));
}
TEST(LittleEndian, Unsigned4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff,
0xef, 0xff, 0xb1, 0xb6,
0x46, 0xca, 0x6a, 0x19 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, false, &a)
.Read(4, false, &b)
.Read(4, false, &c)
.Read(4, false, &d)
.Read(4, false, &e)
.Read(4, false, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(0x80000000, c);
EXPECT_EQ(0xffffffff, d);
EXPECT_EQ(0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, false, &g));
}
TEST(LittleEndian, Unsigned8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, false, &a)
.Read(8, false, &b)
.Read(8, false, &c)
.Read(8, false, &d)
.Read(8, false, &e)
.Read(8, false, &f));
EXPECT_EQ(0U, a);
EXPECT_EQ(0x7fffffffffffffffULL, b);
EXPECT_EQ(0x8000000000000000ULL, c);
EXPECT_EQ(0xffffffffffffffffULL, d);
EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, false, &g));
}
TEST(Extractor, Signed1) {
uint8_t data[] = { 0xfd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int8_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-3, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Signed2) {
uint8_t data[] = { 0x13, 0xcd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int16_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-13037, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Signed4) {
uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a;
// For some reason, G++ 4.4.1 complains:
// warning: array subscript is above array bounds
// in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
// I'm not able to see how such a reference would occur.
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-380377902, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned1) {
uint8_t data[] = { 0xfd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xfd, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned2) {
uint8_t data[] = { 0x13, 0xcd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint16_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xcd13, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned4) {
uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint32_t a;
// For some reason, G++ 4.4.1 complains:
// warning: array subscript is above array bounds
// in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
// I'm not able to see how such a reference would occur.
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xe953e4d2, a);
EXPECT_FALSE(cursor >> a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Mixed) {
uint8_t data[] = { 0x42,
0x25, 0x0b,
0x3d, 0x25, 0xed, 0x2a,
0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf,
0xd8,
0x22, 0xa5,
0x3a, 0x02, 0x6a, 0xd7,
0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
uint8_t a;
uint16_t b;
uint32_t c;
uint64_t d;
int8_t e;
int16_t f;
int32_t g;
int64_t h;
int z;
EXPECT_FALSE(cursor.AtEnd());
EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h);
EXPECT_EQ(0x42U, a);
EXPECT_EQ(0x250bU, b);
EXPECT_EQ(0x3d25ed2aU, c);
EXPECT_EQ(0xec169e14615b2ccfULL, d);
EXPECT_EQ(-40, e);
EXPECT_EQ(0x22a5, f);
EXPECT_EQ(0x3a026ad7, g);
EXPECT_EQ(-7842405714468937530LL, h);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor >> z);
}
TEST(Strings, Zero) {
uint8_t data[] = { 0xa6 };
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
uint8_t received[1];
received[0] = 0xc2;
EXPECT_TRUE(cursor.Read(received, 0));
EXPECT_EQ(0xc2U, received[0]);
}
TEST(Strings, Some) {
uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed };
EXPECT_TRUE(cursor.Skip(2).Read(received, 5));
uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed };
EXPECT_TRUE(memcmp(received, expected, 7) == 0);
}
TEST(Strings, TooMuch) {
uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t received1[3];
uint8_t received2[3];
uint8_t received3[3];
EXPECT_FALSE(cursor
.Read(received1, 3)
.Read(received2, 3)
.Read(received3, 3));
uint8_t expected1[3] = { 0x5d, 0x31, 0x09 };
uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c };
EXPECT_TRUE(memcmp(received1, expected1, 3) == 0);
EXPECT_TRUE(memcmp(received2, expected2, 3) == 0);
}
TEST(Strings, PointTo) {
uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
const uint8_t *received1;
const uint8_t *received2;
const uint8_t *received3;
const uint8_t *received4;
EXPECT_FALSE(cursor
.PointTo(&received1, 3)
.PointTo(&received2, 3)
.PointTo(&received3)
.PointTo(&received4, 3));
EXPECT_EQ(data + 0, received1);
EXPECT_EQ(data + 3, received2);
EXPECT_EQ(data + 6, received3);
EXPECT_EQ(NULL, received4);
}
TEST(Strings, CString) {
uint8_t data[] = "abc\0\0foo";
ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
ByteCursor cursor(&buffer);
string a, b, c;
EXPECT_TRUE(cursor.CString(&a).CString(&b));
EXPECT_EQ("abc", a);
EXPECT_EQ("", b);
EXPECT_FALSE(cursor.CString(&c));
EXPECT_EQ("", c);
EXPECT_TRUE(cursor.AtEnd());
}
TEST(Strings, CStringLimit) {
uint8_t data[] = "abcdef\0\0foobar";
ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
ByteCursor cursor(&buffer);
string a, b, c, d, e;
EXPECT_TRUE(cursor.CString(&a, 3));
EXPECT_EQ("abc", a);
EXPECT_TRUE(cursor.CString(&b, 0));
EXPECT_EQ("", b);
EXPECT_TRUE(cursor.CString(&c, 6));
EXPECT_EQ("def", c);
EXPECT_TRUE(cursor.CString(&d, 4));
EXPECT_EQ("ooba", d);
EXPECT_FALSE(cursor.CString(&e, 4));
EXPECT_EQ("", e);
EXPECT_TRUE(cursor.AtEnd());
}
// uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 };
// ByteBuffer buffer(data, sizeof(data));

View File

@ -29,10 +29,10 @@
#ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__
#define UTIL_DEBUGINFO_BYTEREADER_INL_H__
#include <cassert>
#include "common/dwarf/bytereader.h"
#include <assert.h>
namespace dwarf2reader {
inline uint8 ByteReader::ReadOneByte(const char* buffer) const {

View File

@ -43,10 +43,10 @@ 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 google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using std::string;
using testing::Test;

View File

@ -32,11 +32,11 @@
// 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"
#include <assert.h>
#include <stdlib.h>
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
@ -52,18 +52,14 @@ CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor,
in_fde_ = false;
if (dwarf64) {
D32(0xffffffff);
D32(kDwarf64InitialLengthMarker);
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);
D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier);
} 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);
D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier);
}
D8(version);
AppendCString(augmentation);
@ -193,4 +189,10 @@ CFISection &CFISection::EncodedPointer(u_int64_t address,
return *this;
};
const u_int32_t CFISection::kDwarf64InitialLengthMarker;
const u_int32_t CFISection::kDwarf32CIEIdentifier;
const u_int64_t CFISection::kDwarf64CIEIdentifier;
const u_int32_t CFISection::kEHFrame32CIEIdentifier;
const u_int64_t CFISection::kEHFrame64CIEIdentifier;
} // namespace google_breakpad

View File

@ -31,7 +31,7 @@
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi-assembler.h: Define CFISection, a class for creating properly
// cfi_assembler.h: Define CFISection, a class for creating properly
// (and improperly) formatted DWARF CFI data for unit tests.
#ifndef PROCESSOR_CFI_ASSEMBLER_H_
@ -40,15 +40,15 @@
#include <string>
#include "common/dwarf/dwarf2enums.h"
#include "common/test_assembler.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 google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using std::string;
class CFISection: public Section {
@ -219,6 +219,19 @@ class CFISection: public Section {
Label start;
};
// Constants used in CFI/.eh_frame data:
// If the first four bytes of an "initial length" are this constant, then
// the data uses the 64-bit DWARF format, and the length itself is the
// subsequent eight bytes.
static const u_int32_t kDwarf64InitialLengthMarker = 0xffffffffU;
// The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
static const u_int32_t kDwarf32CIEIdentifier = ~(u_int32_t)0;
static const u_int64_t kDwarf64CIEIdentifier = ~(u_int64_t)0;
static const u_int32_t kEHFrame32CIEIdentifier = 0;
static const u_int64_t kEHFrame64CIEIdentifier = 0;
// The size of a machine address for the data in this section.
size_t address_size_;

View File

@ -31,10 +31,10 @@
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
// See dwarf2diehandler.h for details.
#include <cassert>
#include "common/dwarf/dwarf2diehandler.h"
#include <assert.h>
namespace dwarf2reader {
DIEDispatcher::~DIEDispatcher() {

View File

@ -529,7 +529,7 @@ enum DwarfInline {
DW_INL_not_inlined =0x0,
DW_INL_inlined =0x1,
DW_INL_declared_not_inlined =0x2,
DW_INL_declared_inlined =0x3
DW_INL_declared_inlined =0x3,
};
// Call Frame Info instructions.
@ -575,6 +575,37 @@ enum DwarfCFI
DW_CFA_GNU_negative_offset_extended = 0x2f
};
// Exception handling 'z' augmentation letters.
enum DwarfZAugmentationCodes {
// If the CFI augmentation string begins with 'z', then the CIE and FDE
// have an augmentation data area just before the instructions, whose
// contents are determined by the subsequent augmentation letters.
DW_Z_augmentation_start = 'z',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding, and the FDE
// augmentation data includes a language-specific data area pointer,
// represented using that encoding.
DW_Z_has_LSDA = 'L',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding, followed by a pointer
// to a personality routine, represented using that encoding.
DW_Z_has_personality_routine = 'P',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding describing how the FDE's
// initial location, address range, and DW_CFA_set_loc operands are
// encoded.
DW_Z_has_FDE_address_encoding = 'R',
// If this letter is present in a 'z' augmentation string, then code
// addresses covered by FDEs that cite this CIE are signal delivery
// trampolines. Return addresses of frames in trampolines should not be
// adjusted as described in section 6.4.4 of the DWARF 3 spec.
DW_Z_is_signal_trampoline = 'S'
};
// Exception handling frame description pointer formats, as described
// by the Linux Standard Base Core Specification 4.0, section 11.5,
// DWARF Extensions.

View File

@ -31,16 +31,18 @@
// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit,
// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details.
#include <cassert>
#include <cstdio>
#include <cstring>
#include "common/dwarf/dwarf2reader.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <map>
#include <memory>
#include <stack>
#include <utility>
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/line_state_machine.h"
@ -919,7 +921,8 @@ class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule {
return handler->UndefinedRule(address, reg);
}
bool operator==(const Rule &rhs) const {
// dynamic_cast is prohibited by Google C++ Style Guide, but justified.
// dynamic_cast is allowed by the Google C++ Style Guide, if the use has
// been carefully considered; cheap RTTI-like workarounds are forbidden.
const UndefinedRule *our_rhs = dynamic_cast<const UndefinedRule *>(&rhs);
return (our_rhs != NULL);
}
@ -935,7 +938,8 @@ class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule {
return handler->SameValueRule(address, reg);
}
bool operator==(const Rule &rhs) const {
// dynamic_cast is prohibited by Google C++ Style Guide, but justified.
// dynamic_cast is allowed by the Google C++ Style Guide, if the use has
// been carefully considered; cheap RTTI-like workarounds are forbidden.
const SameValueRule *our_rhs = dynamic_cast<const SameValueRule *>(&rhs);
return (our_rhs != NULL);
}
@ -953,7 +957,8 @@ class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule {
return handler->OffsetRule(address, reg, base_register_, offset_);
}
bool operator==(const Rule &rhs) const {
// dynamic_cast is prohibited by Google C++ Style Guide, but justified.
// dynamic_cast is allowed by the Google C++ Style Guide, if the use has
// been carefully considered; cheap RTTI-like workarounds are forbidden.
const OffsetRule *our_rhs = dynamic_cast<const OffsetRule *>(&rhs);
return (our_rhs &&
base_register_ == our_rhs->base_register_ &&
@ -966,7 +971,7 @@ class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule {
// computes the address at which a register is saved, not a value.
private:
int base_register_;
int offset_;
long offset_;
};
// Rule: the value the register had in the caller is the value of
@ -981,7 +986,8 @@ class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule {
return handler->ValOffsetRule(address, reg, base_register_, offset_);
}
bool operator==(const Rule &rhs) const {
// dynamic_cast is prohibited by Google C++ Style Guide, but justified.
// dynamic_cast is allowed by the Google C++ Style Guide, if the use has
// been carefully considered; cheap RTTI-like workarounds are forbidden.
const ValOffsetRule *our_rhs = dynamic_cast<const ValOffsetRule *>(&rhs);
return (our_rhs &&
base_register_ == our_rhs->base_register_ &&
@ -992,7 +998,7 @@ class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule {
void SetOffset(long long offset) { offset_ = offset; }
private:
int base_register_;
int offset_;
long offset_;
};
// Rule: the register has been saved in another register REGISTER_NUMBER_.
@ -1005,7 +1011,8 @@ class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule {
return handler->RegisterRule(address, reg, register_number_);
}
bool operator==(const Rule &rhs) const {
// dynamic_cast is prohibited by Google C++ Style Guide, but justified.
// dynamic_cast is allowed by the Google C++ Style Guide, if the use has
// been carefully considered; cheap RTTI-like workarounds are forbidden.
const RegisterRule *our_rhs = dynamic_cast<const RegisterRule *>(&rhs);
return (our_rhs && register_number_ == our_rhs->register_number_);
}
@ -1024,7 +1031,8 @@ class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule {
return handler->ExpressionRule(address, reg, expression_);
}
bool operator==(const Rule &rhs) const {
// dynamic_cast is prohibited by Google C++ Style Guide, but justified.
// dynamic_cast is allowed by the Google C++ Style Guide, if the use has
// been carefully considered; cheap RTTI-like workarounds are forbidden.
const ExpressionRule *our_rhs = dynamic_cast<const ExpressionRule *>(&rhs);
return (our_rhs && expression_ == our_rhs->expression_);
}
@ -1043,7 +1051,8 @@ class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule {
return handler->ValExpressionRule(address, reg, expression_);
}
bool operator==(const Rule &rhs) const {
// dynamic_cast is prohibited by Google C++ Style Guide, but justified.
// dynamic_cast is allowed by the Google C++ Style Guide, if the use has
// been carefully considered; cheap RTTI-like workarounds are forbidden.
const ValExpressionRule *our_rhs =
dynamic_cast<const ValExpressionRule *>(&rhs);
return (our_rhs && expression_ == our_rhs->expression_);
@ -1870,7 +1879,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
// If we don't recognize the version, we can't parse any more fields
// of the CIE. For DWARF CFI, we handle versions 1 through 3 (there
// was never a version 2 fo CFI data). For .eh_frame, we handle only
// was never a version 2 of CFI data). For .eh_frame, we handle only
// version 1.
if (eh_frame_) {
if (cie->version != 1) {
@ -1878,7 +1887,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
return false;
}
} else {
if (cie->version < 1 || 3 < cie->version) {
if (cie->version < 1 || cie->version > 3) {
reporter_->UnrecognizedVersion(cie->offset, cie->version);
return false;
}
@ -1893,18 +1902,18 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
// Skip the terminating '\0'.
cursor++;
// Is this an augmentation we recognize?
if (cie->augmentation.empty()) {
; // Stock DWARF CFI.
} else if (cie->augmentation[0] == 'z') {
// Linux C++ ABI 'z' augmentation, used for exception handling data.
cie->has_z_augmentation = true;
} else {
// Not an augmentation we recognize. Augmentations can have
// arbitrary effects on the form of rest of the content, so we
// have to give up.
reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation);
return false;
// Is this CFI augmented?
if (!cie->augmentation.empty()) {
// Is it an augmentation we recognize?
if (cie->augmentation[0] == DW_Z_augmentation_start) {
// Linux C++ ABI 'z' augmentation, used for exception handling data.
cie->has_z_augmentation = true;
} else {
// Not an augmentation we recognize. Augmentations can have arbitrary
// effects on the form of rest of the content, so we have to give up.
reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation);
return false;
}
}
// Parse the code alignment factor.
@ -1947,7 +1956,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
// augmentation data as the string directs.
for (size_t i = 1; i < cie->augmentation.size(); i++) {
switch (cie->augmentation[i]) {
case 'L':
case DW_Z_has_LSDA:
// The CIE's augmentation data holds the language-specific data
// area pointer's encoding, and the FDE's augmentation data holds
// the pointer itself.
@ -1965,7 +1974,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
// LSDA to use, since it appears in the FDE.
break;
case 'P':
case DW_Z_has_personality_routine:
// The CIE's augmentation data holds the personality routine
// pointer's encoding, followed by the pointer itself.
cie->has_z_personality = true;
@ -1992,7 +2001,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
data += len;
break;
case 'R':
case DW_Z_has_FDE_address_encoding:
// The CIE's augmentation data holds the pointer encoding to use
// for addresses in the FDE.
if (data >= data_end) return ReportIncomplete(cie);
@ -2009,7 +2018,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
}
break;
case 'S':
case DW_Z_is_signal_trampoline:
// Frames using this CIE are signal delivery frames.
cie->has_z_signal_frame = true;
break;
@ -2295,7 +2304,8 @@ void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64 offset,
uint8 encoding) {
fprintf(stderr,
"%s: CFI common information entry at offset 0x%llx in '%s':"
" 'z' augmentation specifies a pointer encoding for which we have no base address: 0x%02x\n",
" 'z' augmentation specifies a pointer encoding for which"
" we have no base address: 0x%02x\n",
filename_.c_str(), offset, section_.c_str(), encoding);
}

View File

@ -31,7 +31,8 @@
// dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo
#include <cstdlib>
#include <stdlib.h>
#include <vector>
// The '.eh_frame' format, used by the Linux C++ ABI for exception
@ -39,15 +40,15 @@
// if you #define WRITE_ELF while compiling this file, and add the
// 'include' directory from the binutils, gcc, or gdb source tree to the
// #include path, then each test that calls the
// PERHAPS_WRITE_DEBUG_FRAME_FILE or PERHAPS_WRITE_EH_FRAME_FILE will an
// ELF file containing a .debug_frame or .eh_frame section; you can then
// PERHAPS_WRITE_DEBUG_FRAME_FILE or PERHAPS_WRITE_EH_FRAME_FILE will write
// an ELF file containing a .debug_frame or .eh_frame section; you can then
// use tools like readelf to examine the test data, and check the tools'
// interpretation against the test's intentions. Each ELF file is named
// "cfitest-TEST", where TEST identifies the particular test.
#ifdef WRITE_ELF
#include <cstdio>
#include <cerrno>
#include <cstring>
#include <errno.h>
#include <stdio.h>
#include <string.h>
extern "C" {
// To compile with WRITE_ELF, you should add the 'include' directory
// of the binutils, gcc, or gdb source tree to your #include path;
@ -63,10 +64,10 @@ extern "C" {
#include "google_breakpad/common/breakpad_types.h"
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 google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using dwarf2reader::DwarfPointerEncoding;
using dwarf2reader::ENDIANNESS_BIG;
@ -2315,7 +2316,7 @@ TEST_F(CFIReporter, ClearingCFARule) {
#ifdef WRITE_ELF
// See comments at the top of the file mentioning WRITE_ELF for details.
using google_breakpad::TestAssembler::Section;
using google_breakpad::test_assembler::Section;
struct ELFSectionHeader {
ELFSectionHeader(unsigned int set_type)

View File

@ -30,10 +30,13 @@
// information from the debug info.
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <map>
#include <queue>
#include <vector>
#include <memory>
#include "common/dwarf/functioninfo.h"

View File

@ -36,12 +36,65 @@
#include <sstream>
#include "common/linux/dwarf_cfi_to_module.h"
#include "common/dwarf_cfi_to_module.h"
namespace google_breakpad {
using std::ostringstream;
vector<string> DwarfCFIToModule::RegisterNames::MakeVector(
const char * const *strings,
size_t size) {
vector<string> names(strings, strings + size);
return names;
}
vector<string> DwarfCFIToModule::RegisterNames::I386() {
static const char *const 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"
};
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
vector<string> DwarfCFIToModule::RegisterNames::X86_64() {
static const char *const 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"
};
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
vector<string> DwarfCFIToModule::RegisterNames::ARM() {
static const char *const 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"
};
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address) {

View File

@ -39,11 +39,12 @@
#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H
#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H
#include <cassert>
#include <assert.h>
#include <string>
#include <vector>
#include "common/linux/module.h"
#include "common/module.h"
#include "common/dwarf/dwarf2reader.h"
namespace google_breakpad {
@ -91,6 +92,26 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
string file_, section_;
};
// Register name tables. If TABLE is a vector returned by one of these
// functions, then TABLE[R] is the name of the register numbered R in
// DWARF call frame information.
class RegisterNames {
public:
// Intel's "x86" or IA-32.
static vector<string> I386();
// AMD x86_64, AMD64, Intel EM64T, or Intel 64
static vector<string> X86_64();
// ARM.
static vector<string> ARM();
private:
// Given STRINGS, an array of C strings with SIZE elements, return an
// equivalent vector<string>.
static vector<string> MakeVector(const char * const *strings, size_t size);
};
// Create a handler for the dwarf2reader::CallFrameInfo parser that
// records the stack unwinding information it receives in MODULE.
//

View File

@ -32,7 +32,7 @@
// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
#include "breakpad_googletest_includes.h"
#include "common/linux/dwarf_cfi_to_module.h"
#include "common/dwarf_cfi_to_module.h"
using google_breakpad::Module;
using google_breakpad::DwarfCFIToModule;
@ -258,3 +258,31 @@ TEST_F(Rule, DefaultReturnAddressRuleLater) {
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST(RegisterNames, I386) {
vector<string> names = DwarfCFIToModule::RegisterNames::I386();
EXPECT_EQ("$eax", names[0]);
EXPECT_EQ("$ecx", names[1]);
EXPECT_EQ("$esp", names[4]);
EXPECT_EQ("$eip", names[8]);
}
TEST(RegisterNames, ARM) {
vector<string> names = DwarfCFIToModule::RegisterNames::ARM();
EXPECT_EQ("r0", names[0]);
EXPECT_EQ("r10", names[10]);
EXPECT_EQ("sp", names[13]);
EXPECT_EQ("lr", names[14]);
EXPECT_EQ("pc", names[15]);
}
TEST(RegisterNames, X86_64) {
vector<string> names = DwarfCFIToModule::RegisterNames::X86_64();
EXPECT_EQ("$rax", names[0]);
EXPECT_EQ("$rdx", names[1]);
EXPECT_EQ("$rbp", names[6]);
EXPECT_EQ("$rsp", names[7]);
EXPECT_EQ("$rip", names[16]);
}

View File

@ -31,11 +31,13 @@
// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h.
#include <algorithm>
#include <cassert>
#include "common/dwarf_cu_to_module.h"
#include "common/linux/dwarf_cu_to_module.h"
#include "common/linux/dwarf_line_to_module.h"
#include <assert.h>
#include <algorithm>
#include "common/dwarf_line_to_module.h"
namespace google_breakpad {
@ -441,7 +443,7 @@ dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
default:
return NULL;
}
}
};
void DwarfCUToModule::WarningReporter::CUHeading() {
if (printed_cu_header_)
@ -493,6 +495,8 @@ void DwarfCUToModule::WarningReporter::UncoveredHeading() {
void DwarfCUToModule::WarningReporter::UncoveredFunction(
const Module::Function &function) {
if (!uncovered_warnings_enabled_)
return;
UncoveredHeading();
fprintf(stderr, " function%s: %s\n",
function.size == 0 ? " (zero-length)" : "",
@ -500,6 +504,8 @@ void DwarfCUToModule::WarningReporter::UncoveredFunction(
}
void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) {
if (!uncovered_warnings_enabled_)
return;
UncoveredHeading();
fprintf(stderr, " line%s: %s:%d at 0x%llx\n",
(line.size == 0 ? " (zero-length)" : ""),
@ -615,6 +621,10 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) {
= cu_context_->file_context->section_map;
dwarf2reader::SectionMap::const_iterator map_entry
= section_map.find(".debug_line");
// Mac OS X puts DWARF data in sections whose names begin with "__"
// instead of ".".
if (map_entry == section_map.end())
map_entry = section_map.find("__debug_line");
if (map_entry == section_map.end()) {
cu_context_->reporter->MissingSection(".debug_line");
return;

View File

@ -41,10 +41,8 @@
#include <string>
#include <elf.h>
#include <link.h>
#include "common/linux/language.h"
#include "common/linux/module.h"
#include "common/language.h"
#include "common/module.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/dwarf/dwarf2reader.h"
@ -120,12 +118,24 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
// 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) { }
printed_unpaired_header_(false),
uncovered_warnings_enabled_(false) { }
virtual ~WarningReporter() { }
// Set the name of the compilation unit we're processing to NAME.
virtual void SetCUName(const string &name) { cu_name_ = name; }
// Accessor and setter for uncovered_warnings_enabled_.
// UncoveredFunction and UncoveredLine only report a problem if that is
// true. By default, these warnings are disabled, because those
// conditions occur occasionally in healthy code.
virtual bool uncovered_warnings_enabled() const {
return uncovered_warnings_enabled_;
}
virtual void set_uncovered_warnings_enabled(bool value) {
uncovered_warnings_enabled_ = value;
}
// 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.
@ -154,6 +164,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
string cu_name_;
bool printed_cu_header_;
bool printed_unpaired_header_;
bool uncovered_warnings_enabled_;
private:
// Print a per-CU heading, once.

View File

@ -34,7 +34,7 @@
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/linux/dwarf_cu_to_module.h"
#include "common/dwarf_cu_to_module.h"
using std::vector;
@ -828,7 +828,7 @@ struct Situation {
uncovered_functions, uncovered_lines },
Situation situations[] = {
#include "common/linux/testdata/func-line-pairing.h"
#include "common/testdata/func-line-pairing.h"
};
#undef PAIRING
@ -1633,5 +1633,69 @@ TEST_F(Errors, BadCURootDIETag) {
no_attrs));
}
// Tests for DwarfCUToModule::Reporter. These just produce (or fail to
// produce) output, so their results need to be checked by hand.
struct Reporter: public Test {
Reporter()
: reporter("filename", 0x123456789abcdef0ULL) {
reporter.SetCUName("compilation-unit-name");
function.name = "function name";
function.address = 0x19c45c30770c1eb0ULL;
function.size = 0x89808a5bdfa0a6a3ULL;
function.parameter_size = 0x6a329f18683dcd51ULL;
file.name = "source file name";
line.address = 0x3606ac6267aebeccULL;
line.size = 0x5de482229f32556aULL;
line.file = &file;
line.number = 93400201;
}
DwarfCUToModule::WarningReporter reporter;
Module::Function function;
Module::File file;
Module::Line line;
};
TEST_F(Reporter, UnknownSpecification) {
reporter.UnknownSpecification(0x123456789abcdef1ULL, 0x323456789abcdef2ULL);
}
TEST_F(Reporter, UnknownAbstractOrigin) {
reporter.UnknownAbstractOrigin(0x123456789abcdef1ULL, 0x323456789abcdef2ULL);
}
TEST_F(Reporter, MissingSection) {
reporter.MissingSection("section name");
}
TEST_F(Reporter, BadLineInfoOffset) {
reporter.BadLineInfoOffset(0x123456789abcdef1ULL);
}
TEST_F(Reporter, UncoveredFunctionDisabled) {
reporter.UncoveredFunction(function);
EXPECT_FALSE(reporter.uncovered_warnings_enabled());
}
TEST_F(Reporter, UncoveredFunctionEnabled) {
reporter.set_uncovered_warnings_enabled(true);
reporter.UncoveredFunction(function);
EXPECT_TRUE(reporter.uncovered_warnings_enabled());
}
TEST_F(Reporter, UncoveredLineDisabled) {
reporter.UncoveredLine(line);
EXPECT_FALSE(reporter.uncovered_warnings_enabled());
}
TEST_F(Reporter, UncoveredLineEnabled) {
reporter.set_uncovered_warnings_enabled(true);
reporter.UncoveredLine(line);
EXPECT_TRUE(reporter.uncovered_warnings_enabled());
}
// Would be nice to also test:
// - overlapping lines, functions

View File

@ -32,7 +32,7 @@
// 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"
#include "common/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

View File

@ -38,7 +38,7 @@
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#include "common/linux/module.h"
#include "common/module.h"
#include "common/dwarf/dwarf2reader.h"
namespace google_breakpad {

View File

@ -32,7 +32,7 @@
// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule.
#include "breakpad_googletest_includes.h"
#include "common/linux/dwarf_line_to_module.h"
#include "common/dwarf_line_to_module.h"
using google_breakpad::DwarfLineToModule;
using google_breakpad::Module;

View File

@ -32,7 +32,7 @@
// language.cc: Subclasses and singletons for google_breakpad::Language.
// See language.h for details.
#include "common/linux/language.h"
#include "common/language.h"
namespace google_breakpad {

View File

@ -32,39 +32,40 @@
// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
// Find all the debugging info in a file and dump it as a Breakpad symbol file.
#include "common/linux/dump_symbols.h"
#include <assert.h>
#include <elf.h>
#include <errno.h>
#include <fcntl.h>
#include <link.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <cassert>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#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/dwarf_cfi_to_module.h"
#include "common/dwarf_cu_to_module.h"
#include "common/dwarf_line_to_module.h"
#include "common/linux/file_id.h"
#include "common/linux/module.h"
#include "common/linux/stabs_reader.h"
#include "common/module.h"
#include "common/stabs_reader.h"
#include "common/stabs_to_module.h"
// 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 google_breakpad::StabsToModule;
// Fix offset into virtual address by adding the mapped base into offsets.
// Make life easier when want to find something by offset.
@ -123,17 +124,32 @@ static const ElfW(Shdr) *FindSectionByName(const char *name,
return NULL;
}
static bool LoadStabs(const ElfW(Shdr) *stab_section,
static bool LoadStabs(const ElfW(Ehdr) *elf_header,
const ElfW(Shdr) *stab_section,
const ElfW(Shdr) *stabstr_section,
Module *module) {
// Figure out what endianness this file is.
bool big_endian;
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
big_endian = false;
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
big_endian = true;
else {
fprintf(stderr, "bad data encoding in ELF header: %d\n",
elf_header->e_ident[EI_DATA]);
return false;
}
// A callback object to handle data from the STABS reader.
DumpStabsHandler handler(module);
StabsToModule handler(module);
// Find the addresses of the STABS data, and create a STABS reader object.
// On Linux, STABS entries always have 32-bit values, regardless of the
// address size of the architecture whose code they're describing, and
// the strings are always "unitized".
uint8_t *stabs = reinterpret_cast<uint8_t *>(stab_section->sh_offset);
uint8_t *stabstr = reinterpret_cast<uint8_t *>(stabstr_section->sh_offset);
google_breakpad::StabsReader reader(stabs, stab_section->sh_size,
stabstr, stabstr_section->sh_size,
&handler);
big_endian, 4, true, &handler);
// Read the STABS data, and do post-processing.
if (!reader.Process())
return false;
@ -169,8 +185,8 @@ static bool LoadDwarf(const string &dwarf_filename,
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]);
fprintf(stderr, "%s: bad data encoding in ELF header: %d\n",
dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]);
return false;
}
dwarf2reader::ByteReader byte_reader(endianness);
@ -224,58 +240,20 @@ static bool LoadDwarf(const string &dwarf_filename,
// 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;
vector<string> *register_names) {
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;
case EM_386:
*register_names = DwarfCFIToModule::RegisterNames::I386();
return true;
case EM_ARM:
*register_names = DwarfCFIToModule::RegisterNames::ARM();
return true;
case EM_X86_64:
*register_names = DwarfCFIToModule::RegisterNames::X86_64();
return true;
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,
@ -334,7 +312,7 @@ static bool LoadDwarfCFI(const string &dwarf_filename,
if (got_section)
byte_reader.SetDataBase(got_section->sh_addr);
if (text_section)
byte_reader.SetTextBase(got_section->sh_addr);
byte_reader.SetTextBase(text_section->sh_addr);
dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
section_name);
@ -367,9 +345,9 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
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");
if (!LoadStabs(elf_header, stab_section, stabstr_section, module))
fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS"
" debugging information\n", obj_file.c_str());
}
}
@ -380,8 +358,8 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
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");
fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
"DWARF debugging information\n", obj_file.c_str());
}
// Dwarf Call Frame Information (CFI) is actually independent from
@ -416,8 +394,9 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
}
if (!found_debug_info_section) {
fprintf(stderr, "file contains no debugging information"
" (no \".stab\" or \".debug_info\" sections)\n");
fprintf(stderr, "%s: file contains no debugging information"
" (no \".stab\" or \".debug_info\" sections)\n",
obj_file.c_str());
return false;
}
return true;
@ -558,14 +537,15 @@ bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) {
unsigned char identifier[16];
google_breakpad::FileID file_id(obj_file.c_str());
if (!file_id.ElfFileIdentifier(identifier)) {
fprintf(stderr, "Unable to generate file identifier\n");
fprintf(stderr, "%s: unable to generate file identifier\n",
obj_file.c_str());
return false;
}
const char *architecture = ElfArchitecture(elf_header);
if (!architecture) {
fprintf(stderr, "Unrecognized ELF machine architecture: %d\n",
elf_header->e_machine);
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
obj_file.c_str(), elf_header->e_machine);
return false;
}

View File

@ -35,8 +35,9 @@
#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__
#define COMMON_LINUX_DUMP_SYMBOLS_H__
#include <stdio.h>
#include <string>
#include <cstdio>
namespace google_breakpad {

View File

@ -33,21 +33,22 @@
//
#include "common/linux/file_id.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/linux_syscall_support.h"
#include <arpa/inet.h>
#include <assert.h>
#include <elf.h>
#include <fcntl.h>
#include <link.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include <cassert>
#include <cstdio>
#include "common/linux/linux_libc_support.h"
#include "common/linux/linux_syscall_support.h"
namespace google_breakpad {

View File

@ -30,12 +30,15 @@
#include "common/linux/google_crashdump_uploader.h"
#include "common/linux/libcurl_wrapper.h"
#include "third_party/linux/include/glog/logging.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
using std::string;
namespace google_breakpad {
GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product,
@ -115,21 +118,21 @@ void GoogleCrashdumpUploader::Init(const std::string& product,
proxy_host_ = proxy_host;
proxy_userpassword_ = proxy_userpassword;
minidump_pathname_ = minidump_pathname;
LOG(INFO) << "Uploader initializing";
LOG(INFO) << "\tProduct: " << product_;
LOG(INFO) << "\tVersion: " << version_;
LOG(INFO) << "\tGUID: " << guid_;
std::cout << "Uploader initializing";
std::cout << "\tProduct: " << product_;
std::cout << "\tVersion: " << version_;
std::cout << "\tGUID: " << guid_;
if (!ptime_.empty()) {
LOG(INFO) << "\tProcess uptime: " << ptime_;
std::cout << "\tProcess uptime: " << ptime_;
}
if (!ctime_.empty()) {
LOG(INFO) << "\tCumulative Process uptime: " << ctime_;
std::cout << "\tCumulative Process uptime: " << ctime_;
}
if (!email_.empty()) {
LOG(INFO) << "\tEmail: " << email_;
std::cout << "\tEmail: " << email_;
}
if (!comments_.empty()) {
LOG(INFO) << "\tComments: " << comments_;
std::cout << "\tComments: " << comments_;
}
}
@ -152,7 +155,7 @@ bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() {
}
if (!error_text.empty()) {
LOG(ERROR) << error_text;
std::cout << error_text;
return false;
}
return true;
@ -162,7 +165,7 @@ bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() {
bool GoogleCrashdumpUploader::Upload() {
bool ok = http_layer_->Init();
if (!ok) {
LOG(WARNING) << "http layer init failed";
std::cout << "http layer init failed";
return ok;
}
@ -173,7 +176,7 @@ bool GoogleCrashdumpUploader::Upload() {
struct stat st;
int err = stat(minidump_pathname_.c_str(), &st);
if (err) {
LOG(WARNING) << minidump_pathname_ << " could not be found: " << errno;
std::cout << minidump_pathname_ << " could not be found";
return false;
}
@ -188,7 +191,7 @@ bool GoogleCrashdumpUploader::Upload() {
"upload_file_minidump")) {
return false;
}
LOG(INFO) << "Sending request to " << crash_server_;
std::cout << "Sending request to " << crash_server_;
return http_layer_->SendRequest(crash_server_,
parameters_,
NULL);

View File

@ -27,14 +27,14 @@
// (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 <cassert>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <unistd.h>
#include "common/linux/guid_creator.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
//
// GUIDGenerator
//

View File

@ -27,14 +27,14 @@
// (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 <cassert>
#include "common/linux/http_upload.h"
#include <assert.h>
#include <dlfcn.h>
#include <curl/curl.h>
#include <curl/easy.h>
#include <curl/types.h>
#include "common/linux/http_upload.h"
namespace {
// Callback to get the response data from server.

View File

@ -32,10 +32,12 @@
#include <curl/types.h>
#include <dlfcn.h>
#include <iostream>
#include <string>
#include "common/linux/libcurl_wrapper.h"
#include "third_party/linux/include/glog/logging.h"
using std::string;
namespace google_breakpad {
LibcurlWrapper::LibcurlWrapper()
@ -51,10 +53,10 @@ LibcurlWrapper::LibcurlWrapper()
curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW);
}
if (!curl_lib_) {
LOG(WARNING) << "Could not find libcurl via dlopen";
std::cout << "Could not find libcurl via dlopen";
return;
}
LOG(INFO) << "LibcurlWrapper init succeeded";
std::cout << "LibcurlWrapper init succeeded";
init_ok_ = true;
return;
}
@ -68,16 +70,16 @@ bool LibcurlWrapper::SetProxy(const std::string& proxy_host,
if (!proxy_host.empty()) {
(*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str());
} else {
LOG(WARNING) << "SetProxy called with empty proxy host.";
std::cout << "SetProxy called with empty proxy host.";
return false;
}
if (!proxy_userpwd.empty()) {
(*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str());
} else {
LOG(WARNING) << "SetProxy called with empty proxy username/password.";
std::cout << "SetProxy called with empty proxy username/password.";
return false;
}
LOG(INFO) << "Set proxy host to " << proxy_host;
std::cout << "Set proxy host to " << proxy_host;
return true;
}
@ -86,7 +88,7 @@ bool LibcurlWrapper::AddFile(const std::string& upload_file_path,
if (!init_ok_) {
return false;
}
LOG(INFO) << "Adding " << upload_file_path << " to form upload.";
std::cout << "Adding " << upload_file_path << " to form upload.";
// Add form file.
(*formadd_)(&formpost_, &lastptr_,
CURLFORM_COPYNAME, basename.c_str(),
@ -128,7 +130,9 @@ bool LibcurlWrapper::SendRequest(const std::string& url,
CURLcode err_code = CURLE_OK;
err_code = (*easy_perform_)(curl_);
*(void**) (&easy_strerror_) = dlsym(curl_lib_, "curl_easy_strerror");
easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
(dlsym(curl_lib_, "curl_easy_strerror"));
#ifndef NDEBUG
if (err_code != CURLE_OK)
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
@ -149,12 +153,12 @@ bool LibcurlWrapper::SendRequest(const std::string& url,
bool LibcurlWrapper::Init() {
if (!init_ok_) {
LOG(WARNING) << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages";
std::cout << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages";
return false;
}
if (!SetFunctionPointers()) {
LOG(WARNING) << "Could not find function pointers";
std::cout << "Could not find function pointers";
init_ok_ = false;
return false;
}
@ -165,7 +169,7 @@ bool LibcurlWrapper::Init() {
if (!curl_) {
dlclose(curl_lib_);
LOG(WARNING) << "Curl initialization failed";
std::cout << "Curl initialization failed";
return false;
}
@ -177,10 +181,10 @@ bool LibcurlWrapper::Init() {
return true;
}
#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name) \
*(void**) (&var) = dlsym(curl_lib_, function_name); \
#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name, type) \
var = reinterpret_cast<type>(dlsym(curl_lib_, function_name)); \
if (!var) { \
LOG(WARNING) << "Could not find libcurl function " << function_name; \
std::cout << "Could not find libcurl function " << function_name; \
init_ok_ = false; \
return false; \
}
@ -188,21 +192,34 @@ bool LibcurlWrapper::Init() {
bool LibcurlWrapper::SetFunctionPointers() {
SET_AND_CHECK_FUNCTION_POINTER(easy_init_,
"curl_easy_init");
"curl_easy_init",
CURL*(*)());
SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_,
"curl_easy_setopt");
SET_AND_CHECK_FUNCTION_POINTER(formadd_,
"curl_formadd");
SET_AND_CHECK_FUNCTION_POINTER(slist_append_,
"curl_slist_append");
"curl_easy_setopt",
CURLcode(*)(CURL*, CURLoption, ...));
SET_AND_CHECK_FUNCTION_POINTER(formadd_, "curl_formadd",
CURLFORMcode(*)(curl_httppost**, curl_httppost**, ...));
SET_AND_CHECK_FUNCTION_POINTER(slist_append_, "curl_slist_append",
curl_slist*(*)(curl_slist*, const char*));
SET_AND_CHECK_FUNCTION_POINTER(easy_perform_,
"curl_easy_perform");
"curl_easy_perform",
CURLcode(*)(CURL*));
SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_,
"curl_easy_cleanup");
"curl_easy_cleanup",
void(*)(CURL*));
SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_,
"curl_slist_free_all");
"curl_slist_free_all",
void(*)(curl_slist*));
SET_AND_CHECK_FUNCTION_POINTER(formfree_,
"curl_formfree");
"curl_formfree",
void(*)(curl_httppost*));
return true;
}

View File

@ -30,9 +30,10 @@
// A wrapper for libcurl to do HTTP Uploads, to support easy mocking
// and unit testing of the HTTPUpload class.
#include <curl/curl.h>
#include <string>
#include <map>
#include <curl/curl.h>
namespace google_breakpad {
class LibcurlWrapper {

View File

@ -2214,6 +2214,8 @@ struct kernel_statfs {
#endif
LSS_INLINE _syscall1(pid_t, getsid, pid_t, p)
LSS_INLINE _syscall0(pid_t, _gettid)
LSS_INLINE _syscall2(pid_t, gettimeofday, struct kernel_timeval*, t,
void*, tz)
LSS_INLINE _syscall5(int, setxattr, const char *,p,
const char *, n, const void *,v,
size_t, s, int, f)

View File

@ -1,685 +0,0 @@
// 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>
// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader.
#include <a.out.h>
#include <cassert>
#include <cerrno>
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <stab.h>
#include "breakpad_googletest_includes.h"
#include "common/linux/stabs_reader.h"
using std::istream;
using std::istringstream;
using std::map;
using std::ostream;
using std::ostringstream;
using std::string;
using ::testing::_;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrEq;
using google_breakpad::StabsHandler;
using google_breakpad::StabsReader;
namespace {
// Mock stabs file parser
//
// In order to test StabsReader, we parse a human-readable input file
// describing STABS entries into in-memory .stab and .stabstr
// sections, and then pass those to StabsReader to look at. The
// human-readable file is called a "mock stabs file".
//
// 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
//
// where all data is Latin-1 bytes and fields are separated by single
// space characters, except for NAME, which may contain spaces and
// continues to the end of the line. The fields have the following
// meanings:
//
// - TYPE: the name of the stabs symbol type; like SO or FUN. These are
// the names from /usr/include/bits/stab.def, without the leading N_.
//
// - OTHER, DESC, VALUE: numeric values for the n_other, n_desc, and
// n_value fields of the stab. These can be decimal or hex,
// using C++ notation (10, 0x10)
//
// - NAME: textual data for the entry. STABS packs all kinds of
// interesting data into entries' NAME fields, so calling it a NAME
// 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
// old-fashioned functions wasn't astonishingly clear either, so it
// seemed worth a try.
// A handler class for mock stabs data.
class MockStabsHandler {
public:
MockStabsHandler() { }
virtual ~MockStabsHandler() { }
// The mock stabs parser calls this member function for each entry
// it parses, passing it the contents of the entry. If this function
// returns true, the parser continues; if it returns false, the parser
// 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.
virtual bool Error(const char *format, ...) = 0;
};
// A class for parsing mock stabs files.
class MockStabsParser {
public:
// Create a parser reading input from STREAM and passing data to HANDLER.
// Use FILENAME when reporting errors.
MockStabsParser(const string &filename, istream *stream,
MockStabsHandler *handler);
// Parse data from the STREAM, invoking HANDLER->Entry for each
// entry we get. Return true if we parsed all the data succesfully,
// or false if we stopped early because Entry returned false, or if
// there were any errors during parsing.
bool Process();
private:
// A type for maps from stab type names ("SO", "SLINE", etc.) to
// n_type values.
typedef map<string, unsigned char> StabTypeNameTable;
// Initialize the table mapping STAB type names to n_type values.
void InitializeTypeNames();
// Parse LINE, one line of input from a mock stabs file, and pass
// its contents to handler_->Entry and return the boolean value that
// returns. If we encounter an error parsing the line, report it
// using handler->Error.
bool ParseLine(const string &line);
const string &filename_;
istream *stream_;
MockStabsHandler *handler_;
int line_number_;
StabTypeNameTable type_names_;
};
MockStabsParser::MockStabsParser(const string &filename, istream *stream,
MockStabsHandler *handler):
filename_(filename), stream_(stream), handler_(handler),
line_number_(0) {
InitializeTypeNames();
}
bool MockStabsParser::Process() {
// Iterate once per line, including a line at EOF without a
// terminating newline.
for(;;) {
string line;
getline(*stream_, line, '\n');
if (line.empty() && stream_->eof())
break;
line_number_++;
if (! ParseLine(line))
return false;
}
return true;
}
void MockStabsParser::InitializeTypeNames() {
// On GLIBC-based systems, <bits/stab.def> is a file containing a
// call to an unspecified macro __define_stab for each stab type.
// <stab.h> uses it to define the __stab_debug_code enum type. We
// use it here to initialize our mapping from type names to enum
// values.
//
// This isn't portable to non-GLIBC systems. Feel free to just
// hard-code the values if this becomes a problem.
# define __define_stab(name, code, str) type_names_[string(str)] = code;
# include <bits/stab.def>
# undef __define_stab
}
bool MockStabsParser::ParseLine(const string &line) {
istringstream linestream(line);
// Allow "0x" prefix for hex, and so on.
linestream.unsetf(istringstream::basefield);
// Parse and validate the stabs type.
string typeName;
linestream >> typeName;
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.
//
// A .stab section is an array of struct nlist entries. These
// entries' n_un.n_strx fields are indices into an accompanying
// .stabstr section.
class StabSection {
public:
StabSection(): used_(0), size_(1) {
entries_ = (struct nlist *) malloc(sizeof(*entries_) * size_);
}
~StabSection() { free(entries_); }
// Append a new 'struct nlist' entry to the end of the section, and
// return a pointer to it. This pointer is valid until the next
// 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,
struct nlist *entries_;
// The number of elements of entries_ that are used, and the allocated size
// of the array.
size_t used_, size_;
};
struct nlist *StabSection::Append() {
if (used_ == size_) {
size_ *= 2;
entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_);
}
assert(used_ < size_);
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
// null-terminated strings. A string is identified by the index of
// its initial character in the array. The array always starts with a
// null byte, so that an index of zero refers to the empty string.
//
// This implementation also ensures that if two strings are equal, we
// assign them the same indices; most linkers do this, and some
// clients may rely upon it. (Note that this is not quite the same as
// ensuring that a string only appears once in the section; you could
// share space when one string is a suffix of another, but we don't.)
class StabstrSection {
public:
StabstrSection(): next_byte_(1) { string_indices_[""] = 0; }
// Ensure STR is present in the string section, and return its index.
size_t Insert(const string &str);
// 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;
typedef map<size_t, const string *> IndexToString;
// A map from strings to the indices we've assigned them.
StringToIndex string_indices_;
// The next unused byte in the section. The next string we add
// will get this index.
size_t next_byte_;
};
size_t StabstrSection::Insert(const string &str) {
StringToIndex::iterator it = string_indices_.find(str);
size_t index;
if (it != string_indices_.end()) {
index = it->second;
} else {
// This is the first time we've seen STR; add it to the table.
string_indices_[str] = next_byte_;
index = next_byte_;
next_byte_ += str.size() + 1;
}
return index;
}
void StabstrSection::GetSection(string *section) {
// First we have to invert the map.
IndexToString byIndex;
for (StringToIndex::const_iterator it = string_indices_.begin();
it != string_indices_.end(); it++)
byIndex[it->second] = &it->first;
// Now we build the .stabstr section.
section->clear();
for (IndexToString::const_iterator it = byIndex.begin();
it != byIndex.end(); it++) {
// Make sure we're actually assigning it the index we claim to be.
assert(it->first == section->size());
*section += *(it->second);
*section += '\0';
}
}
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 {
public:
// Construct a handler that will receive data from a MockStabsParser
// 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), 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);
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.
void GetStab(string *section);
void GetStabstr(string *section);
private:
// 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,
short desc, unsigned long value,
const string &name) {
struct nlist *entry = stab_.Append();
entry->n_type = type;
entry->n_other = 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);
vfprintf(stderr, format, args);
va_end(args);
error_count_++;
if (error_count_ >= 20) {
fprintf(stderr,
"%s: lots of errors; is this really a mock stabs file?\n",
filename_.c_str());
return false;
}
return true;
}
void StabsSectionsBuilder::GetStab(string *section) {
FinishCU();
*section = finished_cu_stab_;
}
void StabsSectionsBuilder::GetStabstr(string *section) {
FinishCU();
*section = finished_cu_stabstr_;
}
class MockStabsReaderHandler: public StabsHandler {
public:
MOCK_METHOD3(StartCompilationUnit,
bool(const char *, uint64_t, const char *));
MOCK_METHOD1(EndCompilationUnit, bool(uint64_t));
MOCK_METHOD2(StartFunction, bool(const std::string &, uint64_t));
MOCK_METHOD1(EndFunction, bool(uint64_t));
MOCK_METHOD3(Line, bool(uint64_t, const char *, int));
void Warning(const char *format, ...) { MockWarning(format); }
MOCK_METHOD1(MockWarning, void(const char *));
};
// Create a StabsReader to parse the mock stabs data in INPUT_FILE,
// passing the parsed information to HANDLER. If all goes well, return
// the result of calling the reader's Process member function.
// Otherwise, return false. INPUT_FILE should be relative to the top
// of the source tree.
static bool ApplyHandlerToMockStabsData(StabsHandler *handler,
const string &input_file) {
string full_input_file
= string(getenv("srcdir") ? getenv("srcdir") : ".") + "/" + input_file;
// Open the input file.
std::ifstream stream(full_input_file.c_str());
if (stream.fail()) {
fprintf(stderr, "error opening mock stabs input file %s: %s\n",
full_input_file.c_str(), strerror(errno));
return false;
}
// Parse the mock stabs data, and produce stabs sections to use as
// test input to the reader.
StabsSectionsBuilder builder(full_input_file);
MockStabsParser mock_parser(full_input_file, &stream, &builder);
if (!mock_parser.Process())
return false;
string stab, stabstr;
builder.GetStab(&stab);
builder.GetStabstr(&stabstr);
// Run the parser on the test input, passing whatever we find to HANDLER.
StabsReader reader(
reinterpret_cast<const uint8_t *>(stab.data()), stab.size(),
reinterpret_cast<const uint8_t *>(stabstr.data()), stabstr.size(),
handler);
return reader.Process();
}
TEST(StabsReader, MockStabsInput) {
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file1.c"),
0x42, StrEq("builddir1/")))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0x62))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, Line(0xe4, StrEq("file1.c"), 91))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, Line(0x164, StrEq("header.h"), 111))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndFunction(0x112))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0x112))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, Line(0x234, StrEq("header.h"), 131))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, Line(0x254, StrEq("file1.c"), 151))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndFunction(0x152))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(0x152))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"),
0x182, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(0x192))
.WillOnce(Return(true));
}
ASSERT_TRUE(ApplyHandlerToMockStabsData(
&mock_handler,
"common/linux/testdata/stabs_reader_unittest.input1"));
}
TEST(StabsReader, AbruptCU) {
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler,
StartCompilationUnit(StrEq("file2-1.c"), 0x12, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
.WillOnce(Return(true));
}
ASSERT_TRUE(ApplyHandlerToMockStabsData(
&mock_handler,
"common/linux/testdata/stabs_reader_unittest.input2"));
}
TEST(StabsReader, AbruptFunction) {
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler,
StartCompilationUnit(StrEq("file3-1.c"), 0x12, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0x22))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndFunction(NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
.WillOnce(Return(true));
}
ASSERT_TRUE(ApplyHandlerToMockStabsData(
&mock_handler,
"common/linux/testdata/stabs_reader_unittest.input3"));
}
TEST(StabsReader, NoCU) {
MockStabsReaderHandler mock_handler;
EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _))
.Times(0);
EXPECT_CALL(mock_handler, StartFunction(_, _))
.Times(0);
ASSERT_TRUE(ApplyHandlerToMockStabsData(
&mock_handler,
"common/linux/testdata/stabs_reader_unittest.input4"));
}
TEST(StabsReader, NoCUEnd) {
MockStabsReaderHandler mock_handler;
{
InSequence s;
EXPECT_CALL(mock_handler,
StartCompilationUnit(StrEq("file5-1.c"), 0x12, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler,
StartCompilationUnit(StrEq("file5-2.c"), 0x22, NULL))
.WillOnce(Return(true));
EXPECT_CALL(mock_handler, EndCompilationUnit(NULL))
.WillOnce(Return(true));
}
ASSERT_TRUE(ApplyHandlerToMockStabsData(
&mock_handler,
"common/linux/testdata/stabs_reader_unittest.input5"));
}
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

View File

@ -1,19 +0,0 @@
SO 10 11 0x02 builddir/
FUN 20 21 0x12 not the SO with source file name we expected
SO 30 31 0x22
SO 40 41 0x32 builddir1/
SO 50 51 0x42 file1.c
LSYM 60 61 0x52 not the FUN we're looking for
FUN 70 71 0x62 fun1
BINCL 80 81 0x72 something to ignore in a FUN body
SLINE 90 91 0x82
SOL 100 101 0x92 header.h
SLINE 110 111 0x102
FUN 120 121 0x112 fun2:some stabs type info here, to trim from the name
SLINE 130 131 0x122
SOL 140 141 0x132 file1.c
SLINE 150 151 0x142
SO 160 161 0x152
LSYM 170 171 0x162
SO 180 181 0x182 file3.c
SO 190 191 0x192

View File

@ -1,2 +0,0 @@
SO 10 11 0x12 file3-1.c
FUN 20 21 0x22 fun3_1

View File

@ -1 +0,0 @@
SO 10 11 0x12 build-directory/

View File

@ -1,2 +0,0 @@
SO 10 11 0x12 file5-1.c
SO 20 21 0x22 file5-2.c

View File

@ -1,8 +0,0 @@
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

View File

@ -0,0 +1,48 @@
// -*- 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 <jim@mozilla.com> <jimb@red-bean.com>
// byteswap.h: Overloaded functions for conveniently byteswapping values.
#ifndef COMMON_MAC_BYTESWAP_H_
#define COMMON_MAC_BYTESWAP_H_
#include <libkern/OSByteOrder.h>
static inline uint16_t ByteSwap(uint16_t v) { return OSSwapInt16(v); }
static inline uint32_t ByteSwap(uint32_t v) { return OSSwapInt32(v); }
static inline uint64_t ByteSwap(uint64_t v) { return OSSwapInt64(v); }
static inline int16_t ByteSwap(int16_t v) { return OSSwapInt16(v); }
static inline int32_t ByteSwap(int32_t v) { return OSSwapInt32(v); }
static inline int64_t ByteSwap(int64_t v) { return OSSwapInt64(v); }
#endif // COMMON_MAC_BYTESWAP_H_

View File

@ -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
@ -27,53 +29,133 @@
// (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_syms.h: Interface for DumpSymbols. This class will take a mach-o file
// and extract the symbol information and write it to a file using the
// breakpad symbol file format.
// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
#import <Foundation/Foundation.h>
// dump_syms.h: Declaration of google_breakpad::DumpSymbols, a class for
// reading debugging information from Mach-O files and writing it out as a
// Breakpad symbol file.
#include <Foundation/Foundation.h>
#include <mach-o/loader.h>
#include "common/dwarf/dwarf2reader.h"
#include <stdio.h>
#include <stdlib.h>
// This will map from an architecture string to a SectionMap, which
// will contain the offsets for all the sections in the dictionary
typedef map<string, dwarf2reader::SectionMap *> ArchSectionMap;
#include <string>
#include <vector>
@interface DumpSymbols : NSObject {
@protected
NSString *sourcePath_; // Source of symbols (STRONG)
NSString *architecture_; // Architecture to extract (STRONG)
NSMutableDictionary *addresses_; // Addresses and symbols (STRONG)
NSMutableSet *functionAddresses_; // Function addresses (STRONG)
NSMutableDictionary *sources_; // Address and Source file paths (STRONG)
NSMutableDictionary *headers_; // Mach-o header information (STRONG)
NSMutableDictionary *sectionData_; // Keyed by seg/sect name (STRONG)
uint32_t lastStartAddress_;
ArchSectionMap *sectionsForArch_;
}
#include "common/byte_cursor.h"
#include "common/mac/macho_reader.h"
#include "common/module.h"
- (id)initWithContentsOfFile:(NSString *)machoFile;
namespace google_breakpad {
- (NSArray *)availableArchitectures;
class DumpSymbols {
public:
DumpSymbols()
: input_pathname_(),
object_filename_(),
contents_(),
selected_object_file_(),
selected_object_name_() { }
~DumpSymbols() {
[input_pathname_ release];
[object_filename_ release];
[contents_ release];
}
// One of ppc, x86, i386, ppc64, x86_64
// If the architecture is not available, it will return NO
// If not set, the native architecture will be used
- (BOOL)setArchitecture:(NSString *)architecture;
- (NSString *)architecture;
// Prepare to read debugging information from |filename|. |filename| may be
// the name of a universal binary, a Mach-O file, or a dSYM bundle
// containing either of the above. On success, return true; if there is a
// problem reading |filename|, report it and return false.
//
// (This class uses NSString for filenames and related values,
// because the Mac Foundation framework seems to support
// filename-related operations more fully on NSString values.)
bool Read(NSString *filename);
// Write the symbols to |symbolFilePath|. Return YES if successful.
- (BOOL)writeSymbolFile:(NSString *)symbolFilePath;
// If this dumper's file includes an object file for |cpu_type| and
// |cpu_subtype|, then select that object file for dumping, and return
// true. Otherwise, return false, and leave this dumper's selected
// architecture unchanged.
//
// By default, if this dumper's file contains only one object file, then
// the dumper will dump those symbols; and if it contains more than one
// object file, then the dumper will dump the object file whose
// architecture matches that of this dumper program.
bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
@end
// Return a pointer to an array of 'struct fat_arch' structures,
// describing the object files contained in this dumper's file. Set
// *|count| to the number of elements in the array. The returned array is
// owned by this DumpSymbols instance.
//
// If there are no available architectures, this function
// may return NULL.
const struct fat_arch *AvailableArchitectures(size_t *count) {
*count = object_files_.size();
if (object_files_.size() > 0)
return &object_files_[0];
return NULL;
}
@interface MachSection : NSObject {
@protected
struct section *sect_;
uint32_t sectionNumber_;
}
- (id)initWithMachSection:(struct section *)sect andNumber:(uint32_t)sectionNumber;
- (struct section*)sectionPointer;
- (uint32_t)sectionNumber;
// Read the selected object file's debugging information, and write it
// out to |stream|. Return true on success; if an error occurs, report it
// and return false.
bool WriteSymbolFile(FILE *stream);
@end
private:
// Used internally.
class DumperLineToModule;
class LoadCommandDumper;
// Return an identifier string for the file this DumpSymbols is dumping.
std::string Identifier();
// Read debugging information from |dwarf_sections|, which was taken from
// |macho_reader|, and add it to |module|. On success, return true;
// on failure, report the problem and return false.
bool ReadDwarf(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
const mach_o::SectionMap &dwarf_sections) const;
// Read DWARF CFI or .eh_frame data from |section|, belonging to
// |macho_reader|, and record it in |module|. If |eh_frame| is true,
// then the data is .eh_frame-format data; otherwise, it is standard DWARF
// .debug_frame data. On success, return true; on failure, report
// the problem and return false.
bool ReadCFI(google_breakpad::Module *module,
const mach_o::Reader &macho_reader,
const mach_o::Section &section,
bool eh_frame) const;
// The name of the file or bundle whose symbols this will dump.
// This is the path given to Read, for use in error messages.
NSString *input_pathname_;
// The name of the file this DumpSymbols will actually read debugging
// information from. Normally, this is the same as input_pathname_, but if
// filename refers to a dSYM bundle, then this is the resource file
// within that bundle.
NSString *object_filename_;
// The complete contents of object_filename_, mapped into memory.
NSData *contents_;
// A vector of fat_arch structures describing the object files
// object_filename_ contains. If object_filename_ refers to a fat binary,
// this may have more than one element; if it refers to a Mach-O file, this
// has exactly one element.
vector<struct fat_arch> object_files_;
// The object file in object_files_ selected to dump, or NULL if
// SetArchitecture hasn't been called yet.
const struct fat_arch *selected_object_file_;
// A string that identifies the selected object file, for use in error
// messages. This is usually object_filename_, but if that refers to a
// fat binary, it includes an indication of the particular architecture
// within that binary.
string selected_object_name_;
};
} // namespace google_breakpad

View File

@ -0,0 +1,524 @@
// 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>
// macho_reader.cc: Implementation of google_breakpad::Mach_O::FatReader and
// google_breakpad::Mach_O::Reader. See macho_reader.h for details.
#include "common/mac/macho_reader.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
namespace google_breakpad {
namespace mach_o {
// If NDEBUG is #defined, then the 'assert' macro doesn't evaluate its
// arguments, so you can't place expressions that do necessary work in
// the argument of an assert. Nor can you assign the result of the
// expression to a variable and assert that the variable's value is
// true: you'll get unused variable warnings when NDEBUG is #defined.
//
// ASSERT_ALWAYS_EVAL always evaluates its argument, and asserts that
// the result is true if NDEBUG is not #defined.
#if defined(NDEBUG)
#define ASSERT_ALWAYS_EVAL(x) (x)
#else
#define ASSERT_ALWAYS_EVAL(x) assert(x)
#endif
void FatReader::Reporter::BadHeader() {
fprintf(stderr, "%s: file is neither a fat binary file"
" nor a Mach-O object file\n", filename_.c_str());
}
void FatReader::Reporter::TooShort() {
fprintf(stderr, "%s: file too short for the data it claims to contain\n",
filename_.c_str());
}
void FatReader::Reporter::MisplacedObjectFile() {
fprintf(stderr, "%s: file too short for the object files it claims"
" to contain\n", filename_.c_str());
}
bool FatReader::Read(const uint8_t *buffer, size_t size) {
buffer_.start = buffer;
buffer_.end = buffer + size;
ByteCursor cursor(&buffer_);
// Fat binaries always use big-endian, so read the magic number in
// that endianness. To recognize Mach-O magic numbers, which can use
// either endianness, check for both the proper and reversed forms
// of the magic numbers.
cursor.set_big_endian(true);
if (cursor >> magic_) {
if (magic_ == FAT_MAGIC) {
// How many object files does this fat binary contain?
uint32_t object_files_count;
if (!(cursor >> object_files_count)) { // nfat_arch
reporter_->TooShort();
return false;
}
// Read the list of object files.
object_files_.resize(object_files_count);
for (size_t i = 0; i < object_files_count; i++) {
struct fat_arch *objfile = &object_files_[i];
// Read this object file entry, byte-swapping as appropriate.
cursor >> objfile->cputype
>> objfile->cpusubtype
>> objfile->offset
>> objfile->size
>> objfile->align;
if (!cursor) {
reporter_->TooShort();
return false;
}
// Does the file actually have the bytes this entry refers to?
size_t fat_size = buffer_.Size();
if (objfile->offset > fat_size ||
objfile->size > fat_size - objfile->offset) {
reporter_->MisplacedObjectFile();
return false;
}
}
return true;
} else if (magic_ == MH_MAGIC || magic_ == MH_MAGIC_64 ||
magic_ == MH_CIGAM || magic_ == MH_CIGAM_64) {
// If this is a little-endian Mach-O file, fix the cursor's endianness.
if (magic_ == MH_CIGAM || magic_ == MH_CIGAM_64)
cursor.set_big_endian(false);
// Record the entire file as a single entry in the object file list.
object_files_.resize(1);
// Get the cpu type and subtype from the Mach-O header.
if (!(cursor >> object_files_[0].cputype
>> object_files_[0].cpusubtype)) {
reporter_->TooShort();
return false;
}
object_files_[0].offset = 0;
object_files_[0].size = buffer_.Size();
// This alignment is correct for 32 and 64-bit x86 and ppc.
// See get_align in the lipo source for other architectures:
// http://www.opensource.apple.com/source/cctools/cctools-773/misc/lipo.c
object_files_[0].align = 12; // 2^12 == 4096
return true;
}
}
reporter_->BadHeader();
return false;
}
void Reader::Reporter::BadHeader() {
fprintf(stderr, "%s: file is not a Mach-O object file\n", filename_.c_str());
}
void Reader::Reporter::CPUTypeMismatch(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
cpu_type_t expected_cpu_type,
cpu_subtype_t expected_cpu_subtype) {
fprintf(stderr, "%s: CPU type %d, subtype %d does not match expected"
" type %d, subtype %d\n",
filename_.c_str(), cpu_type, cpu_subtype,
expected_cpu_type, expected_cpu_subtype);
}
void Reader::Reporter::HeaderTruncated() {
fprintf(stderr, "%s: file does not contain a complete Mach-O header\n",
filename_.c_str());
}
void Reader::Reporter::LoadCommandRegionTruncated() {
fprintf(stderr, "%s: file too short to hold load command region"
" given in Mach-O header\n", filename_.c_str());
}
void Reader::Reporter::LoadCommandsOverrun(size_t claimed, size_t i,
LoadCommandType type) {
fprintf(stderr, "%s: file's header claims there are %ld"
" load commands, but load command #%ld",
filename_.c_str(), claimed, i);
if (type) fprintf(stderr, ", of type %d,", type);
fprintf(stderr, " extends beyond the end of the load command region\n");
}
void Reader::Reporter::LoadCommandTooShort(size_t i, LoadCommandType type) {
fprintf(stderr, "%s: the contents of load command #%ld, of type %d,"
" extend beyond the size given in the load command's header\n",
filename_.c_str(), i, type);
}
void Reader::Reporter::SectionsMissing(const string &name) {
fprintf(stderr, "%s: the load command for segment '%s'"
" is too short to hold the section headers it claims to have\n",
filename_.c_str(), name.c_str());
}
void Reader::Reporter::MisplacedSegmentData(const string &name) {
fprintf(stderr, "%s: the segment '%s' claims its contents lie beyond"
" the end of the file\n", filename_.c_str(), name.c_str());
}
void Reader::Reporter::MisplacedSectionData(const string &section,
const string &segment) {
fprintf(stderr, "%s: the section '%s' in segment '%s'"
" claims its contents lie outside the segment's contents\n",
filename_.c_str(), section.c_str(), segment.c_str());
}
void Reader::Reporter::MisplacedSymbolTable() {
fprintf(stderr, "%s: the LC_SYMTAB load command claims that the symbol"
" table's contents are located beyond the end of the file\n",
filename_.c_str());
}
void Reader::Reporter::UnsupportedCPUType(cpu_type_t cpu_type) {
fprintf(stderr, "%s: CPU type %d is not supported\n",
filename_.c_str(), cpu_type);
}
bool Reader::Read(const uint8_t *buffer,
size_t size,
cpu_type_t expected_cpu_type,
cpu_subtype_t expected_cpu_subtype) {
assert(!buffer_.start);
buffer_.start = buffer;
buffer_.end = buffer + size;
ByteCursor cursor(&buffer_, true);
uint32_t magic;
if (!(cursor >> magic)) {
reporter_->HeaderTruncated();
return false;
}
if (expected_cpu_type != CPU_TYPE_ANY) {
uint32_t expected_magic;
// validate that magic matches the expected cpu type
switch (expected_cpu_type) {
case CPU_TYPE_I386:
expected_magic = MH_CIGAM;
break;
case CPU_TYPE_POWERPC:
expected_magic = MH_MAGIC;
break;
case CPU_TYPE_X86_64:
expected_magic = MH_CIGAM_64;
break;
case CPU_TYPE_POWERPC64:
expected_magic = MH_MAGIC_64;
break;
default:
reporter_->UnsupportedCPUType(expected_cpu_type);
return false;
}
if (expected_magic != magic) {
reporter_->BadHeader();
return false;
}
}
// Since the byte cursor is in big-endian mode, a reversed magic number
// always indicates a little-endian file, regardless of our own endianness.
switch (magic) {
case MH_MAGIC: big_endian_ = true; bits_64_ = false; break;
case MH_CIGAM: big_endian_ = false; bits_64_ = false; break;
case MH_MAGIC_64: big_endian_ = true; bits_64_ = true; break;
case MH_CIGAM_64: big_endian_ = false; bits_64_ = true; break;
default:
reporter_->BadHeader();
return false;
}
cursor.set_big_endian(big_endian_);
uint32_t commands_size, reserved;
cursor >> cpu_type_ >> cpu_subtype_ >> file_type_ >> load_command_count_
>> commands_size >> flags_;
if (bits_64_)
cursor >> reserved;
if (!cursor) {
reporter_->HeaderTruncated();
return false;
}
if (expected_cpu_type != CPU_TYPE_ANY &&
(expected_cpu_type != cpu_type_ ||
expected_cpu_subtype != cpu_subtype_)) {
reporter_->CPUTypeMismatch(cpu_type_, cpu_subtype_,
expected_cpu_type, expected_cpu_subtype);
return false;
}
cursor
.PointTo(&load_commands_.start, commands_size)
.PointTo(&load_commands_.end, 0);
if (!cursor) {
reporter_->LoadCommandRegionTruncated();
return false;
}
return true;
}
bool Reader::WalkLoadCommands(Reader::LoadCommandHandler *handler) const {
ByteCursor list_cursor(&load_commands_, big_endian_);
for (size_t index = 0; index < load_command_count_; ++index) {
// command refers to this load command alone, so that cursor will
// refuse to read past the load command's end. But since we haven't
// read the size yet, let command initially refer to the entire
// remainder of the load command series.
ByteBuffer command(list_cursor.here(), list_cursor.Available());
ByteCursor cursor(&command, big_endian_);
// Read the command type and size --- fields common to all commands.
uint32_t type, size;
if (!(cursor >> type)) {
reporter_->LoadCommandsOverrun(load_command_count_, index, 0);
return false;
}
if (!(cursor >> size) || size > command.Size()) {
reporter_->LoadCommandsOverrun(load_command_count_, index, type);
return false;
}
// Now that we've read the length, restrict command's range to this
// load command only.
command.end = command.start + size;
switch (type) {
case LC_SEGMENT:
case LC_SEGMENT_64: {
Segment segment;
segment.bits_64 = (type == LC_SEGMENT_64);
size_t word_size = segment.bits_64 ? 8 : 4;
cursor.CString(&segment.name, 16);
size_t file_offset, file_size;
cursor
.Read(word_size, false, &segment.vmaddr)
.Read(word_size, false, &segment.vmsize)
.Read(word_size, false, &file_offset)
.Read(word_size, false, &file_size);
cursor >> segment.maxprot
>> segment.initprot
>> segment.nsects
>> segment.flags;
if (!cursor) {
reporter_->LoadCommandTooShort(index, type);
return false;
}
if (file_offset > buffer_.Size() ||
file_size > buffer_.Size() - file_offset) {
reporter_->MisplacedSegmentData(segment.name);
return false;
}
// Mach-O files in .dSYM bundles have the contents of the loaded
// segments removed, and their file offsets and file sizes zeroed
// out. To help us handle this special case properly, give such
// segments' contents NULL starting and ending pointers.
if (file_offset == 0 && file_size == 0) {
segment.contents.start = segment.contents.end = NULL;
} else {
segment.contents.start = buffer_.start + file_offset;
segment.contents.end = segment.contents.start + file_size;
}
// The section list occupies the remainder of this load command's space.
segment.section_list.start = cursor.here();
segment.section_list.end = command.end;
if (!handler->SegmentCommand(segment))
return false;
break;
}
case LC_SYMTAB: {
uint32_t symoff, nsyms, stroff, strsize;
cursor >> symoff >> nsyms >> stroff >> strsize;
if (!cursor) {
reporter_->LoadCommandTooShort(index, type);
return false;
}
// How big are the entries in the symbol table?
// sizeof(struct nlist_64) : sizeof(struct nlist),
// but be paranoid about alignment vs. target architecture.
size_t symbol_size = bits_64_ ? 16 : 12;
// How big is the entire symbol array?
size_t symbols_size = nsyms * symbol_size;
if (symoff > buffer_.Size() || symbols_size > buffer_.Size() - symoff ||
stroff > buffer_.Size() || strsize > buffer_.Size() - stroff) {
reporter_->MisplacedSymbolTable();
return false;
}
ByteBuffer entries(buffer_.start + symoff, symbols_size);
ByteBuffer names(buffer_.start + stroff, strsize);
if (!handler->SymtabCommand(entries, names))
return false;
break;
}
default: {
if (!handler->UnknownCommand(type, command))
return false;
break;
}
}
list_cursor.set_here(command.end);
}
return true;
}
// A load command handler that looks for a segment of a given name.
class Reader::SegmentFinder : public LoadCommandHandler {
public:
// Create a load command handler that looks for a segment named NAME,
// and sets SEGMENT to describe it if found.
SegmentFinder(const string &name, Segment *segment)
: name_(name), segment_(segment), found_() { }
// Return true if the traversal found the segment, false otherwise.
bool found() const { return found_; }
bool SegmentCommand(const Segment &segment) {
if (segment.name == name_) {
*segment_ = segment;
found_ = true;
return false;
}
return true;
}
private:
// The name of the segment our creator is looking for.
const string &name_;
// Where we should store the segment if found. (WEAK)
Segment *segment_;
// True if we found the segment.
bool found_;
};
bool Reader::FindSegment(const string &name, Segment *segment) const {
SegmentFinder finder(name, segment);
WalkLoadCommands(&finder);
return finder.found();
}
bool Reader::WalkSegmentSections(const Segment &segment,
SectionHandler *handler) const {
size_t word_size = segment.bits_64 ? 8 : 4;
ByteCursor cursor(&segment.section_list, big_endian_);
for (size_t i = 0; i < segment.nsects; i++) {
Section section;
section.bits_64 = segment.bits_64;
uint64_t size;
uint32_t offset, dummy32;
cursor
.CString(&section.section_name, 16)
.CString(&section.segment_name, 16)
.Read(word_size, false, &section.address)
.Read(word_size, false, &size)
>> offset
>> section.align
>> dummy32
>> dummy32
>> section.flags
>> dummy32
>> dummy32;
if (section.bits_64)
cursor >> dummy32;
if (!cursor) {
reporter_->SectionsMissing(segment.name);
return false;
}
if ((section.flags & SECTION_TYPE) == S_ZEROFILL) {
// Zero-fill sections have a size, but no contents.
section.contents.start = section.contents.end = NULL;
} else if (segment.contents.start == NULL &&
segment.contents.end == NULL) {
// Mach-O files in .dSYM bundles have the contents of the loaded
// segments removed, and their file offsets and file sizes zeroed
// out. However, the sections within those segments still have
// non-zero sizes. There's no reason to call MisplacedSectionData in
// this case; the caller may just need the section's load
// address. But do set the contents' limits to NULL, for safety.
section.contents.start = section.contents.end = NULL;
} else {
if (offset < size_t(segment.contents.start - buffer_.start) ||
offset > size_t(segment.contents.end - buffer_.start) ||
size > size_t(segment.contents.end - buffer_.start - offset)) {
reporter_->MisplacedSectionData(section.section_name,
section.segment_name);
return false;
}
section.contents.start = buffer_.start + offset;
section.contents.end = section.contents.start + size;
}
if (!handler->HandleSection(section))
return false;
}
return true;
}
// A SectionHandler that builds a SectionMap for the sections within a
// given segment.
class Reader::SectionMapper: public SectionHandler {
public:
// Create a SectionHandler that populates MAP with an entry for
// each section it is given.
SectionMapper(SectionMap *map) : map_(map) { }
bool HandleSection(const Section &section) {
(*map_)[section.section_name] = section;
return true;
}
private:
// The map under construction. (WEAK)
SectionMap *map_;
};
bool Reader::MapSegmentSections(const Segment &segment,
SectionMap *section_map) const {
section_map->clear();
SectionMapper mapper(section_map);
return WalkSegmentSections(segment, &mapper);
}
} // namespace mach_o
} // namespace google_breakpad

View File

@ -0,0 +1,459 @@
// -*- 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>
// macho_reader.h: A class for parsing Mach-O files.
#ifndef BREAKPAD_COMMON_MAC_MACHO_READER_H_
#define BREAKPAD_COMMON_MAC_MACHO_READER_H_
#include <mach-o/loader.h>
#include <mach-o/fat.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <map>
#include <string>
#include <vector>
#include "common/byte_cursor.h"
namespace google_breakpad {
namespace mach_o {
using std::map;
using std::string;
using std::vector;
// The Mac headers don't specify particular types for these groups of
// constants, but defining them here provides some documentation
// value. We also give them the same width as the fields in which
// they appear, which makes them a bit easier to use with ByteCursors.
typedef uint32_t Magic;
typedef uint32_t FileType;
typedef uint32_t FileFlags;
typedef uint32_t LoadCommandType;
typedef uint32_t SegmentFlags;
typedef uint32_t SectionFlags;
// A parser for fat binary files, used to store universal binaries.
// When applied to a (non-fat) Mach-O file, this behaves as if the
// file were a fat file containing a single object file.
class FatReader {
public:
// A class for reporting errors found while parsing fat binary files. The
// default definitions of these methods print messages to stderr.
class Reporter {
public:
// Create a reporter that attributes problems to |filename|.
explicit Reporter(const string &filename) : filename_(filename) { }
virtual ~Reporter() { }
// The data does not begin with a fat binary or Mach-O magic number.
// This is a fatal error.
virtual void BadHeader();
// The Mach-O fat binary file ends abruptly, without enough space
// to contain an object file it claims is present.
virtual void MisplacedObjectFile();
// The file ends abruptly: either it is not large enough to hold a
// complete header, or the header implies that contents are present
// beyond the actual end of the file.
virtual void TooShort();
private:
// The filename to which the reader should attribute problems.
string filename_;
};
// Create a fat binary file reader that uses |reporter| to report problems.
explicit FatReader(Reporter *reporter) : reporter_(reporter) { }
// Read the |size| bytes at |buffer| as a fat binary file. On success,
// return true; on failure, report the problem to reporter_ and return
// false.
//
// If the data is a plain Mach-O file, rather than a fat binary file,
// then the reader behaves as if it had found a fat binary file whose
// single object file is the Mach-O file.
bool Read(const uint8_t *buffer, size_t size);
// Return an array of 'struct fat_arch' structures describing the
// object files present in this fat binary file. Set |size| to the
// number of elements in the array.
//
// Assuming Read returned true, the entries are validated: it is
// safe to assume that the offsets and sizes in each 'struct
// fat_arch' refer to subranges of the bytes passed to Read.
//
// If there are no object files in this fat binary, then this
// function can return NULL.
//
// The array is owned by this FatReader instance; it will be freed when
// this FatReader is destroyed.
//
// This function returns a C-style array instead of a vector to make it
// possible to use the result with OS X functions like NXFindBestFatArch,
// so that the symbol dumper will behave consistently with other OS X
// utilities that work with fat binaries.
const struct fat_arch *object_files(size_t *count) const {
*count = object_files_.size();
if (object_files_.size() > 0)
return &object_files_[0];
return NULL;
}
private:
// We use this to report problems parsing the file's contents. (WEAK)
Reporter *reporter_;
// The contents of the fat binary or Mach-O file we're parsing. We do not
// own the storage it refers to.
ByteBuffer buffer_;
// The magic number of this binary, in host byte order.
Magic magic_;
// The list of object files in this binary.
// object_files_.size() == fat_header.nfat_arch
vector<struct fat_arch> object_files_;
};
// A segment in a Mach-O file. All these fields have been byte-swapped as
// appropriate for use by the executing architecture.
struct Segment {
// The ByteBuffers below point into the bytes passed to the Reader that
// created this Segment.
ByteBuffer section_list; // This segment's section list.
ByteBuffer contents; // This segment's contents.
// This segment's name.
string name;
// The address at which this segment should be loaded in memory. If
// bits_64 is false, only the bottom 32 bits of this value are valid.
uint64_t vmaddr;
// The size of this segment when loaded into memory. This may be larger
// than contents.Size(), in which case the extra area will be
// initialized with zeros. If bits_64 is false, only the bottom 32 bits
// of this value are valid.
uint64_t vmsize;
// The maximum and initial VM protection of this segment's contents.
uint32_t maxprot;
uint32_t initprot;
// The number of sections in section_list.
uint32_t nsects;
// Flags describing this segment, from SegmentFlags.
uint32_t flags;
// True if this is a 64-bit section; false if it is a 32-bit section.
bool bits_64;
};
// A section in a Mach-O file. All these fields have been byte-swapped as
// appropriate for use by the executing architecture.
struct Section {
// This section's contents. This points into the bytes passed to the
// Reader that created this Section.
ByteBuffer contents;
// This section's name.
string section_name; // section[_64].sectname
// The name of the segment this section belongs to.
string segment_name; // section[_64].segname
// The address at which this section's contents should be loaded in
// memory. If bits_64 is false, only the bottom 32 bits of this value
// are valid.
uint64_t address;
// The contents of this section should be loaded into memory at an
// address which is a multiple of (two raised to this power).
uint32_t align;
// Flags from SectionFlags describing the section's contents.
uint32_t flags;
// We don't support reading relocations yet.
// True if this is a 64-bit section; false if it is a 32-bit section.
bool bits_64;
};
// A map from section names to Sections.
typedef map<string, Section> SectionMap;
// A reader for a Mach-O file.
//
// This does not handle fat binaries; see FatReader above. FatReader
// provides a friendly interface for parsing data that could be either a
// fat binary or a Mach-O file.
class Reader {
public:
// A class for reporting errors found while parsing Mach-O files. The
// default definitions of these member functions print messages to
// stderr.
class Reporter {
public:
// Create a reporter that attributes problems to |filename|.
explicit Reporter(const string &filename) : filename_(filename) { }
virtual ~Reporter() { }
// Reporter functions for fatal errors return void; the reader will
// definitely return an error to its caller after calling them
// The data does not begin with a Mach-O magic number, or the magic
// number does not match the expected value for the cpu architecture.
// This is a fatal error.
virtual void BadHeader();
// The data contained in a Mach-O fat binary (|cpu_type|, |cpu_subtype|)
// does not match the expected CPU architecture
// (|expected_cpu_type|, |expected_cpu_subtype|).
virtual void CPUTypeMismatch(cpu_type_t cpu_type,
cpu_subtype_t cpu_subtype,
cpu_type_t expected_cpu_type,
cpu_subtype_t expected_cpu_subtype);
// The file ends abruptly: either it is not large enough to hold a
// complete header, or the header implies that contents are present
// beyond the actual end of the file.
virtual void HeaderTruncated();
// The file's load command region, as given in the Mach-O header, is
// too large for the file.
virtual void LoadCommandRegionTruncated();
// The file's Mach-O header claims the file contains |claimed| load
// commands, but the I'th load command, of type |type|, extends beyond
// the end of the load command region, as given by the Mach-O header.
// If |type| is zero, the command's type was unreadable.
virtual void LoadCommandsOverrun(size_t claimed, size_t i,
LoadCommandType type);
// The contents of the |i|'th load command, of type |type|, extend beyond
// the size given in the load command's header.
virtual void LoadCommandTooShort(size_t i, LoadCommandType type);
// The LC_SEGMENT or LC_SEGMENT_64 load command for the segment named
// |name| is too short to hold the sections that its header says it does.
// (This more specific than LoadCommandTooShort.)
virtual void SectionsMissing(const string &name);
// The segment named |name| claims that its contents lie beyond the end
// of the file.
virtual void MisplacedSegmentData(const string &name);
// The section named |section| in the segment named |segment| claims that
// its contents do not lie entirely within the segment.
virtual void MisplacedSectionData(const string &section,
const string &segment);
// The LC_SYMTAB command claims that symbol table contents are located
// beyond the end of the file.
virtual void MisplacedSymbolTable();
// An attempt was made to read a Mach-O file of the unsupported
// CPU architecture |cpu_type|.
virtual void UnsupportedCPUType(cpu_type_t cpu_type);
private:
string filename_;
};
// A handler for sections parsed from a segment. The WalkSegmentSections
// member function accepts an instance of this class, and applies it to
// each section defined in a given segment.
class SectionHandler {
public:
virtual ~SectionHandler() { }
// Called to report that the segment's section list contains |section|.
// This should return true if the iteration should continue, or false
// if it should stop.
virtual bool HandleSection(const Section &section) = 0;
};
// A handler for the load commands in a Mach-O file.
class LoadCommandHandler {
public:
LoadCommandHandler() { }
virtual ~LoadCommandHandler() { }
// When called from WalkLoadCommands, the following handler functions
// should return true if they wish to continue iterating over the load
// command list, or false if they wish to stop iterating.
//
// When called from LoadCommandIterator::Handle or Reader::Handle,
// these functions' return values are simply passed through to Handle's
// caller.
//
// The definitions provided by this base class simply return true; the
// default is to silently ignore sections whose member functions the
// subclass doesn't override.
// COMMAND is load command we don't recognize. We provide only the
// command type and a ByteBuffer enclosing the command's data (If we
// cannot parse the command type or its size, we call
// reporter_->IncompleteLoadCommand instead.)
virtual bool UnknownCommand(LoadCommandType type,
const ByteBuffer &contents) {
return true;
}
// The load command is LC_SEGMENT or LC_SEGMENT_64, defining a segment
// with the properties given in |segment|.
virtual bool SegmentCommand(const Segment &segment) {
return true;
}
// The load command is LC_SYMTAB. |entries| holds the array of nlist
// entries, and |names| holds the strings the entries refer to.
virtual bool SymtabCommand(const ByteBuffer &entries,
const ByteBuffer &names) {
return true;
}
// Add handler functions for more load commands here as needed.
};
// Create a Mach-O file reader that reports problems to |reporter|.
explicit Reader(Reporter *reporter)
: reporter_(reporter) { }
// Read the given data as a Mach-O file. The reader retains pointers
// into the data passed, so the data should live as long as the reader
// does. On success, return true; on failure, return false.
//
// At most one of these functions should be invoked once on each Reader
// instance.
bool Read(const uint8_t *buffer,
size_t size,
cpu_type_t expected_cpu_type,
cpu_subtype_t expected_cpu_subtype);
bool Read(const ByteBuffer &buffer,
cpu_type_t expected_cpu_type,
cpu_subtype_t expected_cpu_subtype) {
return Read(buffer.start,
buffer.Size(),
expected_cpu_type,
expected_cpu_subtype);
}
// Return this file's characteristics, as found in the Mach-O header.
cpu_type_t cpu_type() const { return cpu_type_; }
cpu_subtype_t cpu_subtype() const { return cpu_subtype_; }
FileType file_type() const { return file_type_; }
FileFlags flags() const { return flags_; }
// Return true if this is a 64-bit Mach-O file, false if it is a 32-bit
// Mach-O file.
bool bits_64() const { return bits_64_; }
// Return true if this is a big-endian Mach-O file, false if it is
// little-endian.
bool big_endian() const { return big_endian_; }
// Apply |handler| to each load command in this Mach-O file, stopping when
// a handler function returns false. If we encounter a malformed load
// command, report it via reporter_ and return false. Return true if all
// load commands were parseable and all handlers returned true.
bool WalkLoadCommands(LoadCommandHandler *handler) const;
// Set |segment| to describe the segment named |name|, if present. If
// found, |segment|'s byte buffers refer to a subregion of the bytes
// passed to Read. If we find the section, return true; otherwise,
// return false.
bool FindSegment(const string &name, Segment *segment) const;
// Apply |handler| to each section defined in |segment|. If |handler| returns
// false, stop iterating and return false. If all calls to |handler| return
// true and we reach the end of the section list, return true.
bool WalkSegmentSections(const Segment &segment, SectionHandler *handler)
const;
// Clear |section_map| and then populate it with a map of the sections
// in |segment|, from section names to Section structures.
// Each Section's contents refer to bytes in |segment|'s contents.
// On success, return true; if a problem occurs, report it and return false.
bool MapSegmentSections(const Segment &segment, SectionMap *section_map)
const;
private:
// Used internally.
class SegmentFinder;
class SectionMapper;
// We use this to report problems parsing the file's contents. (WEAK)
Reporter *reporter_;
// The contents of the Mach-O file we're parsing. We do not own the
// storage it refers to.
ByteBuffer buffer_;
// True if this file is big-endian.
bool big_endian_;
// True if this file is a 64-bit Mach-O file.
bool bits_64_;
// This file's cpu type and subtype.
cpu_type_t cpu_type_; // mach_header[_64].cputype
cpu_subtype_t cpu_subtype_; // mach_header[_64].cpusubtype
// This file's type.
FileType file_type_; // mach_header[_64].filetype
// The region of buffer_ occupied by load commands.
ByteBuffer load_commands_;
// The number of load commands in load_commands_.
uint32_t load_command_count_; // mach_header[_64].ncmds
// This file's header flags.
FileFlags flags_;
};
} // namespace mach_o
} // namespace google_breakpad
#endif // BREAKPAD_COMMON_MAC_MACHO_READER_H_

File diff suppressed because it is too large Load Diff

View File

@ -31,43 +31,44 @@
//
// Author: Dave Camp
#include "common/mac/byteswap.h"
#include "common/mac/macho_utilities.h"
void breakpad_swap_uuid_command(struct breakpad_uuid_command *uc,
enum NXByteOrder target_byte_order)
{
uc->cmd = NXSwapLong(uc->cmd);
uc->cmdsize = NXSwapLong(uc->cmdsize);
uc->cmd = ByteSwap(uc->cmd);
uc->cmdsize = ByteSwap(uc->cmdsize);
}
void breakpad_swap_segment_command_64(struct segment_command_64 *sg,
enum NXByteOrder target_byte_order)
{
sg->cmd = NXSwapLong(sg->cmd);
sg->cmdsize = NXSwapLong(sg->cmdsize);
sg->cmd = ByteSwap(sg->cmd);
sg->cmdsize = ByteSwap(sg->cmdsize);
sg->vmaddr = NXSwapLongLong(sg->vmaddr);
sg->vmsize = NXSwapLongLong(sg->vmsize);
sg->fileoff = NXSwapLongLong(sg->fileoff);
sg->filesize = NXSwapLongLong(sg->filesize);
sg->vmaddr = ByteSwap(sg->vmaddr);
sg->vmsize = ByteSwap(sg->vmsize);
sg->fileoff = ByteSwap(sg->fileoff);
sg->filesize = ByteSwap(sg->filesize);
sg->maxprot = NXSwapLong(sg->maxprot);
sg->initprot = NXSwapLong(sg->initprot);
sg->nsects = NXSwapLong(sg->nsects);
sg->flags = NXSwapLong(sg->flags);
sg->maxprot = ByteSwap(sg->maxprot);
sg->initprot = ByteSwap(sg->initprot);
sg->nsects = ByteSwap(sg->nsects);
sg->flags = ByteSwap(sg->flags);
}
void breakpad_swap_mach_header_64(struct mach_header_64 *mh,
enum NXByteOrder target_byte_order)
{
mh->magic = NXSwapLong(mh->magic);
mh->cputype = NXSwapLong(mh->cputype);
mh->cpusubtype = NXSwapLong(mh->cpusubtype);
mh->filetype = NXSwapLong(mh->filetype);
mh->ncmds = NXSwapLong(mh->ncmds);
mh->sizeofcmds = NXSwapLong(mh->sizeofcmds);
mh->flags = NXSwapLong(mh->flags);
mh->reserved = NXSwapLong(mh->reserved);
mh->magic = ByteSwap(mh->magic);
mh->cputype = ByteSwap(mh->cputype);
mh->cpusubtype = ByteSwap(mh->cpusubtype);
mh->filetype = ByteSwap(mh->filetype);
mh->ncmds = ByteSwap(mh->ncmds);
mh->sizeofcmds = ByteSwap(mh->sizeofcmds);
mh->flags = ByteSwap(mh->flags);
mh->reserved = ByteSwap(mh->reserved);
}
void breakpad_swap_section_64(struct section_64 *s,
@ -75,15 +76,15 @@ void breakpad_swap_section_64(struct section_64 *s,
enum NXByteOrder target_byte_order)
{
for (uint32_t i = 0; i < nsects; i++) {
s[i].addr = NXSwapLongLong(s[i].addr);
s[i].size = NXSwapLongLong(s[i].size);
s[i].addr = ByteSwap(s[i].addr);
s[i].size = ByteSwap(s[i].size);
s[i].offset = NXSwapLong(s[i].offset);
s[i].align = NXSwapLong(s[i].align);
s[i].reloff = NXSwapLong(s[i].reloff);
s[i].nreloc = NXSwapLong(s[i].nreloc);
s[i].flags = NXSwapLong(s[i].flags);
s[i].reserved1 = NXSwapLong(s[i].reserved1);
s[i].reserved2 = NXSwapLong(s[i].reserved2);
s[i].offset = ByteSwap(s[i].offset);
s[i].align = ByteSwap(s[i].align);
s[i].reloff = ByteSwap(s[i].reloff);
s[i].nreloc = ByteSwap(s[i].nreloc);
s[i].flags = ByteSwap(s[i].flags);
s[i].reserved1 = ByteSwap(s[i].reserved1);
s[i].reserved2 = ByteSwap(s[i].reserved2);
}
}

View File

@ -56,6 +56,8 @@
#if TARGET_CPU_X86
# define BREAKPAD_MACHINE_THREAD_STATE i386_THREAD_STATE
#elif TARGET_CPU_X86_64
# define BREAKPAD_MACHINE_THREAD_STATE x86_THREAD_STATE64
#else
# define BREAKPAD_MACHINE_THREAD_STATE MACHINE_THREAD_STATE
#endif

View File

@ -43,6 +43,7 @@ extern "C" { // necessary for Leopard
#include <unistd.h>
}
#include "common/mac/byteswap.h"
#include "common/mac/macho_walker.h"
#include "common/mac/macho_utilities.h"
@ -61,20 +62,11 @@ MachoWalker::~MachoWalker() {
}
int MachoWalker::ValidateCPUType(int cpu_type) {
// If the user didn't specify, try to use the local architecture. If that
// fails, use the base type for the executable.
// If the user didn't specify, use the local architecture.
if (cpu_type == 0) {
const NXArchInfo *arch = NXGetLocalArchInfo();
if (arch)
cpu_type = arch->cputype;
else
#if __ppc__
cpu_type = CPU_TYPE_POWERPC;
#elif __i386__
cpu_type = CPU_TYPE_X86;
#else
#error Unknown architecture -- are you on a PDP-11?
#endif
assert(arch);
cpu_type = arch->cputype;
}
return cpu_type;
@ -134,7 +126,7 @@ bool MachoWalker::FindHeader(int cpu_type, off_t &offset) {
return false;
if (magic == MH_CIGAM || magic == MH_CIGAM_64)
header_cpu_type = NXSwapInt(header_cpu_type);
header_cpu_type = ByteSwap(header_cpu_type);
if (valid_cpu_type != header_cpu_type)
return false;

View File

@ -55,10 +55,10 @@ std::string ConvertToString(CFStringRef str) {
unsigned int IntegerValueAtIndex(string &str, unsigned int idx) {
string digits("0123456789"), temp;
unsigned int start = 0;
unsigned int end;
unsigned int found = 0;
unsigned int result = 0;
size_t start = 0;
size_t end;
size_t found = 0;
size_t result = 0;
for (; found <= idx; ++found) {
end = str.find_first_not_of(digits, start);

View File

@ -31,10 +31,10 @@
// module.cc: Implement google_breakpad::Module. See module.h.
#include <cerrno>
#include <cstring>
#include "common/module.h"
#include "common/linux/module.h"
#include <errno.h>
#include <string.h>
namespace google_breakpad {

View File

@ -38,10 +38,11 @@
#ifndef COMMON_LINUX_MODULE_H__
#define COMMON_LINUX_MODULE_H__
#include <stdio.h>
#include <map>
#include <string>
#include <vector>
#include <cstdio>
#include "google_breakpad/common/breakpad_types.h"
@ -153,8 +154,14 @@ class Module {
// for functions and lines will be written to the Breakpad symbol
// file as offsets from this address. Construction initializes this
// module's load address to zero: addresses written to the symbol
// file will be the same as they appear in the File and Line
// structures.
// file will be the same as they appear in the Function, Line, and
// StackFrameEntry structures.
//
// Note that this member function has no effect on addresses stored
// in the data added to this module; the Write member function
// simply subtracts off the load address from addresses before it
// prints them. Only the last load address given before calling
// Write is used.
void SetLoadAddress(Address load_address);
// Add FUNCTION to the module.
@ -223,7 +230,7 @@ class Module {
// established by SetLoadAddress.
bool Write(FILE *stream);
private:
private:
// Report an error that has occurred writing the symbol file, using
// errno to find the appropriate cause. Return false.

View File

@ -31,16 +31,16 @@
// module_unittest.cc: Unit tests for google_breakpad::Module.
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/linux/module.h"
#include "common/module.h"
using google_breakpad::Module;
using std::string;
@ -140,8 +140,6 @@ 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");
@ -174,6 +172,10 @@ TEST(Write, RelativeLoadAddress) {
entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
m.AddStackFrameEntry(entry);
// Set the load address. Doing this after adding all the data to
// the module must work fine.
m.SetLoadAddress(0x2ab698b0b6407073LL);
m.Write(f);
checked_fflush(f);
rewind(f);

Some files were not shown because too many files have changed in this diff Show More