Compare commits
132 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0291f06d41 | ||
|
528035ae4e | ||
|
ae0fdf6f43 | ||
|
cbda3193b1 | ||
|
7cc0d8562b | ||
|
41440eaa17 | ||
|
0205a1526d | ||
|
400a1d9aa5 | ||
|
863883f93f | ||
|
be6ea5e82d | ||
|
a7ffb8c364 | ||
|
816f242158 | ||
|
86d5e9e7f9 | ||
|
fed9db3634 | ||
|
9e60a27911 | ||
|
ff49f92c36 | ||
|
d4d65cfc36 | ||
|
b1a39f985d | ||
|
27a2e317d7 | ||
|
8652b9eaed | ||
|
b9f9f4e840 | ||
|
f20be6a6c0 | ||
|
fe64fd4aa2 | ||
|
4da1cfbad2 | ||
|
95719f4b8c | ||
|
30fa644c27 | ||
|
c53ed14310 | ||
|
cf1087c403 | ||
|
be849f0637 | ||
|
84d37160a7 | ||
|
cc7b69a8f2 | ||
|
9d3e1f1c03 | ||
|
7ecc65758d | ||
|
7ed2476eea | ||
|
94b1ccb51f | ||
|
daed3a4383 | ||
|
55ba26e520 | ||
|
1be03d4e87 | ||
|
6cee755e09 | ||
|
f766c4d790 | ||
|
cf1d2dcef9 | ||
|
282996a91e | ||
|
d9b5c83cc1 | ||
|
5f520807ca | ||
|
2ad6028981 | ||
|
2ef9e783ab | ||
|
f252ca8ed1 | ||
|
7b1ec78407 | ||
|
013acd5326 | ||
|
01f9665c63 | ||
|
6e30d1ad6d | ||
|
24a2f501a8 | ||
|
767d5d5b0c | ||
|
b51a14f396 | ||
|
3fcf2e1eef | ||
|
c8dd1d54f9 | ||
|
a33bd704c1 | ||
|
8a35448388 | ||
|
4f96b603fe | ||
|
baab7a7cb6 | ||
|
383b815ed5 | ||
|
6fa7555947 | ||
|
213e3f7853 | ||
|
c3a641288d | ||
|
413754dfdd | ||
|
c625d7cb82 | ||
|
8608fe10bf | ||
|
044c744012 | ||
|
93c3c398ff | ||
|
398384e1cf | ||
|
0295bfea40 | ||
|
4ef4c7a595 | ||
|
d74d407688 | ||
|
1d2b6ce3cd | ||
|
bd7af02472 | ||
|
ff4d8facbf | ||
|
ca21e62ecf | ||
|
a3f0a28b69 | ||
|
5ecc80d579 | ||
|
b559587dd7 | ||
|
cb4f056f12 | ||
|
f681a13afe | ||
|
8b5c70a438 | ||
|
72c4716d06 | ||
|
b3b3d2144f | ||
|
6ccf732733 | ||
|
e925c2a8c1 | ||
|
3dc3a6d272 | ||
|
5f0fc7acef | ||
|
b0c09bd7ea | ||
|
1d4f1396b2 | ||
|
bffca12384 | ||
|
27447e58e3 | ||
|
8f38da092c | ||
|
097cf4f8ad | ||
|
8cabceb005 | ||
|
1a903f088b | ||
|
b8b8f6c6f3 | ||
|
aa6268fdf9 | ||
|
9d1daff1d2 | ||
|
622588226d | ||
|
be470f3a11 | ||
|
eb81077a4f | ||
|
8ba5453573 | ||
|
b0fbff302a | ||
|
43b75f42a7 | ||
|
8ca937a3a7 | ||
|
2b7360217e | ||
|
4aeb452cdd | ||
|
19a35ba066 | ||
|
5bf649f336 | ||
|
41f858a4d7 | ||
|
54d899d631 | ||
|
16ab317dfc | ||
|
d6eb7fa1b8 | ||
|
d2904bb421 | ||
|
7c7366dd6d | ||
|
55f879151c | ||
|
a4eb2e302c | ||
|
ccc06f4798 | ||
|
acb33ed38f | ||
|
2f77d4e41c | ||
|
0a5700b594 | ||
|
e0d5189f74 | ||
|
c32b7ad535 | ||
|
aad5d66fa5 | ||
|
e9df0305d3 | ||
|
a4283fc4cf | ||
|
259512f933 | ||
|
725a5dff8a | ||
|
63f4e5f479 | ||
|
7de07eed33 |
37
.gitignore
vendored
37
.gitignore
vendored
@ -26,6 +26,38 @@
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Ignore other VCSs.
|
||||
.svn/
|
||||
|
||||
# Ignore common compiled artifacts.
|
||||
*~
|
||||
*.o
|
||||
lib*.a
|
||||
/breakpad.pc
|
||||
/breakpad-client.pc
|
||||
/src/client/linux/linux_client_unittest_shlib
|
||||
/src/client/linux/linux_dumper_unittest_helper
|
||||
/src/processor/microdump_stackwalk
|
||||
/src/processor/minidump_dump
|
||||
/src/processor/minidump_stackwalk
|
||||
/src/tools/linux/core2md/core2md
|
||||
/src/tools/linux/dump_syms/dump_syms
|
||||
/src/tools/linux/md2core/minidump-2-core
|
||||
/src/tools/linux/symupload/minidump_upload
|
||||
/src/tools/linux/symupload/sym_upload
|
||||
/src/tools/mac/dump_syms/dump_syms
|
||||
|
||||
# Ignore autotools generated artifacts.
|
||||
.deps
|
||||
.dirstamp
|
||||
autom4te.cache/
|
||||
/config.cache
|
||||
config.h
|
||||
/config.log
|
||||
/config.status
|
||||
/Makefile
|
||||
stamp-h1
|
||||
|
||||
# Ignore GYP generated Visual Studio artifacts.
|
||||
*.filters
|
||||
*.sdf
|
||||
@ -34,6 +66,11 @@
|
||||
*.vcproj
|
||||
*.vcxproj
|
||||
|
||||
# Ignore GYP generated Makefiles
|
||||
src/Makefile
|
||||
*.Makefile
|
||||
*.target.mk
|
||||
|
||||
# Ignore compiled Python files.
|
||||
*.pyc
|
||||
|
||||
|
21
DEPS
21
DEPS
@ -35,22 +35,31 @@
|
||||
deps = {
|
||||
# Logging code.
|
||||
"src/src/third_party/glog":
|
||||
"http://google-glog.googlecode.com/svn/trunk@97",
|
||||
"https://github.com/google/glog.git" +
|
||||
"@ac3e91896917b5d9e8b4467bd912b20e8668488a",
|
||||
|
||||
# Testing libraries and utilities.
|
||||
"src/src/testing": "http://googlemock.googlecode.com/svn/trunk@408",
|
||||
"src/src/testing/gtest": "http://googletest.googlecode.com/svn/trunk@615",
|
||||
"src/src/testing":
|
||||
"https://github.com/google/googlemock.git" +
|
||||
"@ada23475e27babd85fb9c13250243f6acfd3ffd8",
|
||||
"src/src/testing/gtest":
|
||||
"https://github.com/google/googletest.git" +
|
||||
"@a3b859162dd7a4a1798cf8753a03098f2cbdb62e",
|
||||
|
||||
# Protobuf.
|
||||
"src/src/third_party/protobuf/protobuf":
|
||||
"http://protobuf.googlecode.com/svn/trunk@407",
|
||||
"https://github.com/google/protobuf.git" +
|
||||
"@cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac",
|
||||
|
||||
# GYP project generator.
|
||||
"src/src/tools/gyp": "http://gyp.googlecode.com/svn/trunk@1886",
|
||||
"src/src/tools/gyp":
|
||||
"https://chromium.googlesource.com/external/gyp/" +
|
||||
"@e8ab0833a42691cd2184bd4c45d779e43821d3e0",
|
||||
|
||||
# Linux syscall support.
|
||||
"src/src/third_party/lss":
|
||||
"http://linux-syscall-support.googlecode.com/svn/trunk/lss@24",
|
||||
"https://chromium.googlesource.com/linux-syscall-support/" +
|
||||
"@9292030109847793f7a6689adac1ddafb412fe14"
|
||||
}
|
||||
|
||||
hooks = [
|
||||
|
208
Makefile.am
208
Makefile.am
@ -76,13 +76,16 @@ dist_doc_DATA = \
|
||||
INSTALL \
|
||||
LICENSE \
|
||||
NEWS \
|
||||
README
|
||||
README.md
|
||||
|
||||
## Headers
|
||||
if LINUX_HOST
|
||||
includeclhdir = $(includedir)/$(PACKAGE)/client/linux/handler
|
||||
includeclh_HEADERS = $(top_srcdir)/src/client/linux/handler/*.h
|
||||
|
||||
includecldwcdir = $(includedir)/$(PACKAGE)/client/linux/dump_writer_common
|
||||
includecldwc_HEADERS = $(top_srcdir)/src/client/linux/dump_writer_common/*.h
|
||||
|
||||
includeclmdir = $(includedir)/$(PACKAGE)/client/linux/minidump_writer
|
||||
includeclm_HEADERS = $(top_srcdir)/src/client/linux/minidump_writer/*.h
|
||||
|
||||
@ -128,7 +131,6 @@ pkgconfig_DATA += breakpad-client.pc
|
||||
src_client_linux_libbreakpad_client_a_SOURCES = \
|
||||
src/client/linux/crash_generation/crash_generation_client.cc \
|
||||
src/client/linux/crash_generation/crash_generation_server.cc \
|
||||
src/client/linux/dump_writer_common/seccomp_unwinder.cc \
|
||||
src/client/linux/dump_writer_common/thread_info.cc \
|
||||
src/client/linux/dump_writer_common/ucontext_reader.cc \
|
||||
src/client/linux/handler/exception_handler.cc \
|
||||
@ -168,11 +170,13 @@ src_libbreakpad_a_SOURCES = \
|
||||
src/google_breakpad/processor/exploitability.h \
|
||||
src/google_breakpad/processor/fast_source_line_resolver.h \
|
||||
src/google_breakpad/processor/memory_region.h \
|
||||
src/google_breakpad/processor/microdump.h \
|
||||
src/google_breakpad/processor/microdump_processor.h \
|
||||
src/google_breakpad/processor/minidump.h \
|
||||
src/google_breakpad/processor/minidump_processor.h \
|
||||
src/google_breakpad/processor/process_result.h \
|
||||
src/google_breakpad/processor/process_state.h \
|
||||
src/google_breakpad/processor/proc_maps_linux.h \
|
||||
src/google_breakpad/processor/source_line_resolver_base.h \
|
||||
src/google_breakpad/processor/source_line_resolver_interface.h \
|
||||
src/google_breakpad/processor/stack_frame.h \
|
||||
@ -188,8 +192,6 @@ src_libbreakpad_a_SOURCES = \
|
||||
src/processor/basic_code_modules.h \
|
||||
src/processor/basic_source_line_resolver_types.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 \
|
||||
@ -211,6 +213,7 @@ src_libbreakpad_a_SOURCES = \
|
||||
src/processor/logging.cc \
|
||||
src/processor/map_serializers-inl.h \
|
||||
src/processor/map_serializers.h \
|
||||
src/processor/microdump.cc \
|
||||
src/processor/microdump_processor.cc \
|
||||
src/processor/minidump.cc \
|
||||
src/processor/minidump_processor.cc \
|
||||
@ -224,6 +227,7 @@ src_libbreakpad_a_SOURCES = \
|
||||
src/processor/postfix_evaluator-inl.h \
|
||||
src/processor/postfix_evaluator.h \
|
||||
src/processor/process_state.cc \
|
||||
src/processor/proc_maps_linux.cc \
|
||||
src/processor/range_map-inl.h \
|
||||
src/processor/range_map.h \
|
||||
src/processor/simple_serializer-inl.h \
|
||||
@ -264,11 +268,11 @@ src_libbreakpad_a_SOURCES = \
|
||||
src/processor/static_map.h \
|
||||
src/processor/static_range_map-inl.h \
|
||||
src/processor/static_range_map.h \
|
||||
src/processor/symbolic_constants_win.cc \
|
||||
src/processor/symbolic_constants_win.h \
|
||||
src/processor/tokenize.cc \
|
||||
src/processor/tokenize.h
|
||||
|
||||
src_libbreakpad_a_LIBADD = src/third_party/libdisasm/libdisasm.a
|
||||
|
||||
src_third_party_libdisasm_libdisasm_a_SOURCES = \
|
||||
src/third_party/libdisasm/ia32_implicit.c \
|
||||
src/third_party/libdisasm/ia32_implicit.h \
|
||||
@ -299,6 +303,7 @@ src_third_party_libdisasm_libdisasm_a_SOURCES = \
|
||||
|
||||
## Programs
|
||||
bin_PROGRAMS += \
|
||||
src/processor/microdump_stackwalk \
|
||||
src/processor/minidump_dump \
|
||||
src/processor/minidump_stackwalk
|
||||
endif !DISABLE_PROCESSOR
|
||||
@ -313,7 +318,8 @@ bin_PROGRAMS += \
|
||||
src/tools/linux/dump_syms/dump_syms \
|
||||
src/tools/linux/md2core/minidump-2-core \
|
||||
src/tools/linux/symupload/minidump_upload \
|
||||
src/tools/linux/symupload/sym_upload
|
||||
src/tools/linux/symupload/sym_upload \
|
||||
src/tools/mac/dump_syms/dump_syms_mac
|
||||
endif
|
||||
endif LINUX_HOST
|
||||
|
||||
@ -323,7 +329,6 @@ if !DISABLE_PROCESSOR
|
||||
check_PROGRAMS += \
|
||||
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 \
|
||||
@ -340,6 +345,7 @@ check_PROGRAMS += \
|
||||
src/processor/static_range_map_unittest \
|
||||
src/processor/pathname_stripper_unittest \
|
||||
src/processor/postfix_evaluator_unittest \
|
||||
src/processor/proc_maps_linux_unittest \
|
||||
src/processor/range_map_unittest \
|
||||
src/processor/stackwalker_amd64_unittest \
|
||||
src/processor/stackwalker_arm_unittest \
|
||||
@ -360,6 +366,7 @@ check_PROGRAMS += \
|
||||
if !DISABLE_TOOLS
|
||||
check_PROGRAMS += \
|
||||
src/common/dumper_unittest \
|
||||
src/common/mac/macho_reader_unittest \
|
||||
src/tools/linux/md2core/minidump_2_core_unittest
|
||||
endif
|
||||
endif LINUX_HOST
|
||||
@ -373,6 +380,8 @@ endif !DISABLE_PROCESSOR
|
||||
|
||||
if !DISABLE_PROCESSOR
|
||||
check_SCRIPTS = \
|
||||
src/processor/microdump_stackwalk_test \
|
||||
src/processor/microdump_stackwalk_machine_readable_test \
|
||||
src/processor/minidump_dump_test \
|
||||
src/processor/minidump_stackwalk_test \
|
||||
src/processor/minidump_stackwalk_machine_readable_test
|
||||
@ -428,11 +437,16 @@ src_client_linux_linux_client_unittest_shlib_SOURCES = \
|
||||
src/processor/dump_object.cc \
|
||||
src/processor/logging.cc \
|
||||
src/processor/minidump.cc \
|
||||
src/processor/pathname_stripper.cc
|
||||
src/processor/pathname_stripper.cc \
|
||||
src/processor/proc_maps_linux.cc
|
||||
if ANDROID_HOST
|
||||
src_client_linux_linux_client_unittest_shlib_SOURCES += \
|
||||
src/common/android/breakpad_getcontext.S
|
||||
endif
|
||||
if LINUX_HOST
|
||||
src_client_linux_linux_client_unittest_shlib_SOURCES += \
|
||||
src/client/linux/microdump_writer/microdump_writer_unittest.cc
|
||||
endif
|
||||
|
||||
src_client_linux_linux_client_unittest_shlib_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
@ -445,7 +459,6 @@ src_client_linux_linux_client_unittest_shlib_LDFLAGS = \
|
||||
-Wl,-h,linux_client_unittest_shlib
|
||||
src_client_linux_linux_client_unittest_shlib_LDADD = \
|
||||
src/client/linux/crash_generation/crash_generation_client.o \
|
||||
src/client/linux/dump_writer_common/seccomp_unwinder.o \
|
||||
src/client/linux/dump_writer_common/thread_info.o \
|
||||
src/client/linux/dump_writer_common/ucontext_reader.o \
|
||||
src/client/linux/handler/exception_handler.o \
|
||||
@ -536,6 +549,30 @@ src_tools_linux_symupload_sym_upload_SOURCES = \
|
||||
src/tools/linux/symupload/sym_upload.cc
|
||||
src_tools_linux_symupload_sym_upload_LDADD = -ldl
|
||||
|
||||
src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \
|
||||
src/common/dwarf_cfi_to_module.cc \
|
||||
src/common/dwarf_cu_to_module.cc \
|
||||
src/common/dwarf_line_to_module.cc \
|
||||
src/common/language.cc \
|
||||
src/common/md5.cc \
|
||||
src/common/module.cc \
|
||||
src/common/stabs_reader.cc \
|
||||
src/common/stabs_to_module.cc \
|
||||
src/common/dwarf/bytereader.cc \
|
||||
src/common/dwarf/dwarf2diehandler.cc \
|
||||
src/common/dwarf/dwarf2reader.cc \
|
||||
src/common/mac/arch_utilities.cc \
|
||||
src/common/mac/dump_syms.cc \
|
||||
src/common/mac/file_id.cc \
|
||||
src/common/mac/macho_id.cc \
|
||||
src/common/mac/macho_reader.cc \
|
||||
src/common/mac/macho_utilities.cc \
|
||||
src/common/mac/macho_walker.cc \
|
||||
src/tools/mac/dump_syms/dump_syms_tool.cc
|
||||
src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS= \
|
||||
-I$(top_srcdir)/src/third_party/mac_headers \
|
||||
-DHAVE_MACH_O_NLIST_H
|
||||
|
||||
src_common_dumper_unittest_SOURCES = \
|
||||
src/common/byte_cursor_unittest.cc \
|
||||
src/common/dwarf_cfi_to_module.cc \
|
||||
@ -591,6 +628,42 @@ src_common_dumper_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing \
|
||||
$(PTHREAD_CFLAGS)
|
||||
src_common_dumper_unittest_LDADD = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_common_mac_macho_reader_unittest_SOURCES = \
|
||||
src/common/dwarf_cfi_to_module.cc \
|
||||
src/common/dwarf_cu_to_module.cc \
|
||||
src/common/dwarf_line_to_module.cc \
|
||||
src/common/language.cc \
|
||||
src/common/md5.cc \
|
||||
src/common/module.cc \
|
||||
src/common/stabs_reader.cc \
|
||||
src/common/stabs_to_module.cc \
|
||||
src/common/test_assembler.cc \
|
||||
src/common/dwarf/bytereader.cc \
|
||||
src/common/dwarf/cfi_assembler.cc \
|
||||
src/common/dwarf/dwarf2diehandler.cc \
|
||||
src/common/dwarf/dwarf2reader.cc \
|
||||
src/common/mac/arch_utilities.cc \
|
||||
src/common/mac/file_id.cc \
|
||||
src/common/mac/macho_id.cc \
|
||||
src/common/mac/macho_reader.cc \
|
||||
src/common/mac/macho_reader_unittest.cc \
|
||||
src/common/mac/macho_utilities.cc \
|
||||
src/common/mac/macho_walker.cc \
|
||||
src/common/tests/file_utils.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_common_mac_macho_reader_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing \
|
||||
-I$(top_srcdir)/src/third_party/mac_headers \
|
||||
-DHAVE_MACH_O_NLIST_H \
|
||||
$(PTHREAD_CFLAGS)
|
||||
src_common_mac_macho_reader_unittest_LDADD = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
endif
|
||||
|
||||
src_tools_linux_md2core_minidump_2_core_unittest_SOURCES = \
|
||||
@ -616,20 +689,6 @@ src_processor_address_map_unittest_LDADD = \
|
||||
src/processor/logging.o \
|
||||
src/processor/pathname_stripper.o
|
||||
|
||||
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 \
|
||||
-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.o \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_processor_basic_source_line_resolver_unittest_SOURCES = \
|
||||
src/processor/basic_source_line_resolver_unittest.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
@ -699,20 +758,22 @@ src_processor_exploitability_unittest_LDADD = \
|
||||
src/processor/logging.o \
|
||||
src/processor/minidump.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/proc_maps_linux.o \
|
||||
src/processor/simple_symbol_supplier.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
src/processor/stackwalker_ppc64.o \
|
||||
src/processor/stackwalker_sparc.o \
|
||||
src/processor/stackwalker_x86.o \
|
||||
src/processor/symbolic_constants_win.o \
|
||||
src/processor/tokenize.o \
|
||||
src/third_party/libdisasm/libdisasm.a \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
@ -781,6 +842,31 @@ src_processor_microdump_processor_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
src_processor_microdump_processor_unittest_LDADD = \
|
||||
src/processor/basic_code_modules.o \
|
||||
src/processor/basic_source_line_resolver.o \
|
||||
src/processor/call_stack.o \
|
||||
src/processor/cfi_frame_info.o \
|
||||
src/processor/dump_context.o \
|
||||
src/processor/dump_object.o \
|
||||
src/processor/logging.o \
|
||||
src/processor/microdump.o \
|
||||
src/processor/microdump_processor.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/process_state.o \
|
||||
src/processor/simple_symbol_supplier.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
src/processor/stackwalker_ppc64.o \
|
||||
src/processor/stackwalker_sparc.o \
|
||||
src/processor/stackwalker_x86.o \
|
||||
src/processor/tokenize.o \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_processor_minidump_processor_unittest_SOURCES = \
|
||||
@ -809,19 +895,21 @@ src_processor_minidump_processor_unittest_LDADD = \
|
||||
src/processor/minidump.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/process_state.o \
|
||||
src/processor/proc_maps_linux.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
src/processor/stackwalker_ppc64.o \
|
||||
src/processor/stackwalker_sparc.o \
|
||||
src/processor/stackwalker_x86.o \
|
||||
src/processor/symbolic_constants_win.o \
|
||||
src/processor/tokenize.o \
|
||||
src/third_party/libdisasm/libdisasm.a \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
@ -846,6 +934,25 @@ src_processor_minidump_unittest_LDADD = \
|
||||
src/processor/logging.o \
|
||||
src/processor/minidump.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/proc_maps_linux.o \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_processor_proc_maps_linux_unittest_SOURCES = \
|
||||
src/processor/proc_maps_linux.cc \
|
||||
src/processor/proc_maps_linux_unittest.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_processor_proc_maps_linux_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
src_processor_proc_maps_linux_unittest_LDADD = \
|
||||
src/processor/logging.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/third_party/libdisasm/libdisasm.a \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_processor_static_address_map_unittest_SOURCES = \
|
||||
@ -941,14 +1048,15 @@ src_processor_stackwalker_selftest_LDADD = \
|
||||
src/processor/logging.o \
|
||||
src/processor/minidump.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/proc_maps_linux.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
src/processor/stackwalker_ppc64.o \
|
||||
@ -1097,14 +1205,47 @@ src_processor_minidump_dump_LDADD = \
|
||||
src/processor/dump_object.o \
|
||||
src/processor/logging.o \
|
||||
src/processor/minidump.o \
|
||||
src/processor/pathname_stripper.o
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/proc_maps_linux.o
|
||||
|
||||
src_processor_microdump_stackwalk_SOURCES = \
|
||||
src/processor/microdump_stackwalk.cc
|
||||
src_processor_microdump_stackwalk_LDADD = \
|
||||
src/processor/basic_code_modules.o \
|
||||
src/processor/basic_source_line_resolver.o \
|
||||
src/processor/call_stack.o \
|
||||
src/processor/cfi_frame_info.o \
|
||||
src/processor/disassembler_x86.o \
|
||||
src/processor/dump_context.o \
|
||||
src/processor/dump_object.o \
|
||||
src/processor/logging.o \
|
||||
src/processor/microdump.o \
|
||||
src/processor/microdump_processor.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/process_state.o \
|
||||
src/processor/simple_symbol_supplier.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalk_common.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
src/processor/stackwalker_ppc64.o \
|
||||
src/processor/stackwalker_sparc.o \
|
||||
src/processor/stackwalker_x86.o \
|
||||
src/processor/tokenize.o \
|
||||
src/third_party/libdisasm/libdisasm.a
|
||||
|
||||
src_processor_minidump_stackwalk_SOURCES = \
|
||||
src/processor/minidump_stackwalk.cc
|
||||
src_processor_minidump_stackwalk_LDADD = \
|
||||
src/processor/basic_code_modules.o \
|
||||
src/processor/basic_source_line_resolver.o \
|
||||
src/processor/binarystream.o \
|
||||
src/processor/call_stack.o \
|
||||
src/processor/cfi_frame_info.o \
|
||||
src/processor/disassembler_x86.o \
|
||||
@ -1118,20 +1259,23 @@ src_processor_minidump_stackwalk_LDADD = \
|
||||
src/processor/minidump_processor.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
src/processor/process_state.o \
|
||||
src/processor/proc_maps_linux.o \
|
||||
src/processor/simple_symbol_supplier.o \
|
||||
src/processor/source_line_resolver_base.o \
|
||||
src/processor/stack_frame_cpu.o \
|
||||
src/processor/stack_frame_symbolizer.o \
|
||||
src/processor/stackwalk_common.o \
|
||||
src/processor/stackwalker.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_amd64.o \
|
||||
src/processor/stackwalker_arm.o \
|
||||
src/processor/stackwalker_arm64.o \
|
||||
src/processor/stackwalker_address_list.o \
|
||||
src/processor/stackwalker_mips.o \
|
||||
src/processor/stackwalker_ppc.o \
|
||||
src/processor/stackwalker_ppc64.o \
|
||||
src/processor/stackwalker_sparc.o \
|
||||
src/processor/stackwalker_x86.o \
|
||||
src/processor/symbolic_constants_win.o \
|
||||
src/processor/tokenize.o \
|
||||
src/third_party/libdisasm/libdisasm.a
|
||||
|
||||
@ -1206,7 +1350,7 @@ EXTRA_DIST = \
|
||||
src/common/mac/HTTPMultipartUpload.h \
|
||||
src/common/mac/HTTPMultipartUpload.m \
|
||||
src/common/mac/dump_syms.h \
|
||||
src/common/mac/dump_syms.mm \
|
||||
src/common/mac/dump_syms.cc \
|
||||
src/common/mac/file_id.cc \
|
||||
src/common/mac/file_id.h \
|
||||
src/common/mac/macho_id.cc \
|
||||
@ -1258,7 +1402,7 @@ EXTRA_DIST = \
|
||||
src/tools/mac/crash_report/on_demand_symbol_supplier.h \
|
||||
src/tools/mac/crash_report/on_demand_symbol_supplier.mm \
|
||||
src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj \
|
||||
src/tools/mac/dump_syms/dump_syms_tool.m \
|
||||
src/tools/mac/dump_syms/dump_syms_tool.cc \
|
||||
src/tools/mac/symupload/minidump_upload.m \
|
||||
src/tools/mac/symupload/symupload.m \
|
||||
src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj \
|
||||
|
1689
Makefile.in
1689
Makefile.in
File diff suppressed because it is too large
Load Diff
43
README
43
README
@ -1,43 +0,0 @@
|
||||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
||||
|
||||
|
||||
-----
|
||||
Getting started in 32-bit mode (from trunk)
|
||||
Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure
|
||||
Build: make
|
||||
Test: make check
|
||||
Install: make install
|
||||
|
||||
If you need to reconfigure your build be sure to run "make distclean" first.
|
||||
|
||||
|
||||
-----
|
||||
To request change review:
|
||||
0. Get access to a read-write copy of source.
|
||||
Owners at http://code.google.com/p/google-breakpad/ are able to grant
|
||||
this access.
|
||||
|
||||
1. Check out a read-write copy of source using instructions at
|
||||
http://code.google.com/p/google-breakpad/source/checkout
|
||||
|
||||
2. Make changes. Build and test your changes.
|
||||
For core code like processor use methods above.
|
||||
For linux/mac/windows, there are test targets in each project file.
|
||||
|
||||
3. Download http://codereview.appspot.com/static/upload.py
|
||||
|
||||
4. Run upload.py from the 'src' directory:
|
||||
upload.py --server=breakpad.appspot.com
|
||||
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
5. At http://breakpad.appspot.com you'll find your issue listed; click on it,
|
||||
and select Publish+Mail, and enter in the code reviewer and CC
|
||||
google-breakpad-dev@googlegroups.com
|
||||
|
||||
6. When applying code review feedback, specify the '-i' option when running
|
||||
upload.py again and pass the issue number so it updates the existing issue,
|
||||
rather than creating a new one.
|
||||
Be sure to rerun upload.py from the same directory as you did for previous
|
||||
uploads to allow for proper diff calculations.
|
47
README.md
Normal file
47
README.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Breakpad
|
||||
|
||||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
||||
|
||||
## Getting started in 32-bit mode (from trunk)
|
||||
|
||||
```sh
|
||||
# Configure
|
||||
CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure
|
||||
# Build
|
||||
make
|
||||
# Test
|
||||
make check
|
||||
# Install
|
||||
make install
|
||||
```
|
||||
|
||||
If you need to reconfigure your build be sure to run `make distclean` first.
|
||||
|
||||
## To request change review:
|
||||
|
||||
1. Get a copy of depot_tools repo.
|
||||
http://dev.chromium.org/developers/how-tos/install-depot-tools
|
||||
|
||||
2. Create a new directory for checking out the source code.
|
||||
mkdir breakpad && cd breakpad
|
||||
|
||||
3. Run the `fetch` tool from depot_tools to download all the source repos.
|
||||
`fetch breakpad`
|
||||
|
||||
4. Make changes. Build and test your changes.
|
||||
For core code like processor use methods above.
|
||||
For linux/mac/windows, there are test targets in each project file.
|
||||
|
||||
5. Commit your changes to your local repo and upload them to the server.
|
||||
http://dev.chromium.org/developers/contributing-code
|
||||
e.g. `git commit ... && git cl upload ...`
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
6. At https://codereview.chromium.org/ you'll find your issue listed; click on
|
||||
it, and select Publish+Mail, and enter in the code reviewer and CC
|
||||
google-breakpad-dev@googlegroups.com
|
||||
|
||||
## Documentation
|
||||
|
||||
Visit https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/
|
131
aclocal.m4
vendored
131
aclocal.m4
vendored
@ -1,6 +1,6 @@
|
||||
# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.15 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -20,7 +20,7 @@ You have another version of autoconf. It may work, but is not guaranteed to.
|
||||
If you have problems, you may need to regenerate the build system entirely.
|
||||
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
|
||||
|
||||
# Copyright (C) 2002-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2002-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -32,10 +32,10 @@ To do so, use the procedure documented by the package, typically 'autoreconf'.])
|
||||
# generated from the m4 files accompanying Automake X.Y.
|
||||
# (This private macro should not be called outside this file.)
|
||||
AC_DEFUN([AM_AUTOMAKE_VERSION],
|
||||
[am__api_version='1.14'
|
||||
[am__api_version='1.15'
|
||||
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
|
||||
dnl require some minimum version. Point them to the right macro.
|
||||
m4_if([$1], [1.14.1], [],
|
||||
m4_if([$1], [1.15], [],
|
||||
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
|
||||
])
|
||||
|
||||
@ -51,14 +51,74 @@ m4_define([_AM_AUTOCONF_VERSION], [])
|
||||
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
|
||||
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
|
||||
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
|
||||
[AM_AUTOMAKE_VERSION([1.14.1])dnl
|
||||
[AM_AUTOMAKE_VERSION([1.15])dnl
|
||||
m4_ifndef([AC_AUTOCONF_VERSION],
|
||||
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
|
||||
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
|
||||
|
||||
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# AM_PROG_AR([ACT-IF-FAIL])
|
||||
# -------------------------
|
||||
# Try to determine the archiver interface, and trigger the ar-lib wrapper
|
||||
# if it is needed. If the detection of archiver interface fails, run
|
||||
# ACT-IF-FAIL (default is to abort configure with a proper error message).
|
||||
AC_DEFUN([AM_PROG_AR],
|
||||
[AC_BEFORE([$0], [LT_INIT])dnl
|
||||
AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl
|
||||
AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||
AC_REQUIRE_AUX_FILE([ar-lib])dnl
|
||||
AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false])
|
||||
: ${AR=ar}
|
||||
|
||||
AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface],
|
||||
[AC_LANG_PUSH([C])
|
||||
am_cv_ar_interface=ar
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])],
|
||||
[am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
|
||||
AC_TRY_EVAL([am_ar_try])
|
||||
if test "$ac_status" -eq 0; then
|
||||
am_cv_ar_interface=ar
|
||||
else
|
||||
am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
|
||||
AC_TRY_EVAL([am_ar_try])
|
||||
if test "$ac_status" -eq 0; then
|
||||
am_cv_ar_interface=lib
|
||||
else
|
||||
am_cv_ar_interface=unknown
|
||||
fi
|
||||
fi
|
||||
rm -f conftest.lib libconftest.a
|
||||
])
|
||||
AC_LANG_POP([C])])
|
||||
|
||||
case $am_cv_ar_interface in
|
||||
ar)
|
||||
;;
|
||||
lib)
|
||||
# Microsoft lib, so override with the ar-lib wrapper script.
|
||||
# FIXME: It is wrong to rewrite AR.
|
||||
# But if we don't then we get into trouble of one sort or another.
|
||||
# A longer-term fix would be to have automake use am__AR in this case,
|
||||
# and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something
|
||||
# similar.
|
||||
AR="$am_aux_dir/ar-lib $AR"
|
||||
;;
|
||||
unknown)
|
||||
m4_default([$1],
|
||||
[AC_MSG_ERROR([could not determine $AR interface])])
|
||||
;;
|
||||
esac
|
||||
AC_SUBST([AR])dnl
|
||||
])
|
||||
|
||||
# Figure out how to run the assembler. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -78,7 +138,7 @@ _AM_IF_OPTION([no-dependencies],, [_AM_DEPENDENCIES([CCAS])])dnl
|
||||
|
||||
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -123,15 +183,14 @@ _AM_IF_OPTION([no-dependencies],, [_AM_DEPENDENCIES([CCAS])])dnl
|
||||
# configured tree to be moved without reconfiguration.
|
||||
|
||||
AC_DEFUN([AM_AUX_DIR_EXPAND],
|
||||
[dnl Rely on autoconf to set up CDPATH properly.
|
||||
AC_PREREQ([2.50])dnl
|
||||
# expand $ac_aux_dir to an absolute path
|
||||
am_aux_dir=`cd $ac_aux_dir && pwd`
|
||||
[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
|
||||
# Expand $ac_aux_dir to an absolute path.
|
||||
am_aux_dir=`cd "$ac_aux_dir" && pwd`
|
||||
])
|
||||
|
||||
# AM_CONDITIONAL -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1997-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1997-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -162,7 +221,7 @@ AC_CONFIG_COMMANDS_PRE(
|
||||
Usually this means the macro was only invoked conditionally.]])
|
||||
fi])])
|
||||
|
||||
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -353,7 +412,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl
|
||||
|
||||
# Generate code to set up dependency tracking. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -429,7 +488,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
|
||||
|
||||
# Do all the work for Automake. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -519,8 +578,8 @@ AC_REQUIRE([AC_PROG_MKDIR_P])dnl
|
||||
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
|
||||
# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
|
||||
AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
|
||||
# We need awk for the "check" target. The system "awk" is bad on
|
||||
# some platforms.
|
||||
# We need awk for the "check" target (and possibly the TAP driver). The
|
||||
# system "awk" is bad on some platforms.
|
||||
AC_REQUIRE([AC_PROG_AWK])dnl
|
||||
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
|
||||
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
|
||||
@ -593,7 +652,11 @@ to "yes", and re-run configure.
|
||||
END
|
||||
AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
|
||||
fi
|
||||
fi])
|
||||
fi
|
||||
dnl The trailing newline in this macro's definition is deliberate, for
|
||||
dnl backward compatibility and to allow trailing 'dnl'-style comments
|
||||
dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
|
||||
])
|
||||
|
||||
dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
|
||||
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
|
||||
@ -622,7 +685,7 @@ for _am_header in $config_headers :; do
|
||||
done
|
||||
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
|
||||
|
||||
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -633,7 +696,7 @@ echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_co
|
||||
# Define $install_sh.
|
||||
AC_DEFUN([AM_PROG_INSTALL_SH],
|
||||
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||
if test x"${install_sh}" != xset; then
|
||||
if test x"${install_sh+set}" != xset; then
|
||||
case $am_aux_dir in
|
||||
*\ * | *\ *)
|
||||
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
|
||||
@ -643,7 +706,7 @@ if test x"${install_sh}" != xset; then
|
||||
fi
|
||||
AC_SUBST([install_sh])])
|
||||
|
||||
# Copyright (C) 2003-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2003-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -665,7 +728,7 @@ AC_SUBST([am__leading_dot])])
|
||||
# Add --enable-maintainer-mode option to configure. -*- Autoconf -*-
|
||||
# From Jim Meyering
|
||||
|
||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -700,7 +763,7 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles])
|
||||
|
||||
# Check to see how 'make' treats includes. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -750,7 +813,7 @@ rm -f confinc confmf
|
||||
|
||||
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1997-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1997-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -791,7 +854,7 @@ fi
|
||||
# Obsolete and "removed" macros, that must however still report explicit
|
||||
# error messages when used, to smooth transition.
|
||||
#
|
||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -818,7 +881,7 @@ AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES])
|
||||
|
||||
# Helper functions for option handling. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -847,7 +910,7 @@ AC_DEFUN([_AM_SET_OPTIONS],
|
||||
AC_DEFUN([_AM_IF_OPTION],
|
||||
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
|
||||
|
||||
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -894,7 +957,7 @@ AC_LANG_POP([C])])
|
||||
# For backward compatibility.
|
||||
AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
|
||||
|
||||
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -913,7 +976,7 @@ AC_DEFUN([AM_RUN_LOG],
|
||||
|
||||
# Check to make sure that the build environment is sane. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -994,7 +1057,7 @@ AC_CONFIG_COMMANDS_PRE(
|
||||
rm -f conftest.file
|
||||
])
|
||||
|
||||
# Copyright (C) 2009-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2009-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -1054,7 +1117,7 @@ AC_SUBST([AM_BACKSLASH])dnl
|
||||
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
|
||||
])
|
||||
|
||||
# Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -1082,7 +1145,7 @@ fi
|
||||
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
|
||||
AC_SUBST([INSTALL_STRIP_PROGRAM])])
|
||||
|
||||
# Copyright (C) 2006-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2006-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@ -1101,7 +1164,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
|
||||
|
||||
# Check how to create a tarball. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2004-2013 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2004-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
|
270
autotools/ar-lib
Executable file
270
autotools/ar-lib
Executable file
@ -0,0 +1,270 @@
|
||||
#! /bin/sh
|
||||
# Wrapper for Microsoft lib.exe
|
||||
|
||||
me=ar-lib
|
||||
scriptversion=2012-03-01.08; # UTC
|
||||
|
||||
# Copyright (C) 2010-2014 Free Software Foundation, Inc.
|
||||
# Written by Peter Rosin <peda@lysator.liu.se>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# This file is maintained in Automake, please report
|
||||
# bugs to <bug-automake@gnu.org> or send patches to
|
||||
# <automake-patches@gnu.org>.
|
||||
|
||||
|
||||
# func_error message
|
||||
func_error ()
|
||||
{
|
||||
echo "$me: $1" 1>&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
file_conv=
|
||||
|
||||
# func_file_conv build_file
|
||||
# Convert a $build file to $host form and store it in $file
|
||||
# Currently only supports Windows hosts.
|
||||
func_file_conv ()
|
||||
{
|
||||
file=$1
|
||||
case $file in
|
||||
/ | /[!/]*) # absolute file, and not a UNC file
|
||||
if test -z "$file_conv"; then
|
||||
# lazily determine how to convert abs files
|
||||
case `uname -s` in
|
||||
MINGW*)
|
||||
file_conv=mingw
|
||||
;;
|
||||
CYGWIN*)
|
||||
file_conv=cygwin
|
||||
;;
|
||||
*)
|
||||
file_conv=wine
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
case $file_conv in
|
||||
mingw)
|
||||
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||
;;
|
||||
cygwin)
|
||||
file=`cygpath -m "$file" || echo "$file"`
|
||||
;;
|
||||
wine)
|
||||
file=`winepath -w "$file" || echo "$file"`
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# func_at_file at_file operation archive
|
||||
# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE
|
||||
# for each of them.
|
||||
# When interpreting the content of the @FILE, do NOT use func_file_conv,
|
||||
# since the user would need to supply preconverted file names to
|
||||
# binutils ar, at least for MinGW.
|
||||
func_at_file ()
|
||||
{
|
||||
operation=$2
|
||||
archive=$3
|
||||
at_file_contents=`cat "$1"`
|
||||
eval set x "$at_file_contents"
|
||||
shift
|
||||
|
||||
for member
|
||||
do
|
||||
$AR -NOLOGO $operation:"$member" "$archive" || exit $?
|
||||
done
|
||||
}
|
||||
|
||||
case $1 in
|
||||
'')
|
||||
func_error "no command. Try '$0 --help' for more information."
|
||||
;;
|
||||
-h | --h*)
|
||||
cat <<EOF
|
||||
Usage: $me [--help] [--version] PROGRAM ACTION ARCHIVE [MEMBER...]
|
||||
|
||||
Members may be specified in a file named with @FILE.
|
||||
EOF
|
||||
exit $?
|
||||
;;
|
||||
-v | --v*)
|
||||
echo "$me, version $scriptversion"
|
||||
exit $?
|
||||
;;
|
||||
esac
|
||||
|
||||
if test $# -lt 3; then
|
||||
func_error "you must specify a program, an action and an archive"
|
||||
fi
|
||||
|
||||
AR=$1
|
||||
shift
|
||||
while :
|
||||
do
|
||||
if test $# -lt 2; then
|
||||
func_error "you must specify a program, an action and an archive"
|
||||
fi
|
||||
case $1 in
|
||||
-lib | -LIB \
|
||||
| -ltcg | -LTCG \
|
||||
| -machine* | -MACHINE* \
|
||||
| -subsystem* | -SUBSYSTEM* \
|
||||
| -verbose | -VERBOSE \
|
||||
| -wx* | -WX* )
|
||||
AR="$AR $1"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
action=$1
|
||||
shift
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
orig_archive=$1
|
||||
shift
|
||||
func_file_conv "$orig_archive"
|
||||
archive=$file
|
||||
|
||||
# strip leading dash in $action
|
||||
action=${action#-}
|
||||
|
||||
delete=
|
||||
extract=
|
||||
list=
|
||||
quick=
|
||||
replace=
|
||||
index=
|
||||
create=
|
||||
|
||||
while test -n "$action"
|
||||
do
|
||||
case $action in
|
||||
d*) delete=yes ;;
|
||||
x*) extract=yes ;;
|
||||
t*) list=yes ;;
|
||||
q*) quick=yes ;;
|
||||
r*) replace=yes ;;
|
||||
s*) index=yes ;;
|
||||
S*) ;; # the index is always updated implicitly
|
||||
c*) create=yes ;;
|
||||
u*) ;; # TODO: don't ignore the update modifier
|
||||
v*) ;; # TODO: don't ignore the verbose modifier
|
||||
*)
|
||||
func_error "unknown action specified"
|
||||
;;
|
||||
esac
|
||||
action=${action#?}
|
||||
done
|
||||
|
||||
case $delete$extract$list$quick$replace,$index in
|
||||
yes,* | ,yes)
|
||||
;;
|
||||
yesyes*)
|
||||
func_error "more than one action specified"
|
||||
;;
|
||||
*)
|
||||
func_error "no action specified"
|
||||
;;
|
||||
esac
|
||||
|
||||
if test -n "$delete"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_at_file "${1#@}" -REMOVE "$archive"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
$AR -NOLOGO -REMOVE:"$file" "$archive" || exit $?
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
elif test -n "$extract"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
if test $# -gt 0; then
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_at_file "${1#@}" -EXTRACT "$archive"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
$AR -NOLOGO -EXTRACT:"$file" "$archive" || exit $?
|
||||
;;
|
||||
esac
|
||||
done
|
||||
else
|
||||
$AR -NOLOGO -LIST "$archive" | sed -e 's/\\/\\\\/g' | while read member
|
||||
do
|
||||
$AR -NOLOGO -EXTRACT:"$member" "$archive" || exit $?
|
||||
done
|
||||
fi
|
||||
|
||||
elif test -n "$quick$replace"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
if test -z "$create"; then
|
||||
echo "$me: creating $orig_archive"
|
||||
fi
|
||||
orig_archive=
|
||||
else
|
||||
orig_archive=$archive
|
||||
fi
|
||||
|
||||
for member
|
||||
do
|
||||
case $1 in
|
||||
@*)
|
||||
func_file_conv "${1#@}"
|
||||
set x "$@" "@$file"
|
||||
;;
|
||||
*)
|
||||
func_file_conv "$1"
|
||||
set x "$@" "$file"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
shift
|
||||
done
|
||||
|
||||
if test -n "$orig_archive"; then
|
||||
$AR -NOLOGO -OUT:"$archive" "$orig_archive" "$@" || exit $?
|
||||
else
|
||||
$AR -NOLOGO -OUT:"$archive" "$@" || exit $?
|
||||
fi
|
||||
|
||||
elif test -n "$list"; then
|
||||
if test ! -f "$orig_archive"; then
|
||||
func_error "archive not found"
|
||||
fi
|
||||
$AR -NOLOGO -LIST "$archive" || exit $?
|
||||
fi
|
63
autotools/config.guess
vendored
63
autotools/config.guess
vendored
@ -1,8 +1,8 @@
|
||||
#! /bin/sh
|
||||
# Attempt to guess a canonical system name.
|
||||
# Copyright 1992-2014 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2014-03-23'
|
||||
timestamp='2015-10-21'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
@ -24,12 +24,12 @@ timestamp='2014-03-23'
|
||||
# program. This Exception is an additional permission under section 7
|
||||
# of the GNU General Public License, version 3 ("GPLv3").
|
||||
#
|
||||
# Originally written by Per Bothner.
|
||||
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
|
||||
#
|
||||
# You can get the latest version of this script from:
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
|
||||
#
|
||||
# Please send patches with a ChangeLog entry to config-patches@gnu.org.
|
||||
# Please send patches to <config-patches@gnu.org>.
|
||||
|
||||
|
||||
me=`echo "$0" | sed -e 's,.*/,,'`
|
||||
@ -50,7 +50,7 @@ version="\
|
||||
GNU config.guess ($timestamp)
|
||||
|
||||
Originally written by Per Bothner.
|
||||
Copyright 1992-2014 Free Software Foundation, Inc.
|
||||
Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
@ -168,20 +168,27 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
# Note: NetBSD doesn't particularly care about the vendor
|
||||
# portion of the name. We always set it to "unknown".
|
||||
sysctl="sysctl -n hw.machine_arch"
|
||||
UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
|
||||
/usr/sbin/$sysctl 2>/dev/null || echo unknown)`
|
||||
UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
|
||||
/sbin/$sysctl 2>/dev/null || \
|
||||
/usr/sbin/$sysctl 2>/dev/null || \
|
||||
echo unknown)`
|
||||
case "${UNAME_MACHINE_ARCH}" in
|
||||
armeb) machine=armeb-unknown ;;
|
||||
arm*) machine=arm-unknown ;;
|
||||
sh3el) machine=shl-unknown ;;
|
||||
sh3eb) machine=sh-unknown ;;
|
||||
sh5el) machine=sh5le-unknown ;;
|
||||
earmv*)
|
||||
arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
|
||||
endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
|
||||
machine=${arch}${endian}-unknown
|
||||
;;
|
||||
*) machine=${UNAME_MACHINE_ARCH}-unknown ;;
|
||||
esac
|
||||
# The Operating System including object format, if it has switched
|
||||
# to ELF recently, or will in the future.
|
||||
case "${UNAME_MACHINE_ARCH}" in
|
||||
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
|
||||
arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
|
||||
eval $set_cc_for_build
|
||||
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
|
||||
| grep -q __ELF__
|
||||
@ -197,6 +204,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
os=netbsd
|
||||
;;
|
||||
esac
|
||||
# Determine ABI tags.
|
||||
case "${UNAME_MACHINE_ARCH}" in
|
||||
earm*)
|
||||
expr='s/^earmv[0-9]/-eabi/;s/eb$//'
|
||||
abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
|
||||
;;
|
||||
esac
|
||||
# The OS release
|
||||
# Debian GNU/NetBSD machines have a different userland, and
|
||||
# thus, need a distinct triplet. However, they do not need
|
||||
@ -207,13 +221,13 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
release='-gnu'
|
||||
;;
|
||||
*)
|
||||
release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
|
||||
release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
|
||||
;;
|
||||
esac
|
||||
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
|
||||
# contains redundant information, the shorter form:
|
||||
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
|
||||
echo "${machine}-${os}${release}"
|
||||
echo "${machine}-${os}${release}${abi}"
|
||||
exit ;;
|
||||
*:Bitrig:*:*)
|
||||
UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
|
||||
@ -235,6 +249,9 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
*:MirBSD:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
|
||||
exit ;;
|
||||
*:Sortix:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-sortix
|
||||
exit ;;
|
||||
alpha:OSF1:*:*)
|
||||
case $UNAME_RELEASE in
|
||||
*4.0)
|
||||
@ -579,8 +596,9 @@ EOF
|
||||
else
|
||||
IBM_ARCH=powerpc
|
||||
fi
|
||||
if [ -x /usr/bin/oslevel ] ; then
|
||||
IBM_REV=`/usr/bin/oslevel`
|
||||
if [ -x /usr/bin/lslpp ] ; then
|
||||
IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
|
||||
awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
|
||||
else
|
||||
IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
|
||||
fi
|
||||
@ -932,6 +950,9 @@ EOF
|
||||
crisv32:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-axis-linux-${LIBC}
|
||||
exit ;;
|
||||
e2k:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
frv:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
@ -944,6 +965,9 @@ EOF
|
||||
ia64:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
k1om:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
m32r*:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
@ -1020,18 +1044,7 @@ EOF
|
||||
echo ${UNAME_MACHINE}-dec-linux-${LIBC}
|
||||
exit ;;
|
||||
x86_64:Linux:*:*)
|
||||
eval $set_cc_for_build
|
||||
X86_64_ABI=
|
||||
# If there is a compiler, see if it is configured for 32-bit objects.
|
||||
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
|
||||
if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
|
||||
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
|
||||
grep IS_X32 >/dev/null
|
||||
then
|
||||
X86_64_ABI=x32
|
||||
fi
|
||||
fi
|
||||
echo x86_64-unknown-linux-gnu${X86_64_ABI}
|
||||
echo ${UNAME_MACHINE}-pc-linux-${LIBC}
|
||||
exit ;;
|
||||
xtensa*:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
@ -1110,7 +1123,7 @@ EOF
|
||||
# uname -m prints for DJGPP always 'pc', but it prints nothing about
|
||||
# the processor, so we play safe by assuming i586.
|
||||
# Note: whatever this is, it MUST be the same as what config.sub
|
||||
# prints for the "djgpp" host, or else GDB configury will decide that
|
||||
# prints for the "djgpp" host, or else GDB configure will decide that
|
||||
# this is a cross-build.
|
||||
echo i586-pc-msdosdjgpp
|
||||
exit ;;
|
||||
|
59
autotools/config.sub
vendored
59
autotools/config.sub
vendored
@ -1,8 +1,8 @@
|
||||
#! /bin/sh
|
||||
# Configuration validation subroutine script.
|
||||
# Copyright 1992-2014 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2014-07-28'
|
||||
timestamp='2015-08-20'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
@ -25,7 +25,7 @@ timestamp='2014-07-28'
|
||||
# of the GNU General Public License, version 3 ("GPLv3").
|
||||
|
||||
|
||||
# Please send patches with a ChangeLog entry to config-patches@gnu.org.
|
||||
# Please send patches to <config-patches@gnu.org>.
|
||||
#
|
||||
# Configuration subroutine to validate and canonicalize a configuration type.
|
||||
# Supply the specified configuration type as an argument.
|
||||
@ -68,7 +68,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
||||
version="\
|
||||
GNU config.sub ($timestamp)
|
||||
|
||||
Copyright 1992-2014 Free Software Foundation, Inc.
|
||||
Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
@ -117,7 +117,7 @@ maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
|
||||
case $maybe_os in
|
||||
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
|
||||
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
|
||||
knetbsd*-gnu* | netbsd*-gnu* | \
|
||||
knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
|
||||
kopensolaris*-gnu* | \
|
||||
storm-chaos* | os2-emx* | rtmk-nova*)
|
||||
os=-$maybe_os
|
||||
@ -255,12 +255,13 @@ case $basic_machine in
|
||||
| arc | arceb \
|
||||
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
|
||||
| avr | avr32 \
|
||||
| ba \
|
||||
| be32 | be64 \
|
||||
| bfin \
|
||||
| c4x | c8051 | clipper \
|
||||
| d10v | d30v | dlx | dsp16xx | dvp \
|
||||
| epiphany \
|
||||
| fido | fr30 | frv \
|
||||
| d10v | d30v | dlx | dsp16xx \
|
||||
| e2k | epiphany \
|
||||
| fido | fr30 | frv | ft32 \
|
||||
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
|
||||
| hexagon \
|
||||
| i370 | i860 | i960 | ia64 \
|
||||
@ -302,9 +303,10 @@ case $basic_machine in
|
||||
| pdp10 | pdp11 | pj | pjl \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle \
|
||||
| pyramid \
|
||||
| riscv32 | riscv64 \
|
||||
| rl78 | rx \
|
||||
| score \
|
||||
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh64 | sh64le \
|
||||
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
|
||||
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
|
||||
@ -312,6 +314,7 @@ case $basic_machine in
|
||||
| tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
|
||||
| ubicom32 \
|
||||
| v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
|
||||
| visium \
|
||||
| we32k \
|
||||
| x86 | xc16x | xstormy16 | xtensa \
|
||||
| z8k | z80)
|
||||
@ -326,6 +329,9 @@ case $basic_machine in
|
||||
c6x)
|
||||
basic_machine=tic6x-unknown
|
||||
;;
|
||||
leon|leon[3-9])
|
||||
basic_machine=sparc-$basic_machine
|
||||
;;
|
||||
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
|
||||
basic_machine=$basic_machine-unknown
|
||||
os=-none
|
||||
@ -371,12 +377,13 @@ case $basic_machine in
|
||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
|
||||
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
|
||||
| avr-* | avr32-* \
|
||||
| ba-* \
|
||||
| be32-* | be64-* \
|
||||
| bfin-* | bs2000-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* \
|
||||
| c8051-* | clipper-* | craynv-* | cydra-* \
|
||||
| d10v-* | d30v-* | dlx-* \
|
||||
| elxsi-* \
|
||||
| e2k-* | elxsi-* \
|
||||
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
|
||||
| h8300-* | h8500-* \
|
||||
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
|
||||
@ -423,12 +430,13 @@ case $basic_machine in
|
||||
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
||||
| pyramid-* \
|
||||
| riscv32-* | riscv64-* \
|
||||
| rl78-* | romp-* | rs6000-* | rx-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
|
||||
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
|
||||
| sparclite-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
|
||||
| tahoe-* \
|
||||
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
|
||||
| tile*-* \
|
||||
@ -436,6 +444,7 @@ case $basic_machine in
|
||||
| ubicom32-* \
|
||||
| v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
|
||||
| vax-* \
|
||||
| visium-* \
|
||||
| we32k-* \
|
||||
| x86-* | x86_64-* | xc16x-* | xps100-* \
|
||||
| xstormy16-* | xtensa*-* \
|
||||
@ -512,6 +521,9 @@ case $basic_machine in
|
||||
basic_machine=i386-pc
|
||||
os=-aros
|
||||
;;
|
||||
asmjs)
|
||||
basic_machine=asmjs-unknown
|
||||
;;
|
||||
aux)
|
||||
basic_machine=m68k-apple
|
||||
os=-aux
|
||||
@ -773,6 +785,9 @@ case $basic_machine in
|
||||
basic_machine=m68k-isi
|
||||
os=-sysv
|
||||
;;
|
||||
leon-*|leon[3-9]-*)
|
||||
basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
|
||||
;;
|
||||
m68knommu)
|
||||
basic_machine=m68k-unknown
|
||||
os=-linux
|
||||
@ -814,24 +829,6 @@ case $basic_machine in
|
||||
basic_machine=m68k-atari
|
||||
os=-mint
|
||||
;;
|
||||
mipsEE* | ee | ps2)
|
||||
basic_machine=mips64r5900el-scei
|
||||
case $os in
|
||||
-linux*)
|
||||
;;
|
||||
*)
|
||||
os=-elf
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
iop)
|
||||
basic_machine=mipsel-scei
|
||||
os=-irx
|
||||
;;
|
||||
dvp)
|
||||
basic_machine=dvp-scei
|
||||
os=-elf
|
||||
;;
|
||||
mips3*-*)
|
||||
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
|
||||
;;
|
||||
@ -1382,7 +1379,7 @@ case $os in
|
||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
|
||||
| -sym* | -kopensolaris* | -plan9* \
|
||||
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
|
||||
| -aos* | -aros* \
|
||||
| -aos* | -aros* | -cloudabi* | -sortix* \
|
||||
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
||||
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
|
||||
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
|
||||
@ -1398,7 +1395,7 @@ case $os in
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
|
||||
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
||||
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
|
||||
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -irx* \
|
||||
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
|
||||
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||
|
@ -1,5 +1,5 @@
|
||||
# This file is used by gcl to get repository specific information.
|
||||
CODE_REVIEW_SERVER: breakpad.appspot.com
|
||||
CODE_REVIEW_SERVER: codereview.chromium.org
|
||||
CC_LIST: google-breakpad-dev@googlegroups.com
|
||||
TRY_ON_UPLOAD: False
|
||||
VIEW_VC: http://code.google.com/p/google-breakpad/source/detail?r=
|
||||
VIEW_VC: https://chromium.googlesource.com/breakpad/breakpad/+/
|
||||
|
@ -32,7 +32,7 @@ AC_PREREQ(2.57)
|
||||
|
||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
|
||||
dnl Sanity check: the argument is just a file that should exist.
|
||||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_SRCDIR(README.md)
|
||||
AC_CONFIG_AUX_DIR(autotools)
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
AC_CANONICAL_HOST
|
||||
@ -41,6 +41,7 @@ AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
|
||||
AM_CONFIG_HEADER(src/config.h)
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
AM_PROG_AR
|
||||
AM_PROG_AS
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
@ -75,6 +76,9 @@ m4_include(m4/ax_pthread.m4)
|
||||
AX_PTHREAD
|
||||
AC_CHECK_HEADERS([a.out.h])
|
||||
|
||||
m4_include(m4/ax_cxx_compile_stdcxx.m4)
|
||||
AX_CXX_COMPILE_STDCXX(11, noext, mandatory)
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
|
1
docs/OWNERS
Normal file
1
docs/OWNERS
Normal file
@ -0,0 +1 @@
|
||||
*
|
BIN
docs/breakpad.png
Normal file
BIN
docs/breakpad.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
1023
docs/breakpad.svg
Normal file
1023
docs/breakpad.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 45 KiB |
224
docs/client_design.md
Normal file
224
docs/client_design.md
Normal file
@ -0,0 +1,224 @@
|
||||
# Breakpad Client Libraries
|
||||
|
||||
## Objective
|
||||
|
||||
The Breakpad client libraries are responsible for monitoring an application for
|
||||
crashes (exceptions), handling them when they occur by generating a dump, and
|
||||
providing a means to upload dumps to a crash reporting server. These tasks are
|
||||
divided between the “handler” (short for “exception handler”) library linked in
|
||||
to an application being monitored for crashes, and the “sender” library,
|
||||
intended to be linked in to a separate external program.
|
||||
|
||||
## Background
|
||||
|
||||
As one of the chief tasks of the client handler is to generate a dump, an
|
||||
understanding of [dump files](processor_design.md) will aid in understanding the
|
||||
handler.
|
||||
|
||||
## Overview
|
||||
|
||||
Breakpad provides client libraries for each of its target platforms. Currently,
|
||||
these exist for Windows on x86 and Mac OS X on both x86 and PowerPC. A Linux
|
||||
implementation has been written and is currently under review.
|
||||
|
||||
Because the mechanisms for catching exceptions and the methods for obtaining the
|
||||
information that a dump contains vary between operating systems, each target
|
||||
operating system requires a completely different handler implementation. Where
|
||||
multiple CPUs are supported for a single operating system, the handler
|
||||
implementation will likely also require separate code for each processor type to
|
||||
extract CPU-specific information. One of the goals of the Breakpad handler is to
|
||||
provide a prepackaged cross-platform system that masks many of these
|
||||
system-level differences and quirks from the application developer. Although the
|
||||
underlying implementations differ, the handler library for each system follows
|
||||
the same set of principles and exposes a similar interface.
|
||||
|
||||
Code that wishes to take advantage of Breakpad should be linked against the
|
||||
handler library, and should, at an appropriate time, install a Breakpad handler.
|
||||
For applications, it is generally desirable to install the handler as early in
|
||||
the start-up process as possible. Developers of library code using Breakpad to
|
||||
monitor itself may wish to install a Breakpad handler when the library is
|
||||
loaded, or may only want to install a handler when calls are made in to the
|
||||
library.
|
||||
|
||||
The handler can be triggered to generate a dump either by catching an exception
|
||||
or at the request of the application itself. The latter case may be useful in
|
||||
debugging assertions or other conditions where developers want to know how a
|
||||
program got in to a specific non-crash state. After generating a dump, the
|
||||
handler calls a user-specified callback function. The callback function may
|
||||
collect additional data about the program’s state, quit the program, launch a
|
||||
crash reporter application, or perform other tasks. Allowing for this
|
||||
functionality to be dictated by a callback function preserves flexibility.
|
||||
|
||||
The sender library is also has a separate implementation for each supported
|
||||
platform, because of the varying interfaces for accessing network resources on
|
||||
different operating systems. The sender transmits a dump along with other
|
||||
application-defined information to a crash report server via HTTP. Because dumps
|
||||
may contain sensitive data, the sender allows for the use of HTTPS.
|
||||
|
||||
The canonical example of the entire client system would be for a monitored
|
||||
application to link against the handler library, install a Breakpad handler from
|
||||
its main function, and provide a callback to launch a small crash reporter
|
||||
program. The crash reporter program would be linked against the sender library,
|
||||
and would send the crash dump when launched. A separate process is recommended
|
||||
for this function because of the unreliability inherent in doing any significant
|
||||
amount of work from a crashed process.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Exception Handler Installation
|
||||
|
||||
The mechanisms for installing an exception handler vary between operating
|
||||
systems. On Windows, it’s a relatively simple matter of making one call to
|
||||
register a [top-level exception filter]
|
||||
(http://msdn.microsoft.com/library/en-us/debug/base/setunhandledexceptionfilter.asp)
|
||||
callback function. On most Unix-like systems such as Linux, processes are
|
||||
informed of exceptions by the delivery of a signal, so an exception handler
|
||||
takes the form of a signal handler. The native mechanism to catch exceptions on
|
||||
Mac OS X requires a large amount of code to set up a Mach port, identify it as
|
||||
the exception port, and assign a thread to listen for an exception on that port.
|
||||
Just as the preparation of exception handlers differ, the manner in which they
|
||||
are called differs as well. On Windows and most Unix-like systems, the handler
|
||||
is called on the thread that caused the exception. On Mac OS X, the thread
|
||||
listening to the exception port is notified that an exception has occurred. The
|
||||
different implementations of the Breakpad handler libraries perform these tasks
|
||||
in the appropriate ways on each platform, while exposing a similar interface on
|
||||
each.
|
||||
|
||||
A Breakpad handler is embodied in an `ExceptionHandler` object. Because it’s a
|
||||
C++ object, `ExceptionHandler`s may be created as local variables, allowing them
|
||||
to be installed and removed as functions are called and return. This provides
|
||||
one possible way for a developer to monitor only a portion of an application for
|
||||
crashes.
|
||||
|
||||
### Exception Basics
|
||||
|
||||
Once an application encounters an exception, it is in an indeterminate and
|
||||
possibly hazardous state. Consequently, any code that runs after an exception
|
||||
occurs must take extreme care to avoid performing operations that might fail,
|
||||
hang, or cause additional exceptions. This task is not at all straightforward,
|
||||
and the Breakpad handler library seeks to do it properly, accounting for all of
|
||||
the minute details while allowing other application developers, even those with
|
||||
little systems programming experience, to reap the benefits. All of the Breakpad
|
||||
handler code that executes after an exception occurs has been written according
|
||||
to the following guidelines for safety at exception time:
|
||||
|
||||
* Use of the application heap is forbidden. The heap may be corrupt or
|
||||
otherwise unusable, and allocators may not function.
|
||||
* Resource allocation must be severely limited. The handler may create a new
|
||||
file to contain the dump, and it may attempt to launch a process to continue
|
||||
handling the crash.
|
||||
* Execution on the thread that caused the exception is significantly limited.
|
||||
The only code permitted to execute on this thread is the code necessary to
|
||||
transition handling to a dedicated preallocated handler thread, and the code
|
||||
to return from the exception handler.
|
||||
* Handlers shouldn’t handle crashes by attempting to walk stacks themselves,
|
||||
as stacks may be in inconsistent states. Dump generation should be performed
|
||||
by interfacing with the operating system’s memory manager and code module
|
||||
manager.
|
||||
* Library code, including runtime library code, must be avoided unless it
|
||||
provably meets the above guidelines. For example, this means that the STL
|
||||
string class may not be used, because it performs operations that attempt to
|
||||
allocate and use heap memory. It also means that many C runtime functions
|
||||
must be avoided, particularly on Windows, because of heap operations that
|
||||
they may perform.
|
||||
|
||||
A dedicated handler thread is used to preserve the state of the exception thread
|
||||
when an exception occurs: during dump generation, it is difficult if not
|
||||
impossible for a thread to accurately capture its own state. Performing all
|
||||
exception-handling functions on a separate thread is also critical when handling
|
||||
stack-limit-exceeded exceptions. It would be hazardous to run out of stack space
|
||||
while attempting to handle an exception. Because of the rule against allocating
|
||||
resources at exception time, the Breakpad handler library creates its handler
|
||||
thread when it installs its exception handler. On Mac OS X, this handler thread
|
||||
is created during the normal setup of the exception handler, and the handler
|
||||
thread will be signaled directly in the event of an exception. On Windows and
|
||||
Linux, the handler thread is signaled by a small amount of code that executes on
|
||||
the exception thread. Because the code that executes on the exception thread in
|
||||
this case is small and safe, this does not pose a problem. Even when an
|
||||
exception is caused by exceeding stack size limits, this code is sufficiently
|
||||
compact to execute entirely within the stack’s guard page without causing an
|
||||
exception.
|
||||
|
||||
The handler thread may also be triggered directly by a user call, even when no
|
||||
exception occurs, to allow dumps to be generated at any point deemed
|
||||
interesting.
|
||||
|
||||
### Filter Callback
|
||||
|
||||
When the handler thread begins handling an exception, it calls an optional
|
||||
user-defined filter callback function, which is responsible for judging whether
|
||||
Breakpad’s handler should continue handling the exception or not. This mechanism
|
||||
is provided for the benefit of library or plug-in code, whose developers may not
|
||||
be interested in reports of crashes that occur outside of their modules but
|
||||
within processes hosting their code. If the filter callback indicates that it is
|
||||
not interested in the exception, the Breakpad handler arranges for it to be
|
||||
delivered to any previously-installed handler.
|
||||
|
||||
### Dump Generation
|
||||
|
||||
Assuming that the filter callback approves (or does not exist), the handler
|
||||
writes a dump in a directory specified by the application developer when the
|
||||
handler was installed, using a previously generated unique identifier to avoid
|
||||
name collisions. The mechanics of dump generation also vary between platforms,
|
||||
but in general, the process involves enumerating each thread of execution, and
|
||||
capturing its state, including processor context and the active portion of its
|
||||
stack area. The dump also includes a list of the code modules loaded in to the
|
||||
application, and an indicator of which thread generated the exception or
|
||||
requested the dump. In order to avoid allocating memory during this process, the
|
||||
dump is written in place on disk.
|
||||
|
||||
### Post-Dump Behavior
|
||||
|
||||
Upon completion of writing the dump, a second callback function is called. This
|
||||
callback may be used to launch a separate crash reporting program or to collect
|
||||
additional data from the application. The callback may also be used to influence
|
||||
whether Breakpad will treat the exception as handled or unhandled. Even after a
|
||||
dump is successfully generated, Breakpad can be made to behave as though it
|
||||
didn’t actually handle an exception. This function may be useful for developers
|
||||
who want to test their applications with Breakpad enabled but still retain the
|
||||
ability to use traditional debugging techniques. It also allows a
|
||||
Breakpad-enabled application to coexist with a platform’s native crash reporting
|
||||
system, such as Mac OS X’ [CrashReporter]
|
||||
(http://developer.apple.com/technotes/tn2004/tn2123.html) and [Windows Error
|
||||
Reporting](http://msdn.microsoft.com/isv/resources/wer/).
|
||||
|
||||
Typically, when Breakpad handles an exception fully and no debuggers are
|
||||
involved, the crashed process will terminate.
|
||||
|
||||
Authors of both callback functions that execute within a Breakpad handler are
|
||||
cautioned that their code will be run at exception time, and that as a result,
|
||||
they should observe the same programming practices that the Breakpad handler
|
||||
itself adheres to. Notably, if a callback is to be used to collect additional
|
||||
data from an application, it should take care to read only “safe” data. This
|
||||
might involve accessing only static memory locations that are updated
|
||||
periodically during the course of normal program execution.
|
||||
|
||||
### Sender Library
|
||||
|
||||
The Breakpad sender library provides a single function to send a crash report to
|
||||
a crash server. It accepts a crash server’s URL, a map of key-value parameters
|
||||
that will accompany the dump, and the path to a dump file itself. Each of the
|
||||
key-value parameters and the dump file are sent as distinct parts of a multipart
|
||||
HTTP POST request to the specified URL using the platform’s native HTTP
|
||||
facilities. On Linux, [libcurl](http://curl.haxx.se/) is used for this function,
|
||||
as it is the closest thing to a standard HTTP library available on that
|
||||
platform.
|
||||
|
||||
## Future Plans
|
||||
|
||||
Although we’ve had great success with in-process dump generation by following
|
||||
our guidelines for safe code at exception time, we are exploring options for
|
||||
allowing dumps to be generated in a separate process, to further enhance the
|
||||
handler library’s robustness.
|
||||
|
||||
On Windows, we intend to offer tools to make it easier for Breakpad’s settings
|
||||
to be managed by the native group policy management system.
|
||||
|
||||
We also plan to offer tools that many developers would find desirable in the
|
||||
context of handling crashes, such as a mechanism to determine at launch if the
|
||||
program last terminated in a crash, and a way to calculate “crashiness” in terms
|
||||
of crashes over time or the number of application launches between crashes.
|
||||
|
||||
We are also investigating methods to capture crashes that occur early in an
|
||||
application’s launch sequence, including crashes that occur before a program’s
|
||||
main function begins executing.
|
35
docs/contributing_to_breakpad.md
Normal file
35
docs/contributing_to_breakpad.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Introduction
|
||||
|
||||
Thanks for thinking of contributing to Breakpad! Unfortunately there are some
|
||||
pesky legal issues to get out of the way, but they're quick and painless.
|
||||
|
||||
## Legal
|
||||
|
||||
If you're doing work individually, not as part of any employment, you'll need to
|
||||
sign the <a
|
||||
href='http://code.google.com/legal/individual-cla-v1.0.html'>Individual
|
||||
Contributor License Agreement</a>. This agreement can be completed
|
||||
electronically.
|
||||
|
||||
If you're contributing to Breakpad as part of your employment with another
|
||||
organization, you'll need to sign a <a
|
||||
href='http://code.google.com/legal/corporate-cla-v1.0.html'> Corporate
|
||||
Contributor License Agreement</a>. Once completed this document will need to be
|
||||
faxed.
|
||||
|
||||
**_IMPORTANT_**: The authors(you!) of the contributions will maintain all
|
||||
copyrights; the agreements you sign will grant rights to Google to use your
|
||||
work.
|
||||
|
||||
Thanks, and if you have any questions let me know and I'll loop in the legal guy
|
||||
here to get you an answer.
|
||||
|
||||
## Technical
|
||||
|
||||
Once you have signed the agreement you can be added to our contributors list and
|
||||
have write access to code. For full details on getting started see our trunk
|
||||
`README`.
|
||||
|
||||
## List of people who have signed contributor agreements
|
||||
|
||||
None so far.
|
128
docs/exception_handling.md
Normal file
128
docs/exception_handling.md
Normal file
@ -0,0 +1,128 @@
|
||||
The goal of this document is to give an overview of the exception handling
|
||||
options in breakpad.
|
||||
|
||||
# Basics
|
||||
|
||||
Exception handling is a mechanism designed to handle the occurrence of
|
||||
exceptions, special conditions that change the normal flow of program execution.
|
||||
|
||||
`SetUnhandledExceptionFilter` replaces all unhandled exceptions when Breakpad is
|
||||
enabled. TODO: More on first and second change and vectored v. try/catch.
|
||||
|
||||
There are two main types of exceptions across all platforms: in-process and
|
||||
out-of-process.
|
||||
|
||||
# In-Process
|
||||
|
||||
In process exception handling is relatively simple since the crashing process
|
||||
handles crash reporting. It is generally considered unsafe to write a minidump
|
||||
from a crashed process. For example, key data structures could be corrupted or
|
||||
the stack on which the exception handler runs could have been overwritten. For
|
||||
this reason all platforms also support some level of out-of-process exception
|
||||
handling.
|
||||
|
||||
## Windows
|
||||
|
||||
In-process exception handling Breakpad creates a 'handler head' that waits
|
||||
infinitely on a semaphore at start up. When this thread is woken it writes the
|
||||
minidump and signals to the excepting thread that it may continue. A filter will
|
||||
tell the OS to kill the process if the minidump is written successfully.
|
||||
Otherwise it continues.
|
||||
|
||||
# Out-of-Process
|
||||
|
||||
Out-of-process exception handling is more complicated than in-process exception
|
||||
handling because of the need to set up a separate process that can read the
|
||||
state of the crashing process.
|
||||
|
||||
## Windows
|
||||
|
||||
Breakpad uses two abstractions around the exception handler to make things work:
|
||||
`CrashGenerationServer` and `CrashGenerationClient`. The constructor for these
|
||||
takes a named pipe name.
|
||||
|
||||
During server start up a named pipe and registers callbacks for client
|
||||
connections are created. The named pipe is used for registration and all IO on
|
||||
the pipe is done asynchronously. `OnPipeConnected` is called when a client
|
||||
attempts to connect (call `CreateFile` on the pipe). `OnPipeConnected` does the
|
||||
state machine transition from `Initial` to `Connecting` and on through
|
||||
`Reading`, `Reading_Done`, `Writing`, `Writing_Done`, `Reading_ACK`, and
|
||||
`Disconnecting`.
|
||||
|
||||
When registering callbacks, the client passes in two pointers to pointers: 1. A
|
||||
pointer to the `EXCEPTION_INFO` pointer 1. A pointer to the `MDRawAssertionInfo`
|
||||
which handles various non-exception failures like assertions
|
||||
|
||||
The essence of registration is adding a "`ClientInfo`" object that contains
|
||||
handles used for synchronization with the crashing process to an array
|
||||
maintained by the server. This is how we can keep track of all the clients on
|
||||
the system that have registered for minidumps. These handles are: *
|
||||
`server_died(mutex)` * `dump_requested(Event)` * `dump_generated(Event)`
|
||||
|
||||
The server registers asynchronous waits on these events with the `ClientInfo`
|
||||
object as the callback context. When the `dump_requested` event is set by the
|
||||
client, the `OnDumpRequested()` callback is called. The server uses the handles
|
||||
inside `ClientInfo` to communicate with the child process. Once the child sets
|
||||
the event, it waits for two objects: 1. the `dump_generated` event 1. the
|
||||
`server_died` mutex
|
||||
|
||||
In the end handles are "duped" into the client process, and the clients use
|
||||
`SetEvent` to request events, wait on the other event, or the `server_died`
|
||||
mutex.
|
||||
|
||||
## Linux
|
||||
|
||||
### Current Status
|
||||
|
||||
As of July 2011, Linux had a minidump generator that is not entirely
|
||||
out-of-process. The minidump was generated from a separate process, but one that
|
||||
shared an address space, file descriptors, signal handles and much else with the
|
||||
crashing process. It worked by using the `clone()` system call to duplicate the
|
||||
crashing process, and then uses `ptrace()` and the `/proc` file system to
|
||||
retrieve the information required to write the minidump. Since then Breakpad has
|
||||
updated Linux exception handling to provide more benefits of out-of-process
|
||||
report generation.
|
||||
|
||||
### Proposed Design
|
||||
|
||||
#### Overview
|
||||
|
||||
Breakpad would use a per-user daemon to write out a minidump that does not have,
|
||||
interact with or depend on the crashing process. We don't want to start a new
|
||||
separate process every time a user launches a Breakpad-enabled process. Doing
|
||||
one daemon per machine is unacceptable for security concerns around one user
|
||||
being able to initiate a minidump generation for another user's process.
|
||||
|
||||
#### Client/Server Communication
|
||||
|
||||
On Breakpad initialization in a process, the initializer would check if the
|
||||
daemon is running and, if not, start it. The race condition between the check
|
||||
and the initialization is not a problem because multiple daemons can check if
|
||||
the IPC endpoint already exists and if a server is listening. Even if multiple
|
||||
copies of the daemon try to `bind()` the filesystem to name the socket, all but
|
||||
one will fail and can terminate.
|
||||
|
||||
This point is relevant for error handling conditions. Linux does not clean the
|
||||
file system representation of a UNIX domain socket even if both endpoints
|
||||
terminate, so checking for existence is not strong enough. However checking the
|
||||
process list or sending a ping on the socket can handle this.
|
||||
|
||||
Breakpad uses UNIX domain sockets since they support full duplex communication
|
||||
(unlike Windows, named pipes on Linux are half) and the kernal automatically
|
||||
creates a private channel between the client and server once the client calls
|
||||
`connect()`.
|
||||
|
||||
#### Minidump Generation
|
||||
|
||||
Breakpad could use the current system with `ptrace()` and `/proc` within the
|
||||
daemon executable.
|
||||
|
||||
Overall the operations look like: 1. Signal from OS indicating crash 1. Signal
|
||||
Handler suspends all threads except itself 1. Signal Handler sends
|
||||
`CRASH_DUMP_REQUEST` message to server and waits for response 1. Server inspects
|
||||
1. Minidump is asynchronously written to disk by the server 1. Server responds
|
||||
indicating inspection is done
|
||||
|
||||
## Mac OSX
|
||||
|
||||
Out-of-process exception handling is fully supported on Mac.
|
121
docs/getting_started_with_breakpad.md
Normal file
121
docs/getting_started_with_breakpad.md
Normal file
@ -0,0 +1,121 @@
|
||||
# Introduction
|
||||
|
||||
Breakpad is a library and tool suite that allows you to distribute an
|
||||
application to users with compiler-provided debugging information removed,
|
||||
record crashes in compact "minidump" files, send them back to your server, and
|
||||
produce C and C++ stack traces from these minidumps. Breakpad can also write
|
||||
minidumps on request for programs that have not crashed.
|
||||
|
||||
Breakpad is currently used by Google Chrome, Firefox, Google Picasa, Camino,
|
||||
Google Earth, and other projects.
|
||||
|
||||
![http://google-breakpad.googlecode.com/svn/wiki/breakpad.png]
|
||||
(http://google-breakpad.googlecode.com/svn/wiki/breakpad.png)
|
||||
|
||||
Breakpad has three main components:
|
||||
|
||||
* The **client** is a library that you include in your application. It can
|
||||
write minidump files capturing the current threads' state and the identities
|
||||
of the currently loaded executable and shared libraries. You can configure
|
||||
the client to write a minidump when a crash occurs, or when explicitly
|
||||
requested.
|
||||
|
||||
* The **symbol dumper** is a program that reads the debugging information
|
||||
produced by the compiler and produces a **symbol file**, in [Breakpad's own
|
||||
format](symbol_files.md).
|
||||
|
||||
* The **processor** is a program that reads a minidump file, finds the
|
||||
appropriate symbol files for the versions of the executables and shared
|
||||
libraries the minidump mentions, and produces a human-readable C/C++ stack
|
||||
trace.
|
||||
|
||||
# The minidump file format
|
||||
|
||||
The minidump file format is similar to core files but was developed by Microsoft
|
||||
for its crash-uploading facility. A minidump file contains:
|
||||
|
||||
* A list of the executable and shared libraries that were loaded in the
|
||||
process at the time the dump was created. This list includes both file names
|
||||
and identifiers for the particular versions of those files that were loaded.
|
||||
|
||||
* A list of threads present in the process. For each thread, the minidump
|
||||
includes the state of the processor registers, and the contents of the
|
||||
threads' stack memory. These data are uninterpreted byte streams, as the
|
||||
Breakpad client generally has no debugging information available to produce
|
||||
function names or line numbers, or even identify stack frame boundaries.
|
||||
|
||||
* Other information about the system on which the dump was collected:
|
||||
processor and operating system versions, the reason for the dump, and so on.
|
||||
|
||||
Breakpad uses Windows minidump files on all platforms, instead of the
|
||||
traditional core files, for several reasons:
|
||||
|
||||
* Core files can be very large, making them impractical to send across a
|
||||
network to the collector for processing. Minidumps are smaller, as they were
|
||||
designed to be used this way.
|
||||
|
||||
* The core file format is poorly documented. For example, the Linux Standards
|
||||
Base does not describe how registers are stored in `PT_NOTE` segments.
|
||||
|
||||
* It is harder to persuade a Windows machine to produce a core dump file than
|
||||
it is to persuade other machines to write a minidump file.
|
||||
|
||||
* It simplifies the Breakpad processor to support only one file format.
|
||||
|
||||
# Overview/Life of a minidump
|
||||
|
||||
A minidump is generated via calls into the Breakpad library. By default,
|
||||
initializing Breakpad installs an exception/signal handler that writes a
|
||||
minidump to disk at exception time. On Windows, this is done via
|
||||
`SetUnhandledExceptionFilter()`; on OS X, this is done by creating a thread that
|
||||
waits on the Mach exception port; and on Linux, this is done by installing a
|
||||
signal handler for various exceptions like `SIGILL, SIGSEGV` etc.
|
||||
|
||||
Once the minidump is generated, each platform has a slightly different way of
|
||||
uploading the crash dump. On Windows & Linux, a separate library of functions is
|
||||
provided that can be called into to do the upload. On OS X, a separate process
|
||||
is spawned that prompts the user for permission, if configured to do so, and
|
||||
sends the file.
|
||||
|
||||
# Terminology
|
||||
|
||||
**In-process vs. out-of-process exception handling** - it's generally considered
|
||||
that writing the minidump from within the crashed process is unsafe - key
|
||||
process data structures could be corrupted, or the stack on which the exception
|
||||
handler runs could have been overwritten, etc. All 3 platforms support what's
|
||||
known as "out-of-process" exception handling.
|
||||
|
||||
# Integration overview
|
||||
|
||||
## Breakpad Code Overview
|
||||
|
||||
All the client-side code is found by visiting the Google Project at
|
||||
http://code.google.com/p/google-breakpad. The following directory structure is
|
||||
present in the `src` directory:
|
||||
|
||||
* `processor` Contains minidump-processing code that is used on the server
|
||||
side and isn't of use on the client side
|
||||
* `client` Contains client minidump-generation libraries for all platforms
|
||||
* `tools` Contains source code & projects for building various tools on each
|
||||
platform.
|
||||
|
||||
(Among other directories)
|
||||
|
||||
* <a
|
||||
href='http://code.google.com/p/google-breakpad/wiki/WindowsClientIntegration'>Windows
|
||||
Integration Guide</a>
|
||||
* <a
|
||||
href='http://code.google.com/p/google-breakpad/wiki/MacBreakpadStarterGuide'>Mac
|
||||
Integration Guide</a>
|
||||
* <a href='http://code.google.com/p/google-breakpad/wiki/LinuxStarterGuide'>
|
||||
Linux Integration Guide</a>
|
||||
|
||||
## Build process specifics(symbol generation)
|
||||
|
||||
This applies to all platforms. Inside `src/tools/{platform}/dump_syms` is a tool
|
||||
that can read debugging information for each platform (e.g. for OS X/Linux,
|
||||
DWARF and STABS, and for Windows, PDB files) and generate a Breakpad symbol
|
||||
file. This tool should be run on your binary before it's stripped(in the case of
|
||||
OS X/Linux) and the symbol files need to be stored somewhere that the minidump
|
||||
processor can find. There is another tool, `symupload`, that can be used to
|
||||
upload symbol files if you have written a server that can accept them.
|
97
docs/linux_starter_guide.md
Normal file
97
docs/linux_starter_guide.md
Normal file
@ -0,0 +1,97 @@
|
||||
# How To Add Breakpad To Your Linux Application
|
||||
|
||||
This document is an overview of using the Breakpad client libraries on Linux.
|
||||
|
||||
## Building the Breakpad libraries
|
||||
|
||||
Breakpad provides an Autotools build system that will build both the Linux
|
||||
client libraries and the processor libraries. Running `./configure && make` in
|
||||
the Breakpad source directory will produce
|
||||
**src/client/linux/libbreakpad\_client.a**, which contains all the code
|
||||
necessary to produce minidumps from an application.
|
||||
|
||||
## Integrating Breakpad into your Application
|
||||
|
||||
First, configure your build process to link **libbreakpad\_client.a** into your
|
||||
binary, and set your include paths to include the **src** directory in the
|
||||
**google-breakpad** source tree. Next, include the exception handler header: ```
|
||||
|
||||
# include "client/linux/handler/exception_handler.h"
|
||||
|
||||
```
|
||||
|
||||
Now you can instantiate an `ExceptionHandler` object. Exception handling is active for the lifetime of the `ExceptionHandler` object, so you should instantiate it as early as possible in your application's startup process, and keep it alive for as close to shutdown as possible. To do anything useful, the `ExceptionHandler` constructor requires a path where it can write minidumps, as well as a callback function to receive information about minidumps that were written:
|
||||
```
|
||||
|
||||
static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor,
|
||||
void* context, bool succeeded) { printf("Dump path: %s\n", descriptor.path());
|
||||
return succeeded; }
|
||||
|
||||
void crash() { volatile int* a = (int*)(NULL); *a = 1; }
|
||||
|
||||
int main(int argc, char* argv[]) { google_breakpad::MinidumpDescriptor
|
||||
descriptor("/tmp"); google_breakpad::ExceptionHandler eh(descriptor, NULL,
|
||||
dumpCallback, NULL, true, -1); crash(); return 0; } ```
|
||||
|
||||
Compiling and running this example should produce a minidump file in /tmp, and
|
||||
it should print the minidump filename before exiting. You can read more about
|
||||
the other parameters to the `ExceptionHandler` constructor <a
|
||||
href='http://code.google.com/p/google-breakpad/source/browse/trunk/src/client/linux/handler/exception_handler.h'>in
|
||||
the exception_handler.h source file</a>.
|
||||
|
||||
**Note**: You should do as little work as possible in the callback function.
|
||||
Your application is in an unsafe state. It may not be safe to allocate memory or
|
||||
call functions from other shared libraries. The safest thing to do is `fork` and
|
||||
`exec` a new process to do any work you need to do. If you must do some work in
|
||||
the callback, the Breakpad source contains <a
|
||||
href='http://code.google.com/p/google-breakpad/source/browse/trunk/src/common/linux/linux_libc_support.h'>some
|
||||
simple reimplementations of libc functions</a>, to avoid calling directly into
|
||||
libc, as well as <a href='http://code.google.com/p/linux-syscall-support/'>a
|
||||
header file for making Linux system calls</a> (in **src/third\_party/lss**) to
|
||||
avoid calling into other shared libraries.
|
||||
|
||||
## Sending the minidump file
|
||||
|
||||
In a real application, you would want to handle the minidump in some way, likely
|
||||
by sending it to a server for analysis. The Breakpad source tree contains <a
|
||||
href='http://code.google.com/p/google-breakpad/source/browse/#svn/trunk/src/common/linux'>some
|
||||
HTTP upload source</a> that you might find useful, as well as <a
|
||||
href='http://code.google.com/p/google-breakpad/source/browse/#svn/trunk/src/tools/linux/symupload'>a
|
||||
minidump upload tool</a>.
|
||||
|
||||
## Producing symbols for your application
|
||||
|
||||
To produce useful stack traces, Breakpad requires you to convert the debugging
|
||||
symbols in your binaries to <a
|
||||
href='http://code.google.com/p/google-breakpad/wiki/SymbolFiles'>text-format
|
||||
symbol files</a>. First, ensure that you've compiled your binaries with `-g` to
|
||||
include debugging symbols. Next, compile the `dump_syms` tool by running
|
||||
`configure && make` in the Breakpad source directory. Next, run `dump_syms` on
|
||||
your binaries to produce the text-format symbols. For example, if your main
|
||||
binary was named `test`: `$ google-breakpad/src/tools/linux/dump_syms/dump_syms
|
||||
./test > test.sym
|
||||
`
|
||||
|
||||
In order to use these symbols with the `minidump_stackwalk` tool, you will need
|
||||
to place them in a specific directory structure. The first line of the symbol
|
||||
file contains the information you need to produce this directory structure, for
|
||||
example (your output will vary): `$ head -n1 test.sym MODULE Linux x86_64
|
||||
6EDC6ACDB282125843FD59DA9C81BD830 test $ mkdir -p
|
||||
./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830 $ mv test.sym
|
||||
./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830
|
||||
`
|
||||
|
||||
You may also find the <a
|
||||
href='http://mxr.mozilla.org/mozilla-central/source/toolkit/crashreporter/tools/symbolstore.py'>symbolstore.py</a>
|
||||
script in the Mozilla repository useful, as it encapsulates these steps.
|
||||
|
||||
## Processing the minidump to produce a stack trace
|
||||
|
||||
Breakpad includes a tool called `minidump_stackwalk` which can take a minidump
|
||||
plus its corresponding text-format symbols and produce a symbolized stacktrace.
|
||||
It should be in the **google-breakpad/src/processor** directory if you compiled
|
||||
the Breakpad source using the directions above. Simply pass it the minidump and
|
||||
the symbol path as commandline parameters:
|
||||
`google-breakpad/src/processor/minidump_stackwalk minidump.dmp ./symbols
|
||||
` It produces verbose output on stderr, and the stacktrace on stdout, so you may
|
||||
want to redirect stderr.
|
47
docs/linux_system_calls.md
Normal file
47
docs/linux_system_calls.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Introduction
|
||||
|
||||
Linux implements its userland-to-kernel transition using a special library
|
||||
called linux-gate.so that is mapped by the kernel into every process. For more
|
||||
information, see
|
||||
|
||||
http://www.trilithium.com/johan/2005/08/linux-gate/
|
||||
|
||||
In a nutshell, the problem is that the system call gate function,
|
||||
kernel\_vsyscall does not use EBP to point to the frame pointer.
|
||||
|
||||
However, the Breakpad processor supports special frames like this via STACK
|
||||
lines in the symbol file. If you look in src/client/linux/data you will see
|
||||
symbol files for linux-gate.so for both Intel & AMD(the implementation of
|
||||
kernel\_vsyscall changes depending on the CPU manufacturer). When processing
|
||||
minidumps from Linux 2.6, having these symbol files is necessary for walking the
|
||||
stack for crashes that happen while a thread is in a system call.
|
||||
|
||||
If you're just interested in processing minidumps, those two symbol files should
|
||||
be all you need!
|
||||
|
||||
# Details
|
||||
|
||||
The particular details of understanding the linux-gate.so symbol files can be
|
||||
found by reading about STACK lines inside
|
||||
src/common/windows/pdb\_source\_line\_writer.cc, and the above link. To
|
||||
summarize briefly, we just have to inform the processor how to get to the
|
||||
previous frame when the EIP is inside kernel\_vsyscall, and we do that by
|
||||
telling the processor how many bytes kernel\_vsyscall has pushed onto the stack
|
||||
in it's prologue. For example, one of the symbol files looks somewhat like the
|
||||
following:
|
||||
|
||||
MODULE Linux x86 random\_debug\_id linux-gate.so PUBLIC 400 0 kernel\_vsyscall
|
||||
STACK WIN 4 100 1 1 0 0 0 0 0 1
|
||||
|
||||
The PUBLIC line indicates that kernel\_vsyscall is at offset 400 (in bytes) from
|
||||
the beginning of linux-gate.so. The STACK line indicates the size of the
|
||||
function(100), how many bytes it pushes(1), and how many bytes it pops(1). The
|
||||
last 1 indicates that EBP is pushed onto the stack before being used by the
|
||||
function.
|
||||
|
||||
# Warnings
|
||||
|
||||
These functions might change significantly depending on kernel version. In my
|
||||
opinion, the actual function stack information is unlikely to change frequently,
|
||||
but the Linux kernel might change the address of kernel\_vsyscall w.r.t the
|
||||
beginning of linux-gate.so, which would cause these symbol files to be invalid.
|
184
docs/mac_breakpad_starter_guide.md
Normal file
184
docs/mac_breakpad_starter_guide.md
Normal file
@ -0,0 +1,184 @@
|
||||
# How To Add Breakpad To Your Mac Client Application
|
||||
|
||||
This document is a step-by-step recipe to get your Mac client app to build with
|
||||
Breakpad.
|
||||
|
||||
## Preparing a binary build of Breakpad for use in your tree
|
||||
|
||||
You can either check in a binary build of the Breakpad framework & tools or
|
||||
build it as a dependency of your project. The former is recommended, and
|
||||
detailed here, since building dependencies through other projects is
|
||||
problematic(matching up configuration names), and the Breakpad code doesn't
|
||||
change nearly often enough as your application's will.
|
||||
|
||||
## Building the requisite targets
|
||||
|
||||
All directories are relative to the `src` directory of the Breakpad checkout.
|
||||
|
||||
* Build the 'All' target of `client/mac/Breakpad.xcodeproj` in Release mode.
|
||||
* Execute `cp -R client/mac/build/Release/Breakpad.framework <location in your
|
||||
source tree>`
|
||||
* Inside `tools/mac/dump_syms` directory, build dump\_syms.xcodeproj, and copy
|
||||
tools/mac/dump\_syms/build/Release/dump\_syms to a safe location where it
|
||||
can be run during the build process.
|
||||
|
||||
## Adding Breakpad.framework
|
||||
|
||||
Inside your application's framework, add the Breakpad.Framework to your
|
||||
project's framework settings. When you select it from the file chooser, it will
|
||||
let you pick a target to add it to; go ahead and check the one that's relevant
|
||||
to your application.
|
||||
|
||||
## Copy Breakpad into your Application Package
|
||||
|
||||
Copy Breakpad into your Application Package, so it will be around at run time.
|
||||
|
||||
Go to the Targets section of your Xcode Project window. Hit the disclosure
|
||||
triangle to reveal the build phases of your application. Add a new Copy Files
|
||||
phase using the Contextual menu (Control Click). On the General panel of the new
|
||||
'Get Info' of this new phase, set the destination to 'Frameworks' Close the
|
||||
'Info' panel. Use the Contextual Menu to Rename your new phase 'Copy Frameworks'
|
||||
Now drag Breakpad again into this Copy Frameworks phase. Drag it from whereever
|
||||
it appears in the project file tree.
|
||||
|
||||
## Add a New Run Script build phase
|
||||
|
||||
Near the end of the build phases, add a new Run Script build phase. This will be
|
||||
run before Xcode calls /usr/bin/strip on your project. This is where you'll be
|
||||
calling dump\_sym to output the symbols for each architecture of your build. In
|
||||
my case, the relevant lines read:
|
||||
|
||||
```
|
||||
#!/bin/sh
|
||||
$TOOL_DIR=<location of dump_syms from step 3 above>
|
||||
|
||||
"$TOOL_DIR/dump_syms" -a ppc "$PROD" > "$TARGET_NAME ppc.breakpad"
|
||||
|
||||
"$TOOL_DIR/dump_syms" -a i386 "$PROD" > "$TARGET_NAME i386.breakpad"
|
||||
```
|
||||
|
||||
## Adjust the Project Settings
|
||||
|
||||
* Turn on Separate Strip,
|
||||
* Set the Strip Style to Non-Global Symbols.
|
||||
|
||||
## Write Code!
|
||||
|
||||
You'll need to have an object that acts as the delegate for NSApplication.
|
||||
Inside this object's header, you'll need to add
|
||||
|
||||
1. add an ivar for Breakpad and
|
||||
2. a declaration for the applicationShouldTerminate:(NSApplication`*` sender)
|
||||
message.
|
||||
|
||||
```
|
||||
#import <Breakpad/Breakpad.h>
|
||||
|
||||
@interface BreakpadTest : NSObject {
|
||||
.
|
||||
.
|
||||
.
|
||||
BreakpadRef breakpad;
|
||||
.
|
||||
.
|
||||
.
|
||||
}
|
||||
.
|
||||
.
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender;
|
||||
.
|
||||
.
|
||||
@end
|
||||
```
|
||||
|
||||
Inside your object's implementation file,
|
||||
|
||||
1. add the following method InitBreakpad
|
||||
2. modify your awakeFromNib method to look like the one below,
|
||||
3. modify/add your application's delegate method to look like the one below
|
||||
|
||||
```
|
||||
static BreakpadRef InitBreakpad(void) {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
BreakpadRef breakpad = 0;
|
||||
NSDictionary *plist = [[NSBundle mainBundle] infoDictionary];
|
||||
if (plist) {
|
||||
// Note: version 1.0.0.4 of the framework changed the type of the argument
|
||||
// from CFDictionaryRef to NSDictionary * on the next line:
|
||||
breakpad = BreakpadCreate(plist);
|
||||
}
|
||||
[pool release];
|
||||
return breakpad;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
breakpad = InitBreakpad();
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender {
|
||||
BreakpadRelease(breakpad);
|
||||
return NSTerminateNow;
|
||||
}
|
||||
```
|
||||
|
||||
## Configure Breakpad
|
||||
|
||||
Configure Breakpad for your application.
|
||||
|
||||
1. Take a look inside the Breakpad.framework at the Breakpad.h file for the
|
||||
keys, default values, and descriptions to be passed to BreakpadCreate().
|
||||
2. Add/Edit the Breakpad specific entries in the dictionary passed to
|
||||
BreakpadCreate() -- typically your application's info plist.
|
||||
|
||||
Example from the Notifier Info.plist:
|
||||
`<key>BreakpadProduct</key><string>Google_Notifier_Mac</string>
|
||||
<key>BreakpadProductDisplay</key><string>${PRODUCT_NAME}</string>
|
||||
`
|
||||
|
||||
## Build Your Application
|
||||
|
||||
Almost done!
|
||||
|
||||
## Verify
|
||||
|
||||
Double-check:
|
||||
|
||||
Your app should have in its package contents:
|
||||
myApp.app/Contents/Frameworks/Breakpad.framework.
|
||||
|
||||
The symbol files have reasonable contents (you can look at them with a text
|
||||
editor.)
|
||||
|
||||
Look again at the Copy Frameworks phase of your project. Are you leaking .h
|
||||
files? Select them and delete them. (If you drag a bunch of files into your
|
||||
project, Xcode often wants to copy your .h files into the build, revealing
|
||||
Google secrets. Be vigilant!)
|
||||
|
||||
## Upload the symbol file
|
||||
|
||||
You'll need to configure your build process to store symbols in a location that
|
||||
is accessible by the minidump processor. There is a tool in tools/mac/symupload
|
||||
that can be used to send the symbol file via HTTP post.
|
||||
|
||||
1. Test
|
||||
|
||||
Configure breakpad to send reports to a URL by adding to your app's Info.plist:
|
||||
|
||||
```
|
||||
<key>BreakpadURL</key>
|
||||
<string>upload URL</string>
|
||||
<key>BreakpadReportInterval</key>
|
||||
<string>30</string>
|
||||
```
|
||||
|
||||
## Final Notes
|
||||
|
||||
Breakpad checks whether it is being run under a debugger, and if so, normally
|
||||
does nothing. But, you can force Breakpad to function under a debugger by
|
||||
setting the Unix shell variable BREAKPAD\_IGNORE\_DEBUGGER to a non-zero value.
|
||||
You can bracket the source code in the above Write The Code step with #if DEBUG
|
||||
to completely eliminate it from Debug builds. See
|
||||
//depot/googlemac/GoogleNotifier/main.m for an example. FYI, when your process
|
||||
forks(), exception handlers are reset to the default for child processes. So
|
||||
they must reinitialize Breakpad, otherwise exceptions will be handled by Apple's
|
||||
Crash Reporter.
|
84
docs/mozilla_brown_bag_talk.md
Normal file
84
docs/mozilla_brown_bag_talk.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Breakpad Crash Reporting for Mozilla
|
||||
|
||||
* January 24, 2007
|
||||
* Links updated February 14, 2007
|
||||
* Mozilla HQ
|
||||
* Mark Mentovai
|
||||
* Brian Ryner
|
||||
|
||||
## What is a crash reporter?
|
||||
|
||||
* Enables developers to analyze crashes that occur in the wild
|
||||
* Produces stack backtraces that help identify how a program failed
|
||||
* Offers higher-level data aggregation (topcrashes, MTBF statistics)
|
||||
|
||||
## Motivation
|
||||
|
||||
* Talkback is proprietary and unmaintained
|
||||
* Smaller open-source projects have few options
|
||||
* Larger projects need flexibility and scalability
|
||||
|
||||
## Design Options
|
||||
|
||||
* Stackwalking done on client
|
||||
* Apple CrashReporter
|
||||
* GNOME BugBuddy
|
||||
* Client sends memory dump
|
||||
* Talkback
|
||||
* Windows Error Reporting
|
||||
* Breakpad
|
||||
|
||||
## Goals
|
||||
|
||||
* Provide libraries around which systems can be based
|
||||
* Open-source
|
||||
* Cross-platform
|
||||
* Mac OS X x86, PowerPC
|
||||
* Linux x86
|
||||
* Windows x86
|
||||
* No requirement to distribute symbols
|
||||
|
||||
## Client Libraries
|
||||
|
||||
* Exception handler installed at application startup
|
||||
* Spawns a separate thread
|
||||
* Minidump file written at crash time
|
||||
* Format used by Windows debuggers
|
||||
* Separate application invoked to send
|
||||
* HTTP[S](S.md) POST, can include additional parameters
|
||||
|
||||
## Symbols
|
||||
|
||||
* Cross-platform symbol file format
|
||||
* Contents
|
||||
* Function names
|
||||
* Source file names and line numbers
|
||||
* Windows: Frame pointer omission data
|
||||
* Future: parameters and local variables
|
||||
* Symbol conversion methods
|
||||
|
||||
## Processor
|
||||
|
||||
* Examines minidump file and invokes stackwalker
|
||||
* Symbol files requested from a SymbolSupplier
|
||||
* Produces stack trace
|
||||
* Output may be placed where convenient
|
||||
|
||||
## Intergation
|
||||
|
||||
* Breakpad client present in Gran Paradiso Alpha 1 for Windows
|
||||
* Disabled by default
|
||||
* Enable with `MOZ_AIRBAG`
|
||||
* Proof-of-concept collector
|
||||
* http://mavra.perilith.com/~luser/airbag-collector/list.pl
|
||||
* Other platforms coming soon
|
||||
|
||||
## More Information
|
||||
|
||||
* Project home: http://code.google.com/p/google-breakpad/
|
||||
* Mailing lists
|
||||
* [google-breakpad-dev@googlegroups.com]
|
||||
(http://groups.google.com/group/google-breakpad-dev/)
|
||||
* [google-breakpad-discuss@googlegroups.com]
|
||||
(http://groups.google.com/group/google-breakpad-discuss/)
|
||||
* Ask me (irc.mozilla.org: mento)
|
230
docs/processor_design.md
Normal file
230
docs/processor_design.md
Normal file
@ -0,0 +1,230 @@
|
||||
# Breakpad Processor Library
|
||||
|
||||
## Objective
|
||||
|
||||
The Breakpad processor library is an open-source framework to access the the
|
||||
information contained within crash dumps for multiple platforms, and to use that
|
||||
information to produce stack traces showing the call chain of each thread in a
|
||||
process. After processing, this data is made available to users of the library.
|
||||
|
||||
## Background
|
||||
|
||||
The Breakpad processor is intended to sit at the core of a comprehensive
|
||||
crash-reporting system that does not require debugging information to be
|
||||
provided to those running applications being monitored. Some existing
|
||||
crash-reporting systems, such as [GNOME](http://www.gnome.org/)’s Bug-Buddy and
|
||||
[Apple](http://www.apple.com/)’s [CrashReporter]
|
||||
(http://developer.apple.com/technotes/tn2004/tn2123.html), require symbolic
|
||||
information to be present on the end user’s computer; in the case of
|
||||
CrashReporter, the reports are transmitted only to Apple, not to third-party
|
||||
developers. Other systems, such as [Microsoft](http://www.microsoft.com/)’s
|
||||
[Windows Error Reporting](http://msdn.microsoft.com/isv/resources/wer/) and
|
||||
SupportSoft’s Talkback, transmit only a snapshot of a crashed process’ state,
|
||||
which can later be combined with symbolic debugging information without the need
|
||||
for it to be present on end users’ computers. Because symbolic debugging
|
||||
information consumes a large amount of space and is otherwise not needed during
|
||||
the normal operation of software, and because some developers are reluctant to
|
||||
release debugging symbols to their customers, Breakpad follows the latter
|
||||
approach.
|
||||
|
||||
We know of no currently-maintained crash-reporting systems that meet our
|
||||
requirements, which are to: * allow for symbols to be separate from the
|
||||
application, * handle crash reports from multiple platforms, * allow developers
|
||||
to operate their own crash-reporting platform, and to * be open-source. Windows
|
||||
Error Reporting only functions for Microsoft products, and requires the
|
||||
involvement of Microsoft’s servers. Talkback, while cross-platform, has not been
|
||||
maintained and at this point does not support Mac OS X on x86, which we consider
|
||||
to be a significant platform. Talkback is also closed-source commercial
|
||||
software, and has very specific requirements for its server platform.
|
||||
|
||||
We are aware of Windows-only crash-reporting systems that leverage Microsoft’s
|
||||
debugging interfaces. Such systems, even if extended to support dumps from other
|
||||
platforms, are tied to using Windows for at least a portion of the processor
|
||||
platform.
|
||||
|
||||
## Overview
|
||||
|
||||
The Breakpad processor itself is written in standard C++ and will work on a
|
||||
variety of platforms. The dumps it accepts may also have been created on a
|
||||
variety of systems. The library is able to combine dumps with symbolic debugging
|
||||
information to create stack traces that include function signatures. The
|
||||
processor library includes simple command-line tools to examine dumps and
|
||||
process them, producing stack traces. It also exposes several layers of APIs
|
||||
enabling crash-reporting systems to be built around the Breakpad processor.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### Dump Files
|
||||
|
||||
In the processor, the dump data is of primary significance. Dumps typically
|
||||
contain:
|
||||
|
||||
* CPU context (register data) as it was at the time the crash occurred, and an
|
||||
indication of which thread caused the crash. General-purpose registers are
|
||||
included, as are special-purpose registers such as the instruction pointer
|
||||
(program counter).
|
||||
* Information about each thread of execution within a crashed process,
|
||||
including:
|
||||
* The memory region used for each thread’s stack.
|
||||
* CPU context for each thread, which for various reasons is not the same
|
||||
as the crash context in the case of the crashed thread.
|
||||
* A list of loaded code segments (or modules), including:
|
||||
* The name of the file (`.so`, `.exe`, `.dll`, etc.) which provides the
|
||||
code.
|
||||
* The boundaries of the memory region in which the code segment is visible
|
||||
to the process.
|
||||
* A reference to the debugging information for the code module, when such
|
||||
information is available.
|
||||
|
||||
Ordinarily, dumps are produced as a result of a crash, but other triggers may be
|
||||
set to produce dumps at any time a developer deems appropriate. The Breakpad
|
||||
processor can handle dumps in the minidump format, either generated by an
|
||||
[Breakpad client “handler”](client_design.md) implementation, or by another
|
||||
implementation that produces dumps in this format. The
|
||||
[DbgHelp.dll!MiniDumpWriteDump]
|
||||
(http://msdn2.microsoft.com/en-us/library/ms680360.aspx) function on Windows
|
||||
produces dumps in this format, and is the basis for the Breakpad handler
|
||||
implementation on that platform.
|
||||
|
||||
The [minidump format]
|
||||
(http://msdn.microsoft.com/en-us/library/ms679293%28VS.85%29.aspx) is
|
||||
essentially a simple container format, organized as a series of streams. Each
|
||||
stream contains some type of data relevant to the crash. A typical “normal”
|
||||
minidump contains streams for the thread list, the module list, the CPU context
|
||||
at the time of the crash, and various bits of additional system information.
|
||||
Other types of minidump can be generated, such as a full-memory minidump, which
|
||||
in addition to stack memory contains snapshots of all of a process’ mapped
|
||||
memory regions.
|
||||
|
||||
The minidump format was chosen as Breakpad’s dump format because it has an
|
||||
established track record on Windows, and it can be adapted to meet the needs of
|
||||
the other platforms that Breakpad supports. Most other operating systems use
|
||||
“core” files as their native dump formats, but the capabilities of core files
|
||||
vary across platforms, and because core files are usually presented in a
|
||||
platform’s native executable format, there are complications involved in
|
||||
accessing the data contained therein without the benefit of the header files
|
||||
that define an executable format’s entire structure. Because minidumps are
|
||||
leaner than a typical executable format, a redefinition of the format in a
|
||||
cross-platform header file, `minidump_format.h`, was a straightforward task.
|
||||
Similarly, the capabilities of the minidump format are understood, and because
|
||||
it provides an extensible container, any of Breakpad’s needs that could not be
|
||||
met directly by the standard minidump format could likely be met by extending it
|
||||
as needed. Finally, using this format means that the dump file is compatible
|
||||
with native debugging tools at least on Windows. A possible future avenue for
|
||||
exploration is the conversion of minidumps to core files, to enable this same
|
||||
benefit on other platforms.
|
||||
|
||||
We have already provided an extension to the minidump format that allows it to
|
||||
carry dumps generated on systems with PowerPC processors. The format already
|
||||
allows for variable CPUs, so our work in this area was limited to defining a
|
||||
context structure sufficient to represent the execution state of a PowerPC. We
|
||||
have also defined an extension that allows minidumps to indicate which thread of
|
||||
execution requested a dump be produced for non-crash dumps.
|
||||
|
||||
Often, the information contained within a dump alone is sufficient to produce a
|
||||
full stack backtrace for each thread. Certain optimizations that compilers
|
||||
employ in producing code frustrate this process. Specifically, the “frame
|
||||
pointer omission” optimization of x86 compilers can make it impossible to
|
||||
produce useful stack traces given only a stack snapshot and CPU context. In
|
||||
these cases, however, compiler-emitted debugging information can aid in
|
||||
producing useful stack traces. The Breakpad processor is able to take advantage
|
||||
of this debugging information as supplied by Microsoft’s C/C++ compiler, the
|
||||
only compiler to apply such optimizations by default. As a result, the Breakpad
|
||||
processor can produce useful stack traces even from code with frame pointer
|
||||
omission optimizations as produced by this compiler.
|
||||
|
||||
### Symbol Files
|
||||
|
||||
The [symbol files](symbol_files.md) that the Breakpad processor accepts allow
|
||||
for frame pointer omission data, but this is only one of their capabilities.
|
||||
Each symbol file also includes information about the functions, source files,
|
||||
and source code line numbers for a single module of code. A module is an
|
||||
individually-loadble chunk of code: these can be executables containing a main
|
||||
program (`exe` files on Windows) or shared libraries (`.so` files on Linux,
|
||||
`.dylib` files, frameworks, and bundles on Mac OS X, and `.dll` files on
|
||||
Windows). Dumps contain information about which of these modules were loaded at
|
||||
the time the dump was produced, and given this information, the Breakpad
|
||||
processor attempts to locate debugging symbols for the module through a
|
||||
user-supplied function embodied in a “symbol supplier.” Breakpad includes a
|
||||
sample symbol supplier, called `SimpleSymbolSupplier`, that is used by its
|
||||
command-line tools; this supplier locates symbol files by pathname.
|
||||
`SimpleSymbolSupplier` is also available to other users of the Breakpad
|
||||
processor library. This allows for the use of a simple reference implementation,
|
||||
but preserves flexibility for users who may have more demanding symbol file
|
||||
storage needs.
|
||||
|
||||
Breakpad’s symbol file format is text-based, and was defined to be fairly
|
||||
human-readable and to encompass the needs of multiple platforms. The Breakpad
|
||||
processor itself does not operate directly with native symbol formats ([DWARF]
|
||||
(http://dwarf.freestandards.org/) and [STABS]
|
||||
(http://sourceware.org/gdb/current/onlinedocs/stabs.html) on most Unix-like
|
||||
systems, [.pdb files]
|
||||
(http://msdn2.microsoft.com/en-us/library/yd4f8bd1(VS.80).aspx) on Windows),
|
||||
because of the complications in accessing potentially complex symbol formats
|
||||
with slight variations between platforms, stored within different types of
|
||||
binary formats. In the case of `.pdb` files, the debugging format is not even
|
||||
documented. Instead, Breakpad’s symbol files are produced on each platform,
|
||||
using specific debugging APIs where available, to convert native symbols to
|
||||
Breakpad’s cross-platform format.
|
||||
|
||||
### Processing
|
||||
|
||||
Most commonly, a developer will enable an application to use Breakpad by
|
||||
building it with a platform-specific [client “handler”](client_design.md)
|
||||
library. After building the application, the developer will create symbol files
|
||||
for Breakpad’s use using the included `dump_syms` or `symupload` tools, or
|
||||
another suitable tool, and place the symbol files where the processor’s symbol
|
||||
supplier will be able to locate them.
|
||||
|
||||
When a dump file is given to the processor’s `MinidumpProcessor` class, it will
|
||||
read it using its included minidump reader, contained in the `Minidump` family
|
||||
of classes. It will collect information about the operating system and CPU that
|
||||
produced the dump, and determine whether the dump was produced as a result of a
|
||||
crash or at the direct request of the application itself. It then loops over all
|
||||
of the threads in a process, attempting to walk the stack associated with each
|
||||
thread. This process is achieved by the processor’s `Stackwalker` components, of
|
||||
which there are a slightly different implementations for each CPU type that the
|
||||
processor is able to handle dumps from. Beginning with a thread’s context, and
|
||||
possibly using debugging data, the stackwalker produces a list of stack frames,
|
||||
containing each instruction executed in the chain. These instructions are
|
||||
matched up with the modules that contributed them to a process, and the
|
||||
`SymbolSupplier` is invoked to locate a symbol file. The symbol file is given to
|
||||
a `SourceLineResolver`, which matches the instruction up with a specific
|
||||
function name, source file, and line number, resulting in a representation of a
|
||||
stack frame that can easily be used to identify which code was executing.
|
||||
|
||||
The results of processing are made available in a `ProcessState` object, which
|
||||
contains a vector of threads, each containing a vector of stack frames.
|
||||
|
||||
For small-scale use of the Breakpad processor, and for testing and debugging,
|
||||
the `minidump_stackwalk` tool is provided. It invokes the processor and displays
|
||||
the full results of processing, optionally allowing symbols to be provided to
|
||||
the processor by a pathname-based symbol supplier, `SimpleSymbolSupplier`.
|
||||
|
||||
For lower-level testing and debugging, the processor library also includes a
|
||||
`minidump_dump` tool, which walks through an entire minidump file and displays
|
||||
its contents in somewhat readable form.
|
||||
|
||||
### Platform Support
|
||||
|
||||
The Breakpad processor library is able to process dumps produced on Mac OS X
|
||||
systems running on x86, x86-64, and PowerPC processors, on Windows and Linux
|
||||
systems running on x86 or x86-64 processors, and on Android systems running ARM
|
||||
or x86 processors. The processor library itself is written in standard C++, and
|
||||
should function properly in most Unix-like environments. It has been tested on
|
||||
Linux and Mac OS X.
|
||||
|
||||
## Future Plans
|
||||
|
||||
There are currently no firm plans or timetables to implement any of these
|
||||
features, although they are possible avenues for future exploration.
|
||||
|
||||
The symbol file format can be extended to carry information about the locations
|
||||
of parameters and local variables as stored in stack frames and registers, and
|
||||
the processor can use this information to provide enhanced stack traces showing
|
||||
function arguments and variable values.
|
||||
|
||||
On Mac OS X and Linux, we can provide tools to convert files from the minidump
|
||||
format into the native core format. This will enable developers to open dump
|
||||
files in a native debugger, just as they are presently able to do with minidumps
|
||||
on Windows.
|
160
docs/stack_walking.md
Normal file
160
docs/stack_walking.md
Normal file
@ -0,0 +1,160 @@
|
||||
# Introduction
|
||||
|
||||
This page aims to provide a detailed description of how Breakpad produces stack
|
||||
traces from the information contained within a minidump file.
|
||||
|
||||
# Details
|
||||
|
||||
## Starting the Process
|
||||
|
||||
Typically the stack walking process is initiated by instantiating the
|
||||
[MinidumpProcessor]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/minidump_processor.cc)
|
||||
class and calling the [MinidumpProcessor::Process]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/minidump_processor.cc#61)
|
||||
method, providing it a minidump file to process. To produce a useful stack
|
||||
trace, the MinidumpProcessor requires two other objects which are passed in its
|
||||
constructor: a [SymbolSupplier]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/symbol_supplier.h)
|
||||
and a [SourceLineResolverInterface]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h).
|
||||
The SymbolSupplier object is responsible for locating and providing SymbolFiles
|
||||
that match modules from the minidump. The SourceLineResolverInterface is
|
||||
responsible for loading the symbol files and using the information contained
|
||||
within to provide function and source information for stack frames, as well as
|
||||
information on how to unwind from a stack frame to its caller. More detail will
|
||||
be provided on these interactions later.
|
||||
|
||||
A number of data streams are extracted from the minidump to begin stack walking:
|
||||
the list of threads from the process ([MinidumpThreadList]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#335)),
|
||||
the list of modules loaded in the process ([MinidumpModuleList]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#501)),
|
||||
and information about the exception that caused the process to crash
|
||||
([MinidumpException]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#615)).
|
||||
|
||||
## Enumerating Threads
|
||||
|
||||
For each thread in the thread list ([MinidumpThread]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#299)),
|
||||
the thread memory containing the stack for the thread ([MinidumpMemoryRegion]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#236))
|
||||
and the CPU context representing the CPU state of the thread at the time the
|
||||
dump was written ([MinidumpContext]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/minidump.h#171))
|
||||
are extracted from the minidump. If the thread being processed is the thread
|
||||
that produced the exception then a CPU context is obtained from the
|
||||
MinidumpException object instead, which represents the CPU state of the thread
|
||||
at the point of the exception. A stack walker is then instantiated by calling
|
||||
the [Stackwalker::StackwalkerForCPU]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#77)
|
||||
method and passing it the CPU context, the thread memory, the module list, as
|
||||
well as the SymbolSupplier and SourceLineResolverInterface. This method selects
|
||||
the specific !Stackwalker subclass based on the CPU architecture of the provided
|
||||
CPU context and returns an instance of that subclass.
|
||||
|
||||
## Walking a thread's stack
|
||||
|
||||
Once a !Stackwalker instance has been obtained, the processor calls the
|
||||
[Stackwalker::Walk]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h)
|
||||
method to obtain a list of frames representing the stack of this thread. The
|
||||
!Stackwalker starts by calling the GetContextFrame method which returns a
|
||||
StackFrame representing the top of the stack, with CPU state provided by the
|
||||
initial CPU context. From there, the stack walker repeats the following steps
|
||||
for each frame in turn:
|
||||
|
||||
### Finding the Module
|
||||
|
||||
The address of the instruction pointer of the current frame is used to determine
|
||||
which module contains the current frame by calling the module list's
|
||||
[GetModuleForAddress]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/code_modules.h#56)
|
||||
method.
|
||||
|
||||
### Locating Symbols
|
||||
|
||||
If a module is located, the SymbolSupplier is asked to locate symbols
|
||||
corresponding to the module by calling its [GetCStringSymbolData]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/symbol_supplier.h#87)
|
||||
method. Typically this is implemented by using the module's debug filename (the
|
||||
PDB filename for Windows dumps) and debug identifier (a GUID plus one extra
|
||||
digit) as a lookup key. The [SimpleSymbolSupplier]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/simple_symbol_supplier.cc)
|
||||
class simply uses these as parts of a file path to locate a flat file on disk.
|
||||
|
||||
### Loading Symbols
|
||||
|
||||
If a symbol file is located, the SourceLineResolverInterface is then asked to
|
||||
load the symbol file by calling its [LoadModuleUsingMemoryBuffer]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#71)
|
||||
method. The [BasicSourceLineResolver]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/basic_source_line_resolver.cc)
|
||||
implementation parses the text-format [symbol file](symbol_files.md) into
|
||||
in-memory data structures to make lookups by address of function names, source
|
||||
line information, and unwind information easy.
|
||||
|
||||
### Getting source line information
|
||||
|
||||
If a symbol file has been successfully loaded, the SourceLineResolverInterface's
|
||||
[FillSourceLineInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#89)
|
||||
method is called to provide a function name and source line information for the
|
||||
current frame. This is done by subtracting the base address of the module
|
||||
containing the current frame from the instruction pointer of the current frame
|
||||
to obtain a relative virtual address (RVA), which is a code offset relative to
|
||||
the start of the module. This RVA is then used as a lookup into a table of
|
||||
functions ([FUNC lines](SymbolFiles#FUNC_records.md) from the symbol file), each
|
||||
of which has an associated address range (function start address, function
|
||||
size). If a function is found whose address range contains the RVA, then its
|
||||
name is used. The RVA is then used as a lookup into a table of source lines
|
||||
([line records](SymbolFiles#Line_records.md) from the symbol file), each of
|
||||
which also has an associated address range. If a match is found it will provide
|
||||
the file name and source line associated with the current frame. If no match was
|
||||
found in the function table, another table of publicly exported symbols may be
|
||||
consulted ([PUBLIC lines](SymbolFiles#PUBLIC_records.md) from the symbol file).
|
||||
Public symbols contain only a start address, so the lookup simply looks for the
|
||||
nearest symbol that is less than the provided RVA.
|
||||
|
||||
### Finding the caller frame
|
||||
|
||||
To find the next frame in the stack, the !Stackwalker calls its [GetCallerFrame]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#186)
|
||||
method, passing in the current frame. Each !Stackwalker subclass implements
|
||||
GetCallerFrame differently, but there are common patterns.
|
||||
|
||||
Typically the first step is to query the SourceLineResolverInterface for the
|
||||
presence of detailed unwind information. This is done using its
|
||||
[FindWindowsFrameInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#96)
|
||||
and [FindCFIFrameInfo]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/source_line_resolver_interface.h#102)
|
||||
methods. These methods look for Windows unwind info extracted from a PDB file
|
||||
([STACK WIN](SymbolFiles#STACK_WIN_records.md) lines from the symbol file), or
|
||||
DWARF CFI extracted from a binary ([STACK CFI](SymbolFiles#STACK_CFI_records.md)
|
||||
lines from the symbol file) respectively. The information covers address ranges,
|
||||
so the RVA of the current frame is used for lookup as with function and source
|
||||
line information.
|
||||
|
||||
If unwind info is found it provides a set of rules to recover the register state
|
||||
of the caller frame given the current register state as well as the thread's
|
||||
stack memory. The rules are evaluated to produce the caller frame.
|
||||
|
||||
If unwind info is not found then the !Stackwalker may resort to other methods.
|
||||
Typically on architectures which specify a frame pointer unwinding by
|
||||
dereferencing the frame pointer is tried next. If that is successful it is used
|
||||
to produce the caller frame.
|
||||
|
||||
If no caller frame was found by any other method most !Stackwalker
|
||||
implementations resort to stack scanning by looking at each word on the stack
|
||||
down to a fixed depth (implemented in the [Stackwalker::ScanForReturnAddress]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#131)
|
||||
method) and using a heuristic to attempt to find a reasonable return address
|
||||
(implemented in the [Stackwalker::InstructionAddressSeemsValid]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/google_breakpad/processor/stackwalker.h#111)
|
||||
method).
|
||||
|
||||
If no caller frame is found or the caller frame seems invalid, stack walking
|
||||
stops. If a caller frame was found then these steps repeat using the new frame
|
||||
as the current frame.
|
497
docs/symbol_files.md
Normal file
497
docs/symbol_files.md
Normal file
@ -0,0 +1,497 @@
|
||||
# Introduction
|
||||
|
||||
Given a minidump file, the Breakpad processor produces stack traces that include
|
||||
function names and source locations. However, minidump files contain only the
|
||||
byte-by-byte contents of threads' registers and stacks, without function names
|
||||
or machine-code-to-source mapping data. The processor consults Breakpad symbol
|
||||
files for the information it needs to produce human-readable stack traces from
|
||||
the binary-only minidump file.
|
||||
|
||||
The platform-specific symbol dumping tools parse the debugging information the
|
||||
compiler provides (whether as DWARF or STABS sections in an ELF file or as
|
||||
stand-alone PDB files), and write that information back out in the Breakpad
|
||||
symbol file format. This format is much simpler and less detailed than compiler
|
||||
debugging information, and values legibility over compactness.
|
||||
|
||||
# Overview
|
||||
|
||||
Breakpad symbol files are ASCII text files, with lines delimited as appropriate
|
||||
for the host platform. Each line is a _record_, divided into fields by single
|
||||
spaces; in some cases, the last field of the record can contain spaces. The
|
||||
first field is a string indicating what sort of record the line represents
|
||||
(except for line records; these are very common, making them the default saves
|
||||
space). Some fields hold decimal or hexadecimal numbers; hexadecimal numbers
|
||||
have no "0x" prefix, and use lower-case letters.
|
||||
|
||||
Breakpad symbol files contain the following record types. With some
|
||||
restrictions, these may appear in any order.
|
||||
|
||||
* A `MODULE` record describes the executable file or shared library from which
|
||||
this data was derived, for use by symbol suppliers. A `MODULE' record should
|
||||
be the first record in the file.
|
||||
|
||||
* A `FILE` record gives a source file name, and assigns it a number by which
|
||||
other records can refer to it.
|
||||
|
||||
* A `FUNC` record describes a function present in the source code.
|
||||
|
||||
* A line record indicates to which source file and line a given range of
|
||||
machine code should be attributed. The line is attributed to the function
|
||||
defined by the most recent `FUNC` record.
|
||||
|
||||
* A `PUBLIC` record gives the address of a linker symbol.
|
||||
|
||||
* A `STACK` record provides information necessary to produce stack traces.
|
||||
|
||||
# `MODULE` records
|
||||
|
||||
A `MODULE` record provides meta-information about the module the symbol file
|
||||
describes. It has the form:
|
||||
|
||||
> `MODULE` _operatingsystem_ _architecture_ _id_ _name_
|
||||
|
||||
For example: `MODULE Linux x86 D3096ED481217FD4C16B29CD9BC208BA0 firefox-bin
|
||||
` These records provide meta-information about the executable or shared library
|
||||
from which this symbol file was generated. A symbol supplier might use this
|
||||
information to find the correct symbol files to use to interpret a given
|
||||
minidump, or to perform other sorts of validation. If present, a `MODULE` record
|
||||
should be the first line in the file.
|
||||
|
||||
The fields are separated by spaces, and cannot contain spaces themselves, except
|
||||
for _name_.
|
||||
|
||||
* The _operatingsystem_ field names the operating system on which the
|
||||
executable or shared library was intended to run. This field should have one
|
||||
of the following values: | **Value** | **Meaning** |
|
||||
|:----------|:--------------------| | Linux | Linux | | mac | Macintosh OSX
|
||||
| | windows | Microsoft Windows |
|
||||
|
||||
* The _architecture_ field indicates what processor architecture the
|
||||
executable or shared library contains machine code for. This field should
|
||||
have one of the following values: | **Value** | **Instruction Set
|
||||
Architecture** | |:----------|:---------------------------------| | x86 |
|
||||
Intel IA-32 | | x86\_64 | AMD64/Intel 64 | | ppc | 32-bit PowerPC | | ppc64
|
||||
| 64-bit PowerPC | | unknown | unknown |
|
||||
|
||||
* The _id_ field is a sequence of hexadecimal digits that identifies the exact
|
||||
executable or library whose contents the symbol file describes. The way in
|
||||
which it is computed varies from platform to platform.
|
||||
|
||||
* The _name_ field contains the base name (the final component of the
|
||||
directory path) of the executable or library. It may contain spaces, and
|
||||
extends to the end of the line.
|
||||
|
||||
# `FILE` records
|
||||
|
||||
A `FILE` record holds a source file name for other records to refer to. It has
|
||||
the form:
|
||||
|
||||
> `FILE` _number_ _name_
|
||||
|
||||
For example: `FILE 2 /home/jimb/mc/in/browser/app/nsBrowserApp.cpp
|
||||
`
|
||||
|
||||
A `FILE` record provides the name of a source file, and assigns it a number
|
||||
which other records (line records, in particular) can use to refer to that file
|
||||
name. The _number_ field is a decimal number. The _name_ field is the name of
|
||||
the file; it may contain spaces.
|
||||
|
||||
# `FUNC` records
|
||||
|
||||
A `FUNC` record describes a source-language function. It has the form:
|
||||
|
||||
> `FUNC` _address_ _size_ _parameter\_size_ _name_
|
||||
|
||||
For example: `FUNC c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&,
|
||||
void**) const
|
||||
`
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
||||
address and length in bytes of the machine code instructions the function
|
||||
occupies. (Breakpad symbol files cannot accurately describe functions whose code
|
||||
is not contiguous.) The start address is relative to the module's load address.
|
||||
|
||||
The _parameter\_size_ field is a hexadecimal number indicating the size, in
|
||||
bytes, of the arguments pushed on the stack for this function. Some calling
|
||||
conventions, like the Microsoft Windows `stdcall` convention, require the called
|
||||
function to pop parameters passed to it on the stack from its caller before
|
||||
returning. The stack walker uses this value, along with data from `STACK`
|
||||
records, to step from the called function's frame to the caller's frame.
|
||||
|
||||
The _name_ field is the name of the function. In languages that use linker
|
||||
symbol name mangling like C++, this should be the source language name (the
|
||||
"unmangled" form). This field may contain spaces.
|
||||
|
||||
# Line records
|
||||
|
||||
A line record describes the source file and line number to which a given range
|
||||
of machine code should be attributed. It has the form:
|
||||
|
||||
> _address_ _size_ _line_ _filenum_
|
||||
|
||||
For example: `c184 7 59 4
|
||||
`
|
||||
|
||||
Because they are so common, line records do not begin with a string indicating
|
||||
the record type. All other record types' names use upper-case letters;
|
||||
hexadecimal numbers, like a line record's _address_, use lower-case letters.
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers indicating the start
|
||||
address and length in bytes of the machine code. The address is relative to the
|
||||
module's load address.
|
||||
|
||||
The _line_ field is the line number to which the machine code should be
|
||||
attributed, in decimal; the first line of the source file is line number 1. The
|
||||
_filenum_ field is a decimal number appearing in a prior `FILE` record; the name
|
||||
given in that record is the source file name for the machine code.
|
||||
|
||||
The line is assumed to belong to the function described by the last preceding
|
||||
`FUNC` record. Line records may not appear before the first `FUNC' record.
|
||||
|
||||
No two line records in a symbol file cover the same range of addresses. However,
|
||||
there may be many line records with identical line and file numbers, as a given
|
||||
source line may contribute many non-contiguous blocks of machine code.
|
||||
|
||||
# `PUBLIC` records
|
||||
|
||||
A `PUBLIC` record describes a publicly visible linker symbol, such as that used
|
||||
to identify an assembly language entry point or region of memory. It has the
|
||||
form:
|
||||
|
||||
> PUBLIC _address_ _parameter\_size_ _name_
|
||||
|
||||
For example: `PUBLIC 2160 0 Public2_1
|
||||
`
|
||||
|
||||
The Breakpad processor essentially treats a `PUBLIC` record as defining a
|
||||
function with no line number data and an indeterminate size: the code extends to
|
||||
the next address mentioned. If a given address is covered by both a `PUBLIC`
|
||||
record and a `FUNC` record, the processor uses the `FUNC` data.
|
||||
|
||||
The _address_ field is a hexadecimal number indicating the symbol's address,
|
||||
relative to the module's load address.
|
||||
|
||||
The _parameter\_size_ field is a hexadecimal number indicating the size of the
|
||||
parameters passed to the code whose entry point the symbol marks, if known. This
|
||||
field has the same meaning as the _parameter\_size_ field of a `FUNC` record;
|
||||
see that description for more details.
|
||||
|
||||
The _name_ field is the name of the symbol. In languages that use linker symbol
|
||||
name mangling like C++, this should be the source language name (the "unmangled"
|
||||
form). This field may contain spaces.
|
||||
|
||||
# `STACK WIN` records
|
||||
|
||||
Given a stack frame, a `STACK WIN` record indicates how to find the frame that
|
||||
called it. It has the form:
|
||||
|
||||
> STACK WIN _type_ _rva_ _code\_size_ _prologue\_size_ _epilogue\_size_
|
||||
> _parameter\_size_ _saved\_register\_size_ _local\_size_ _max\_stack\_size_
|
||||
> _has\_program\_string_ _program\_string\_OR\_allocates\_base\_pointer_
|
||||
|
||||
For example: `STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + =
|
||||
$ebp $ebp ^ =
|
||||
`
|
||||
|
||||
All fields of a `STACK WIN` record, except for the last, are hexadecimal
|
||||
numbers.
|
||||
|
||||
The _type_ field indicates what sort of stack frame data this record holds. Its
|
||||
value should be one of the values of the [StackFrameTypeEnum]
|
||||
(http://msdn.microsoft.com/en-us/library/bc5207xw%28VS.100%29.aspx) type in
|
||||
Microsoft's [Debug Interface Access (DIA)]
|
||||
(http://msdn.microsoft.com/en-us/library/x93ctkx8%28VS.100%29.aspx) API.
|
||||
Breakpad uses only records of type 4 (`FrameTypeFrameData`) and 0
|
||||
(`FrameTypeFPO`); it ignores others. These types differ only in whether the last
|
||||
field is an _allocates\_base\_pointer_ flag (`FrameTypeFPO`) or a program string
|
||||
(`FrameTypeFrameData`). If more than one record covers a given address, Breakpad
|
||||
prefers `FrameTypeFrameData` records over `FrameTypeFPO` records.
|
||||
|
||||
The _rva_ and _code\_size_ fields give the starting address and length in bytes
|
||||
of the machine code covered by this record. The starting address is relative to
|
||||
the module's load address.
|
||||
|
||||
The _prologue\_size_ and _epilogue\_size_ fields give the length, in bytes, of
|
||||
the prologue and epilogue machine code within the record's range. Breakpad does
|
||||
not use these values.
|
||||
|
||||
The _parameter\_size_ field gives the number of argument bytes this function
|
||||
expects to have been passed. This field has the same meaning as the
|
||||
_parameter\_size_ field of a `FUNC` record; see that description for more
|
||||
details.
|
||||
|
||||
The _saved\_register\_size_ field gives the number of bytes in the stack frame
|
||||
dedicated to preserving the values of any callee-saves registers used by this
|
||||
function.
|
||||
|
||||
The _local\_size_ field gives the number of bytes in the stack frame dedicated
|
||||
to holding the function's local variables and temporary values.
|
||||
|
||||
The _max\_stack\_size_ field gives the maximum number of bytes pushed on the
|
||||
stack in the frame. Breakpad does not use this value.
|
||||
|
||||
If the _has\_program\_string_ field is zero, then the `STACK WIN` record's final
|
||||
field is an _allocates\_base\_pointer_ flag, as a hexadecimal number; this is
|
||||
expected for records whose _type_ is 0. Otherwise, the final field is a program
|
||||
string.
|
||||
|
||||
## Interpreting a `STACK WIN` record
|
||||
|
||||
Given the register values for a frame F, we can find the calling frame as
|
||||
follows:
|
||||
|
||||
* If the _has\_program\_string_ field of a `STACK WIN` record is zero, then
|
||||
the final field is _allocates\_base\_pointer_, a flag indicating whether the
|
||||
frame uses the frame pointer register, `%ebp`, as a general-purpose
|
||||
register.
|
||||
* If _allocates\_base\_pointer_ is true, then `%ebp` does not point to the
|
||||
frame's base address. Instead,
|
||||
* Let _next\_parameter\_size_ be the parameter size of the function
|
||||
frame F called (**not** this record's _parameter\_size_ field), or
|
||||
zero if F is the youngest frame on the stack. You must find this
|
||||
value in F's callee's `FUNC`, `STACK WIN`, or `PUBLIC` records.
|
||||
* Let _frame\_size_ be the sum of the _local\_size_ field, the
|
||||
_saved\_register\_size_ field, and _next\_parameter\_size_. > > With
|
||||
those definitions in place, we can recover the calling frame as
|
||||
follows:
|
||||
* F's return address is at `%esp +`_frame\_size_,
|
||||
* the caller's value of `%ebp` is saved at `%esp
|
||||
+`_next\_parameter\_size_`+`_saved\_register\_size_`- 8`, and
|
||||
* the caller's value of `%esp` just before the call instruction was
|
||||
`%esp +`_frame\_size_`+ 4`. > > (Why do we include
|
||||
_next\_parameter\_size_ in the sum when computing _frame\_size_ and
|
||||
the address of the saved `%ebp`? When a function A has called a
|
||||
function B, the arguments that A pushed for B are considered part of
|
||||
A's stack frame: A's value for `%esp` points at the last argument
|
||||
pushed for B. Thus, we must include the size of those arguments
|
||||
(given by the debugging info for B) along with the size of A's
|
||||
register save area and local variable area (given by the debugging
|
||||
info for A) when computing the overall size of A's frame.)
|
||||
* If _allocates\_base\_pointer_ is false, then F's function doesn't use
|
||||
`%ebp` at all. You may recover the calling frame as above, except that
|
||||
the caller's value of `%ebp` is the same as F's value for `%ebp`, so no
|
||||
steps are necessary to recover it.
|
||||
* If the _has\_program\_string_ field of a `STACK WIN` record is not zero,
|
||||
then the record's final field is a string containing a program to be
|
||||
interpreted to recover the caller's frame. The comments in the
|
||||
[postfix\_evaluator.h]
|
||||
(http://code.google.com/p/google-breakpad/source/browse/trunk/src/processor/postfix_evaluator.h#40)
|
||||
header file explain the language in which the program is written. You should
|
||||
place the following variables in the dictionary before interpreting the
|
||||
program:
|
||||
* `$ebp` and `$esp` should be the values of the `%ebp` and `%esp`
|
||||
registers in F.
|
||||
* `.cbParams`, `.cbSavedRegs`, and `.cbLocals`, should be the values of
|
||||
the `STACK WIN` record's _parameter\_size_, _saved\_register\_size_, and
|
||||
_local\_size_ fields.
|
||||
* `.raSearchStart` should be set to the address on the stack to begin
|
||||
scanning for a return address, if necessary. The Breakpad processor sets
|
||||
this to the value of `%esp` in F, plus the _frame\_size_ value mentioned
|
||||
above.
|
||||
|
||||
> If the program stores values for `$eip`, `$esp`, `$ebp`, `$ebx`, `$esi`, or
|
||||
> `$edi`, then those are the values of the given registers in the caller. If the
|
||||
> value of `$eip` is zero, that indicates that the end of the stack has been
|
||||
> reached.
|
||||
|
||||
The Breakpad processor checks that the value yielded by the above for the
|
||||
calling frame's instruction address refers to known code; if the address seems
|
||||
to be bogus, then it uses a heuristic search to find F's return address and
|
||||
stack base.
|
||||
|
||||
# `STACK CFI` records
|
||||
|
||||
`STACK CFI` ("Call Frame Information") records describe how to walk the stack
|
||||
when execution is at a given machine instruction. These records take one of two
|
||||
forms:
|
||||
|
||||
> `STACK CFI INIT` _address_ _size_ _register<sub>1</sub>_:
|
||||
> _expression<sub>1</sub>_ _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
|
||||
>
|
||||
> `STACK CFI` _address_ _register<sub>1</sub>_: _expression<sub>1</sub>_
|
||||
> _register<sub>2</sub>_: _expression<sub>2</sub>_ ...
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
STACK CFI INIT 804c4b0 40 .cfa: $esp 4 + $eip: .cfa 4 - ^
|
||||
STACK CFI 804c4b1 .cfa: $esp 8 + $ebp: .cfa 8 - ^
|
||||
```
|
||||
|
||||
The _address_ and _size_ fields are hexadecimal numbers. Each
|
||||
_register_<sub>i</sub> is the name of a register or pseudoregister. Each
|
||||
_expression_ is a Breakpad postfix expression, which may contain spaces, but
|
||||
never ends with a colon. (The appropriate register names for a given
|
||||
architecture are determined when `STACK CFI` records are first enabled for that
|
||||
architecture, and should be documented in the appropriate
|
||||
`stackwalker_`_architecture_`.cc` source file.)
|
||||
|
||||
STACK CFI records describe, at each machine instruction in a given function, how
|
||||
to recover the values the machine registers had in the function's caller.
|
||||
Naturally, some registers' values are simply lost, but there are three cases in
|
||||
which they can be recovered:
|
||||
|
||||
* You can always recover the program counter, because that's the function's
|
||||
return address. If the function is ever going to return, the PC must be
|
||||
saved somewhere.
|
||||
|
||||
* You can always recover the stack pointer. The function is responsible for
|
||||
popping its stack frame before it returns to the caller, so it must be able
|
||||
to restore this, as well.
|
||||
|
||||
* You should be able to recover the values of callee-saves registers. These
|
||||
are registers whose values the callee must preserve, either by saving them
|
||||
in its own stack frame before using them and re-loading them before
|
||||
returning, or by not using them at all.
|
||||
|
||||
(As an exception, note that functions which never return may not save any of
|
||||
this data. It may not be possible to walk the stack past such functions' stack
|
||||
frames.)
|
||||
|
||||
Given rules for recovering the values of a function's caller's registers, we can
|
||||
walk up the stack. Starting with the current set of registers --- the PC of the
|
||||
instruction we're currently executing, the current stack pointer, etc. --- we
|
||||
use CFI to recover the values those registers had in the caller of the current
|
||||
frame. This gives us a PC in the caller whose CFI we can look up; we apply the
|
||||
process again to find that function's caller; and so on.
|
||||
|
||||
Concretely, CFI records represent a table with a row for each machine
|
||||
instruction address and a column for each register. The table entry for a given
|
||||
address and register contains a rule describing how, when the PC is at that
|
||||
address, to restore the value that register had in the caller.
|
||||
|
||||
There are some special columns:
|
||||
|
||||
* A column named `.cfa`, for "Canonical Frame Address", tells how to compute
|
||||
the base address of the frame; other entries can refer to the CFA in their
|
||||
rules.
|
||||
|
||||
* A column named `.ra` represents the return address.
|
||||
|
||||
For example, suppose we have a machine with 32-bit registers, one-byte
|
||||
instructions, a stack that grows downwards, and an assembly language that
|
||||
resembles C. Suppose further that we have a function whose machine code looks
|
||||
like this:
|
||||
|
||||
```
|
||||
func: ; entry point; return address at sp
|
||||
func+0: sp -= 16 ; allocate space for stack frame
|
||||
func+1: sp[12] = r0 ; save 4-byte r0 at sp+12
|
||||
... ; stuff that doesn't affect stack
|
||||
func+10: sp -= 4; *sp = x ; push some 4-byte x on the stack
|
||||
... ; stuff that doesn't affect stack
|
||||
func+20: r0 = sp[16] ; restore saved r0
|
||||
func+21: sp += 20 ; pop whole stack frame
|
||||
func+22: pc = *sp; sp += 4 ; pop return address and jump to it
|
||||
```
|
||||
|
||||
The following table would describe the function above:
|
||||
|
||||
**code address** | **.cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **.ra**
|
||||
:--------------- | :------- | :---------------------- | :---------------------- | :-- | :-------
|
||||
func+0 | sp | | | | `cfa[0]`
|
||||
func+1 | sp+16 | | | | `cfa[0]`
|
||||
func+2 | sp+16 | `cfa[-4]` | | | `cfa[0]`
|
||||
func+11 | sp+20 | `cfa[-4]` | | | `cfa[0]`
|
||||
func+21 | sp+20 | | | | `cfa[0]`
|
||||
func+22 | sp | | | | `cfa[0]`
|
||||
|
||||
Some things to note here:
|
||||
|
||||
* Each row describes the state of affairs **before** executing the instruction
|
||||
at the given address. Thus, the row for func+0 describes the state before we
|
||||
execute the first instruction, which allocates the stack frame. In the next
|
||||
row, the formula for computing the CFA has changed, reflecting the
|
||||
allocation.
|
||||
|
||||
* The other entries are written in terms of the CFA; this allows them to
|
||||
remain unchanged as the stack pointer gets bumped around. For example, to
|
||||
find the caller's value for r0 (on Google Code) at func+2, we would first
|
||||
compute the CFA by adding 16 to the sp, and then subtract four from that to
|
||||
find the address at which r0 (on Google Code) was saved.
|
||||
|
||||
* Although the example doesn't show this, most calling conventions designate
|
||||
"callee-saves" and "caller-saves" registers. The callee must restore the
|
||||
values of "callee-saves" registers before returning (if it uses them at
|
||||
all), whereas the callee is free to use "caller-saves" registers without
|
||||
restoring their values. A function that uses caller-saves registers
|
||||
typically does not save their original values at all; in this case, the CFI
|
||||
marks such registers' values as "unrecoverable".
|
||||
|
||||
* Exactly where the CFA points in the frame --- at the return address? below
|
||||
it? At some fixed point within the frame? --- is a question of definition
|
||||
that depends on the architecture and ABI in use. But by definition, the CFA
|
||||
remains constant throughout the lifetime of the frame. It's up to
|
||||
architecture- specific code to know what significance to assign the CFA, if
|
||||
any.
|
||||
|
||||
To save space, the most common type of CFI record only mentions the table
|
||||
entries at which changes take place. So for the above, the CFI data would only
|
||||
actually mention the non-blank entries here:
|
||||
|
||||
**insn** | **cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **ra**
|
||||
:------- | :------ | :---------------------- | :---------------------- | :-- | :-------
|
||||
func+0 | sp | | | | `cfa[0]`
|
||||
func+1 | sp+16 | | | |
|
||||
func+2 | | `cfa[-4]` | | |
|
||||
func+11 | sp+20 | | | |
|
||||
func+21 | | r0 (on Google Code) | | |
|
||||
func+22 | sp | | | |
|
||||
|
||||
A `STACK CFI INIT` record indicates that, at the machine instruction at
|
||||
_address_, belonging to some function, the value that _register<sub>n</sub>_ had
|
||||
in that function's caller can be recovered by evaluating
|
||||
_expression<sub>n</sub>_. The values of any callee-saves registers not mentioned
|
||||
are assumed to be unchanged. (`STACK CFI` records never mention caller-saves
|
||||
registers.) These rules apply starting at _address_ and continue up to, but not
|
||||
including, the address given in the next `STACK CFI` record. The _size_ field is
|
||||
the total number of bytes of machine code covered by this record and any
|
||||
subsequent `STACK CFI` records (until the next `STACK CFI INIT` record). The
|
||||
_address_ field is relative to the module's load address.
|
||||
|
||||
A `STACK CFI` record (no `INIT`) is the same, except that it mentions only those
|
||||
registers whose recovery rules have changed from the previous CFI record. There
|
||||
must be a prior `STACK CFI INIT` or `STACK CFI` record in the symbol file. The
|
||||
_address_ field of this record must be greater than that of the previous record,
|
||||
and it must not be at or beyond the end of the range given by the most recent
|
||||
`STACK CFI INIT` record. The address is relative to the module's load address.
|
||||
|
||||
Each expression is a breakpad-style postfix expression. Expressions may contain
|
||||
spaces, but their tokens may not end with colons. When an expression mentions a
|
||||
register, it refers to the value of that register in the callee, even if a prior
|
||||
name/expression pair gives that register's value in the caller. The exception is
|
||||
`.cfa`, which refers to the canonical frame address computed by the .cfa rule in
|
||||
force at the current instruction.
|
||||
|
||||
The special expression `.undef` indicates that the given register's value cannot
|
||||
be recovered.
|
||||
|
||||
The register names preceding the expressions are always followed by colons. The
|
||||
expressions themselves never contain tokens ending with colons.
|
||||
|
||||
There are two special register names:
|
||||
|
||||
* `.cfa` ("Canonical Frame Address") is the base address of the stack frame.
|
||||
Other registers' rules may refer to this. If no rule is provided for the
|
||||
stack pointer, the value of `.cfa` is the caller's stack pointer.
|
||||
|
||||
* `.ra` is the return address. This is the value of the restored program
|
||||
counter. We use `.ra` instead of the architecture-specific name for the
|
||||
program counter.
|
||||
|
||||
The Breakpad stack walker requires that there be rules in force for `.cfa` and
|
||||
`.ra` at every code address from which it unwinds. If those rules are not
|
||||
present, the stack walker will ignore the `STACK CFI` data, and try to use a
|
||||
different strategy.
|
||||
|
||||
So the CFI for the example function above would be as follows, if `func` were at
|
||||
address 0x1000 (relative to the module's load address):
|
||||
|
||||
```
|
||||
STACK CFI INIT 1000 .cfa: $sp .ra: .cfa ^
|
||||
STACK CFI 1001 .cfa: $sp 16 +
|
||||
STACK CFI 1002 $r0: .cfa 4 - ^
|
||||
STACK CFI 100b .cfa: $sp 20 +
|
||||
STACK CFI 1015 $r0: $r0
|
||||
STACK CFI 1016 .cfa: $sp
|
||||
```
|
70
docs/windows_client_integration.md
Normal file
70
docs/windows_client_integration.md
Normal file
@ -0,0 +1,70 @@
|
||||
# Windows Integration overview
|
||||
|
||||
## Windows Client Code
|
||||
|
||||
The Windows client code is in the `src/client/windows` directory of the tree.
|
||||
Since the header files are fairly well commented some specifics are purposely
|
||||
omitted from this document.
|
||||
|
||||
## Integration of minidump-generation
|
||||
|
||||
Once you build the solution inside `src/client/windows`, an output file of
|
||||
`exception_handler.lib` will be generated. You can either check this into your
|
||||
project's directory or build directly from the source, as the project itself
|
||||
does.
|
||||
|
||||
Enabling Breakpad in your application requires you to `#include
|
||||
"exception_handler.h"` and instantiate the `ExceptionHandler` object like so:
|
||||
|
||||
```
|
||||
handler = new ExceptionHandler(const wstring& dump_path,
|
||||
FilterCallback filter,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context,
|
||||
int handler_types,
|
||||
MINIDUMP_TYPE dump_type,
|
||||
const wchar_t* pipe_name,
|
||||
const CustomClientInfo* custom_info);
|
||||
```
|
||||
|
||||
The parameters, in order, are:
|
||||
|
||||
* pathname for minidumps to be written to - this is ignored if OOP dump
|
||||
generation is used
|
||||
* A callback that is called when the exception is first handled - you can
|
||||
return true/false here to continue/stop exception processing
|
||||
* A callback that is called after minidumps have been written
|
||||
* Context for the callbacks
|
||||
* Which exceptions to handle - see `HandlerType` enumeration in
|
||||
exception\_handler.h
|
||||
* The type of minidump to generate, using the `MINIDUMP_TYPE` definitions in
|
||||
`DbgHelp.h`
|
||||
* A pipe name that can be used to communicate with a crash generation server
|
||||
* A pointer to a CustomClientInfo class that can be used to send custom data
|
||||
along with the minidump when using OOP generation
|
||||
|
||||
You can also see `src/client/windows/tests/crash_generation_app/*` for a sample
|
||||
app that uses OOP generation.
|
||||
|
||||
## OOP Minidump Generation
|
||||
|
||||
For out of process minidump generation, more work is needed. If you look inside
|
||||
`src/client/windows/crash_generation`, you will see a file called
|
||||
`crash_generation_server.h`. This file is the interface for a crash generation
|
||||
server, which must be instantiated with the same pipe name that is passed to the
|
||||
client above. The logistics of running a separate process that instantiates the
|
||||
crash generation server is left up to you, however.
|
||||
|
||||
## Build process specifics(symbol generation, upload)
|
||||
|
||||
The symbol creation step is talked about in the general overview doc, since it
|
||||
doesn't vary much by platform. You'll need to make sure that the symbols are
|
||||
available wherever minidumps are uploaded to for processing.
|
||||
|
||||
## Out in the field - uploading the minidump
|
||||
|
||||
Inside `src/client/windows/sender` is a class implementation called
|
||||
`CrashReportSender`. This class can be compiled into a separate standalone CLI
|
||||
or in the crash generation server and used to upload the report; it can know
|
||||
when to do so via one of the callbacks provided by the `CrashGenerationServer`
|
||||
or the `ExceptionHandler` object for in-process generation.
|
558
m4/ax_cxx_compile_stdcxx.m4
Normal file
558
m4/ax_cxx_compile_stdcxx.m4
Normal file
@ -0,0 +1,558 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check for baseline language coverage in the compiler for the specified
|
||||
# version of the C++ standard. If necessary, add switches to CXXFLAGS to
|
||||
# enable support. VERSION may be '11' (for the C++11 standard) or '14'
|
||||
# (for the C++14 standard).
|
||||
#
|
||||
# The second argument, if specified, indicates whether you insist on an
|
||||
# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
|
||||
# -std=c++11). If neither is specified, you get whatever works, with
|
||||
# preference for an extended mode.
|
||||
#
|
||||
# The third argument, if specified 'mandatory' or if left unspecified,
|
||||
# indicates that baseline support for the specified C++ standard is
|
||||
# required and that the macro should error out if no mode with that
|
||||
# support is found. If specified 'optional', then configuration proceeds
|
||||
# regardless, after defining HAVE_CXX${VERSION} if and only if a
|
||||
# supporting mode is found.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
|
||||
# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
|
||||
# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
|
||||
# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
|
||||
# Copyright (c) 2015 Paul Norman <penorman@mac.com>
|
||||
# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 1
|
||||
|
||||
dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
|
||||
dnl (serial version number 13).
|
||||
|
||||
AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
|
||||
m4_if([$1], [11], [],
|
||||
[$1], [14], [],
|
||||
[$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
|
||||
[m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$2], [], [],
|
||||
[$2], [ext], [],
|
||||
[$2], [noext], [],
|
||||
[m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
|
||||
m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
|
||||
[$3], [optional], [ax_cxx_compile_cxx$1_required=false],
|
||||
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
|
||||
AC_LANG_PUSH([C++])dnl
|
||||
ac_success=no
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
|
||||
ax_cv_cxx_compile_cxx$1,
|
||||
[AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[ax_cv_cxx_compile_cxx$1=yes],
|
||||
[ax_cv_cxx_compile_cxx$1=no])])
|
||||
if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
|
||||
ac_success=yes
|
||||
fi
|
||||
|
||||
m4_if([$2], [noext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
for switch in -std=gnu++$1 -std=gnu++0x; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
|
||||
m4_if([$2], [ext], [], [dnl
|
||||
if test x$ac_success = xno; then
|
||||
dnl HP's aCC needs +std=c++11 according to:
|
||||
dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
|
||||
dnl Cray's crayCC needs "-h std=c++11"
|
||||
for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do
|
||||
cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
|
||||
AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
|
||||
$cachevar,
|
||||
[ac_save_CXXFLAGS="$CXXFLAGS"
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
|
||||
[eval $cachevar=yes],
|
||||
[eval $cachevar=no])
|
||||
CXXFLAGS="$ac_save_CXXFLAGS"])
|
||||
if eval test x\$$cachevar = xyes; then
|
||||
CXXFLAGS="$CXXFLAGS $switch"
|
||||
ac_success=yes
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi])
|
||||
AC_LANG_POP([C++])
|
||||
if test x$ax_cxx_compile_cxx$1_required = xtrue; then
|
||||
if test x$ac_success = xno; then
|
||||
AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
|
||||
fi
|
||||
else
|
||||
if test x$ac_success = xno; then
|
||||
HAVE_CXX$1=0
|
||||
AC_MSG_NOTICE([No compiler with C++$1 support was found])
|
||||
else
|
||||
HAVE_CXX$1=1
|
||||
AC_DEFINE(HAVE_CXX$1,1,
|
||||
[define if the compiler supports basic C++$1 syntax])
|
||||
fi
|
||||
|
||||
AC_SUBST(HAVE_CXX$1)
|
||||
fi
|
||||
])
|
||||
|
||||
|
||||
dnl Test body for checking C++11 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
)
|
||||
|
||||
|
||||
dnl Test body for checking C++14 support
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_11
|
||||
_AX_CXX_COMPILE_STDCXX_testbody_new_in_14
|
||||
)
|
||||
|
||||
|
||||
dnl Tests for new features in C++11
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++11, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201103L
|
||||
|
||||
#error "This is not a C++11 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx11
|
||||
{
|
||||
|
||||
namespace test_static_assert
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct check
|
||||
{
|
||||
static_assert(sizeof(int) <= sizeof(T), "not big enough");
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_final_override
|
||||
{
|
||||
|
||||
struct Base
|
||||
{
|
||||
virtual void f() {}
|
||||
};
|
||||
|
||||
struct Derived : public Base
|
||||
{
|
||||
virtual void f() override {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace test_double_right_angle_brackets
|
||||
{
|
||||
|
||||
template < typename T >
|
||||
struct check {};
|
||||
|
||||
typedef check<void> single_type;
|
||||
typedef check<check<void>> double_type;
|
||||
typedef check<check<check<void>>> triple_type;
|
||||
typedef check<check<check<check<void>>>> quadruple_type;
|
||||
|
||||
}
|
||||
|
||||
namespace test_decltype
|
||||
{
|
||||
|
||||
int
|
||||
f()
|
||||
{
|
||||
int a = 1;
|
||||
decltype(a) b = 2;
|
||||
return a + b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_type_deduction
|
||||
{
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
auto
|
||||
add(T1 a1, T2 a2) -> decltype(a1 + a2)
|
||||
{
|
||||
return a1 + a2;
|
||||
}
|
||||
|
||||
int
|
||||
test(const int c, volatile int v)
|
||||
{
|
||||
static_assert(is_same<int, decltype(0)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(c)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(v)>::value == false, "");
|
||||
auto ac = c;
|
||||
auto av = v;
|
||||
auto sumi = ac + av + 'x';
|
||||
auto sumf = ac + av + 1.0;
|
||||
static_assert(is_same<int, decltype(ac)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(av)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumi)>::value == true, "");
|
||||
static_assert(is_same<int, decltype(sumf)>::value == false, "");
|
||||
static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
|
||||
return (sumf > 0.0) ? sumi : add(c, v);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_noexcept
|
||||
{
|
||||
|
||||
int f() { return 0; }
|
||||
int g() noexcept { return 0; }
|
||||
|
||||
static_assert(noexcept(f()) == false, "");
|
||||
static_assert(noexcept(g()) == true, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
|
||||
{
|
||||
return *s ? strlen_c_r(s + 1, acc + 1) : acc;
|
||||
}
|
||||
|
||||
template < typename CharT >
|
||||
unsigned long constexpr
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
return strlen_c_r(s, 0UL);
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("1") == 1UL, "");
|
||||
static_assert(strlen_c("example") == 7UL, "");
|
||||
static_assert(strlen_c("another\0example") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_rvalue_references
|
||||
{
|
||||
|
||||
template < int N >
|
||||
struct answer
|
||||
{
|
||||
static constexpr int value = N;
|
||||
};
|
||||
|
||||
answer<1> f(int&) { return answer<1>(); }
|
||||
answer<2> f(const int&) { return answer<2>(); }
|
||||
answer<3> f(int&&) { return answer<3>(); }
|
||||
|
||||
void
|
||||
test()
|
||||
{
|
||||
int i = 0;
|
||||
const int c = 0;
|
||||
static_assert(decltype(f(i))::value == 1, "");
|
||||
static_assert(decltype(f(c))::value == 2, "");
|
||||
static_assert(decltype(f(0))::value == 3, "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_uniform_initialization
|
||||
{
|
||||
|
||||
struct test
|
||||
{
|
||||
static const int zero {};
|
||||
static const int one {1};
|
||||
};
|
||||
|
||||
static_assert(test::zero == 0, "");
|
||||
static_assert(test::one == 1, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambdas
|
||||
{
|
||||
|
||||
void
|
||||
test1()
|
||||
{
|
||||
auto lambda1 = [](){};
|
||||
auto lambda2 = lambda1;
|
||||
lambda1();
|
||||
lambda2();
|
||||
}
|
||||
|
||||
int
|
||||
test2()
|
||||
{
|
||||
auto a = [](int i, int j){ return i + j; }(1, 2);
|
||||
auto b = []() -> int { return '0'; }();
|
||||
auto c = [=](){ return a + b; }();
|
||||
auto d = [&](){ return c; }();
|
||||
auto e = [a, &b](int x) mutable {
|
||||
const auto identity = [](int y){ return y; };
|
||||
for (auto i = 0; i < a; ++i)
|
||||
a += b--;
|
||||
return x + identity(a + b);
|
||||
}(0);
|
||||
return a + b + c + d + e;
|
||||
}
|
||||
|
||||
int
|
||||
test3()
|
||||
{
|
||||
const auto nullary = [](){ return 0; };
|
||||
const auto unary = [](int x){ return x; };
|
||||
using nullary_t = decltype(nullary);
|
||||
using unary_t = decltype(unary);
|
||||
const auto higher1st = [](nullary_t f){ return f(); };
|
||||
const auto higher2nd = [unary](nullary_t f1){
|
||||
return [unary, f1](unary_t f2){ return f2(unary(f1())); };
|
||||
};
|
||||
return higher1st(nullary) + higher2nd(nullary)(unary);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_variadic_templates
|
||||
{
|
||||
|
||||
template <int...>
|
||||
struct sum;
|
||||
|
||||
template <int N0, int... N1toN>
|
||||
struct sum<N0, N1toN...>
|
||||
{
|
||||
static constexpr auto value = N0 + sum<N1toN...>::value;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct sum<>
|
||||
{
|
||||
static constexpr auto value = 0;
|
||||
};
|
||||
|
||||
static_assert(sum<>::value == 0, "");
|
||||
static_assert(sum<1>::value == 1, "");
|
||||
static_assert(sum<23>::value == 23, "");
|
||||
static_assert(sum<1, 2>::value == 3, "");
|
||||
static_assert(sum<5, 5, 11>::value == 21, "");
|
||||
static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
|
||||
|
||||
}
|
||||
|
||||
// http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
|
||||
// Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
|
||||
// because of this.
|
||||
namespace test_template_alias_sfinae
|
||||
{
|
||||
|
||||
struct foo {};
|
||||
|
||||
template<typename T>
|
||||
using member = typename T::member_type;
|
||||
|
||||
template<typename T>
|
||||
void func(...) {}
|
||||
|
||||
template<typename T>
|
||||
void func(member<T>*) {}
|
||||
|
||||
void test();
|
||||
|
||||
void test() { func<foo>(0); }
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx11
|
||||
|
||||
#endif // __cplusplus >= 201103L
|
||||
|
||||
]])
|
||||
|
||||
|
||||
dnl Tests for new features in C++14
|
||||
|
||||
m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
|
||||
|
||||
// If the compiler admits that it is not ready for C++14, why torture it?
|
||||
// Hopefully, this will speed up the test.
|
||||
|
||||
#ifndef __cplusplus
|
||||
|
||||
#error "This is not a C++ compiler"
|
||||
|
||||
#elif __cplusplus < 201402L
|
||||
|
||||
#error "This is not a C++14 compiler"
|
||||
|
||||
#else
|
||||
|
||||
namespace cxx14
|
||||
{
|
||||
|
||||
namespace test_polymorphic_lambdas
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
const auto lambda = [](auto&&... args){
|
||||
const auto istiny = [](auto x){
|
||||
return (sizeof(x) == 1UL) ? 1 : 0;
|
||||
};
|
||||
const int aretiny[] = { istiny(args)... };
|
||||
return aretiny[0];
|
||||
};
|
||||
return lambda(1, 1L, 1.0f, '1');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_binary_literals
|
||||
{
|
||||
|
||||
constexpr auto ivii = 0b0000000000101010;
|
||||
static_assert(ivii == 42, "wrong value");
|
||||
|
||||
}
|
||||
|
||||
namespace test_generalized_constexpr
|
||||
{
|
||||
|
||||
template < typename CharT >
|
||||
constexpr unsigned long
|
||||
strlen_c(const CharT *const s) noexcept
|
||||
{
|
||||
auto length = 0UL;
|
||||
for (auto p = s; *p; ++p)
|
||||
++length;
|
||||
return length;
|
||||
}
|
||||
|
||||
static_assert(strlen_c("") == 0UL, "");
|
||||
static_assert(strlen_c("x") == 1UL, "");
|
||||
static_assert(strlen_c("test") == 4UL, "");
|
||||
static_assert(strlen_c("another\0test") == 7UL, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_lambda_init_capture
|
||||
{
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
const auto lambda1 = [a = x](int b){ return a + b; };
|
||||
const auto lambda2 = [a = lambda1(x)](){ return a; };
|
||||
return lambda2();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace test_digit_seperators
|
||||
{
|
||||
|
||||
constexpr auto ten_million = 100'000'000;
|
||||
static_assert(ten_million == 100000000, "");
|
||||
|
||||
}
|
||||
|
||||
namespace test_return_type_deduction
|
||||
{
|
||||
|
||||
auto f(int& x) { return x; }
|
||||
decltype(auto) g(int& x) { return x; }
|
||||
|
||||
template < typename T1, typename T2 >
|
||||
struct is_same
|
||||
{
|
||||
static constexpr auto value = false;
|
||||
};
|
||||
|
||||
template < typename T >
|
||||
struct is_same<T, T>
|
||||
{
|
||||
static constexpr auto value = true;
|
||||
};
|
||||
|
||||
int
|
||||
test()
|
||||
{
|
||||
auto x = 0;
|
||||
static_assert(is_same<int, decltype(f(x))>::value, "");
|
||||
static_assert(is_same<int&, decltype(g(x))>::value, "");
|
||||
return x;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace cxx14
|
||||
|
||||
#endif // __cplusplus >= 201402L
|
||||
|
||||
]])
|
@ -45,7 +45,7 @@
|
||||
#import "client/mac/handler/protected_memory_allocator.h"
|
||||
#import "common/simple_string_dictionary.h"
|
||||
|
||||
#ifndef __EXCEPTIONS
|
||||
#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions))
|
||||
// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
|
||||
// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
|
||||
// exceptions disabled even when other C++ libraries are used. #undef the try
|
||||
@ -263,9 +263,9 @@ void Breakpad::UncaughtExceptionHandler(NSException *exception) {
|
||||
NSSetUncaughtExceptionHandler(NULL);
|
||||
if (current_breakpad_) {
|
||||
current_breakpad_->HandleUncaughtException(exception);
|
||||
}
|
||||
BreakpadRelease(current_breakpad_);
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
#pragma mark -
|
||||
@ -341,7 +341,7 @@ bool Breakpad::ExtractParameters(NSDictionary *parameters) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!version)
|
||||
if (!version.length) // Default nil or empty string to CFBundleVersion
|
||||
version = [parameters objectForKey:@"CFBundleVersion"];
|
||||
|
||||
if (!vendor) {
|
||||
|
@ -123,6 +123,9 @@ CrashGenerationServer::Stop()
|
||||
void* dummy;
|
||||
pthread_join(thread_, &dummy);
|
||||
|
||||
close(control_pipe_in_);
|
||||
close(control_pipe_out_);
|
||||
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
|
@ -1,154 +0,0 @@
|
||||
// Copyright (c) 2014, 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 "client/linux/dump_writer_common/seccomp_unwinder.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
void SeccompUnwinder::PopSeccompStackFrame(RawContextCPU* cpu,
|
||||
const MDRawThread& thread,
|
||||
uint8_t* stack_copy) {
|
||||
#if defined(__x86_64)
|
||||
uint64_t bp = cpu->rbp;
|
||||
uint64_t top = thread.stack.start_of_memory_range;
|
||||
for (int i = 4; i--; ) {
|
||||
if (bp < top ||
|
||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||
thread.stack.memory.data_size ||
|
||||
bp & 1) {
|
||||
break;
|
||||
}
|
||||
uint64_t old_top = top;
|
||||
top = bp;
|
||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
||||
if (bp == 0xDEADBEEFDEADBEEFull) {
|
||||
struct {
|
||||
uint64_t r15;
|
||||
uint64_t r14;
|
||||
uint64_t r13;
|
||||
uint64_t r12;
|
||||
uint64_t r11;
|
||||
uint64_t r10;
|
||||
uint64_t r9;
|
||||
uint64_t r8;
|
||||
uint64_t rdi;
|
||||
uint64_t rsi;
|
||||
uint64_t rdx;
|
||||
uint64_t rcx;
|
||||
uint64_t rbx;
|
||||
uint64_t deadbeef;
|
||||
uint64_t rbp;
|
||||
uint64_t fakeret;
|
||||
uint64_t ret;
|
||||
/* char redzone[128]; */
|
||||
} seccomp_stackframe;
|
||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||
sizeof(seccomp_stackframe) >
|
||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||
break;
|
||||
}
|
||||
my_memcpy(&seccomp_stackframe,
|
||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||
sizeof(seccomp_stackframe));
|
||||
cpu->rbx = seccomp_stackframe.rbx;
|
||||
cpu->rcx = seccomp_stackframe.rcx;
|
||||
cpu->rdx = seccomp_stackframe.rdx;
|
||||
cpu->rsi = seccomp_stackframe.rsi;
|
||||
cpu->rdi = seccomp_stackframe.rdi;
|
||||
cpu->rbp = seccomp_stackframe.rbp;
|
||||
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
|
||||
cpu->r8 = seccomp_stackframe.r8;
|
||||
cpu->r9 = seccomp_stackframe.r9;
|
||||
cpu->r10 = seccomp_stackframe.r10;
|
||||
cpu->r11 = seccomp_stackframe.r11;
|
||||
cpu->r12 = seccomp_stackframe.r12;
|
||||
cpu->r13 = seccomp_stackframe.r13;
|
||||
cpu->r14 = seccomp_stackframe.r14;
|
||||
cpu->r15 = seccomp_stackframe.r15;
|
||||
cpu->rip = seccomp_stackframe.fakeret;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#elif defined(__i386__)
|
||||
uint32_t bp = cpu->ebp;
|
||||
uint32_t top = thread.stack.start_of_memory_range;
|
||||
for (int i = 4; i--; ) {
|
||||
if (bp < top ||
|
||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||
thread.stack.memory.data_size ||
|
||||
bp & 1) {
|
||||
break;
|
||||
}
|
||||
uint32_t old_top = top;
|
||||
top = bp;
|
||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
||||
if (bp == 0xDEADBEEFu) {
|
||||
struct {
|
||||
uint32_t edi;
|
||||
uint32_t esi;
|
||||
uint32_t edx;
|
||||
uint32_t ecx;
|
||||
uint32_t ebx;
|
||||
uint32_t deadbeef;
|
||||
uint32_t ebp;
|
||||
uint32_t fakeret;
|
||||
uint32_t ret;
|
||||
} seccomp_stackframe;
|
||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||
sizeof(seccomp_stackframe) >
|
||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||
break;
|
||||
}
|
||||
my_memcpy(&seccomp_stackframe,
|
||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||
sizeof(seccomp_stackframe));
|
||||
cpu->ebx = seccomp_stackframe.ebx;
|
||||
cpu->ecx = seccomp_stackframe.ecx;
|
||||
cpu->edx = seccomp_stackframe.edx;
|
||||
cpu->esi = seccomp_stackframe.esi;
|
||||
cpu->edi = seccomp_stackframe.edi;
|
||||
cpu->ebp = seccomp_stackframe.ebp;
|
||||
cpu->esp = top + 4*sizeof(void*);
|
||||
cpu->eip = seccomp_stackframe.fakeret;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
@ -30,6 +30,7 @@
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
@ -178,12 +179,8 @@ void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->flt_save.data_offset = fpregs.rdp;
|
||||
out->flt_save.data_selector = 0; // We don't have this.
|
||||
out->flt_save.mx_csr = fpregs.mxcsr;
|
||||
#if defined (__ANDROID__)
|
||||
// Internal bug b/18097559
|
||||
out->flt_save.mx_csr_mask = fpregs.mxcsr_mask;
|
||||
#else
|
||||
out->flt_save.mx_csr_mask = fpregs.mxcr_mask;
|
||||
#endif
|
||||
|
||||
my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16);
|
||||
my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16);
|
||||
}
|
||||
@ -234,35 +231,69 @@ void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
#elif defined(__mips__)
|
||||
|
||||
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||
return regs.epc;
|
||||
return mcontext.pc;
|
||||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||
out->iregs[i] = regs.regs[i];
|
||||
out->iregs[i] = mcontext.gregs[i];
|
||||
|
||||
out->mdhi = regs.hi;
|
||||
out->mdlo = regs.lo;
|
||||
out->mdhi = mcontext.mdhi;
|
||||
out->mdlo = mcontext.mdlo;
|
||||
out->dsp_control = mcontext.dsp;
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i) {
|
||||
out->hi[i] = hi[i];
|
||||
out->lo[i] = lo[i];
|
||||
}
|
||||
out->dsp_control = dsp_control;
|
||||
out->hi[0] = mcontext.hi1;
|
||||
out->lo[0] = mcontext.lo1;
|
||||
out->hi[1] = mcontext.hi2;
|
||||
out->lo[1] = mcontext.lo2;
|
||||
out->hi[2] = mcontext.hi3;
|
||||
out->lo[2] = mcontext.lo3;
|
||||
|
||||
out->epc = regs.epc;
|
||||
out->badvaddr = regs.badvaddr;
|
||||
out->status = regs.status;
|
||||
out->cause = regs.cause;
|
||||
out->epc = mcontext.pc;
|
||||
out->badvaddr = 0; // Not stored in mcontext
|
||||
out->status = 0; // Not stored in mcontext
|
||||
out->cause = 0; // Not stored in mcontext
|
||||
|
||||
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
||||
out->float_save.regs[i] = fpregs.regs[i];
|
||||
out->float_save.regs[i] = mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs;
|
||||
|
||||
out->float_save.fpcsr = fpregs.fpcsr;
|
||||
out->float_save.fir = fpregs.fir;
|
||||
}
|
||||
out->float_save.fpcsr = mcontext.fpc_csr;
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
out->float_save.fir = mcontext.fpc_eir;
|
||||
#endif
|
||||
}
|
||||
#endif // __mips__
|
||||
|
||||
void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) {
|
||||
assert(gp_regs || size);
|
||||
#if defined(__mips__)
|
||||
if (gp_regs)
|
||||
*gp_regs = mcontext.gregs;
|
||||
if (size)
|
||||
*size = sizeof(mcontext.gregs);
|
||||
#else
|
||||
if (gp_regs)
|
||||
*gp_regs = ®s;
|
||||
if (size)
|
||||
*size = sizeof(regs);
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) {
|
||||
assert(fp_regs || size);
|
||||
#if defined(__mips__)
|
||||
if (fp_regs)
|
||||
*fp_regs = &mcontext.fpregs;
|
||||
if (size)
|
||||
*size = sizeof(mcontext.fpregs);
|
||||
#else
|
||||
if (fp_regs)
|
||||
*fp_regs = &fpregs;
|
||||
if (size)
|
||||
*size = sizeof(fpregs);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -40,7 +40,7 @@
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
#endif
|
||||
|
||||
// We produce one of these structures for each thread in the crashed process.
|
||||
@ -65,15 +65,12 @@ struct ThreadInfo {
|
||||
struct user_regs regs;
|
||||
struct user_fpregs fpregs;
|
||||
#elif defined(__aarch64__)
|
||||
// Use the structures defined in <asm/ptrace.h>
|
||||
struct user_pt_regs regs;
|
||||
struct user_fpsimd_state fpregs;
|
||||
// Use the structures defined in <sys/user.h>
|
||||
struct user_regs_struct regs;
|
||||
struct user_fpsimd_struct fpregs;
|
||||
#elif defined(__mips__)
|
||||
user_regs_struct regs;
|
||||
user_fpregs_struct fpregs;
|
||||
uint32_t hi[3];
|
||||
uint32_t lo[3];
|
||||
uint32_t dsp_control;
|
||||
// Use the structure defined in <sys/ucontext.h>.
|
||||
mcontext_t mcontext;
|
||||
#endif
|
||||
|
||||
// Returns the instruction pointer (platform-dependent impl.).
|
||||
@ -81,6 +78,12 @@ struct ThreadInfo {
|
||||
|
||||
// Fills a RawContextCPU using the context in the ThreadInfo object.
|
||||
void FillCPUContext(RawContextCPU* out) const;
|
||||
|
||||
// Returns the pointer and size of general purpose register area.
|
||||
void GetGeneralPurposeRegisters(void** gp_regs, size_t* size);
|
||||
|
||||
// Returns the pointer and size of float point register area.
|
||||
void GetFloatingPointRegisters(void** fp_regs, size_t* size);
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -244,7 +244,9 @@ void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i];
|
||||
|
||||
out->float_save.fpcsr = uc->uc_mcontext.fpc_csr;
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -188,12 +188,36 @@ void RestoreAlternateStackLocked() {
|
||||
stack_installed = false;
|
||||
}
|
||||
|
||||
void InstallDefaultHandler(int sig) {
|
||||
#if defined(__ANDROID__)
|
||||
// Android L+ expose signal and sigaction symbols that override the system
|
||||
// ones. There is a bug in these functions where a request to set the handler
|
||||
// to SIG_DFL is ignored. In that case, an infinite loop is entered as the
|
||||
// signal is repeatedly sent to breakpad's signal handler.
|
||||
// To work around this, directly call the system's sigaction.
|
||||
struct kernel_sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sys_sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler_ = SIG_DFL;
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t));
|
||||
#else
|
||||
signal(sig, SIG_DFL);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The global exception handler stack. This is needed because there may exist
|
||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||
// registered in this stack.
|
||||
std::vector<ExceptionHandler*>* g_handler_stack_ = NULL;
|
||||
pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// sizeof(CrashContext) can be too big w.r.t the size of alternatate stack
|
||||
// for SignalHandler(). Keep the crash context as a .bss field. Exception
|
||||
// handlers are serialized by the |g_handler_stack_mutex_| and at most one at a
|
||||
// time can use |g_crash_context_|.
|
||||
ExceptionHandler::CrashContext g_crash_context_;
|
||||
|
||||
} // namespace
|
||||
|
||||
// Runs before crashing: normal context.
|
||||
@ -215,7 +239,17 @@ ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||
!minidump_descriptor_.IsMicrodumpOnConsole())
|
||||
minidump_descriptor_.UpdatePath();
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole())
|
||||
logger::initializeCrashLogWriter();
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||
|
||||
// Pre-fault the crash context struct. This is to avoid failing due to OOM
|
||||
// if handling an exception when the process ran out of virtual memory.
|
||||
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
|
||||
|
||||
if (!g_handler_stack_)
|
||||
g_handler_stack_ = new std::vector<ExceptionHandler*>;
|
||||
if (install_handler) {
|
||||
@ -283,7 +317,7 @@ void ExceptionHandler::RestoreHandlersLocked() {
|
||||
|
||||
for (int i = 0; i < kNumHandledSignals; ++i) {
|
||||
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
|
||||
signal(kExceptionSignals[i], SIG_DFL);
|
||||
InstallDefaultHandler(kExceptionSignals[i]);
|
||||
}
|
||||
}
|
||||
handlers_installed = false;
|
||||
@ -323,7 +357,7 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
if (sigaction(sig, &cur_handler, NULL) == -1) {
|
||||
// When resetting the handler fails, try to reset the
|
||||
// default one to avoid an infinite loop here.
|
||||
signal(sig, SIG_DFL);
|
||||
InstallDefaultHandler(sig);
|
||||
}
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
return;
|
||||
@ -340,14 +374,15 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||
// previously installed handler. Then, when the signal is retriggered, it will
|
||||
// be delivered to the appropriate handler.
|
||||
if (handled) {
|
||||
signal(sig, SIG_DFL);
|
||||
InstallDefaultHandler(sig);
|
||||
} else {
|
||||
RestoreHandlersLocked();
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||
|
||||
if (info->si_pid || sig == SIGABRT) {
|
||||
// info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise).
|
||||
if (info->si_code <= 0 || sig == SIGABRT) {
|
||||
// This signal was triggered by somebody sending us the signal with kill().
|
||||
// In order to retrigger it, we have to queue a new signal by calling
|
||||
// kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
|
||||
@ -400,17 +435,18 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
|
||||
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||
}
|
||||
CrashContext context;
|
||||
|
||||
// Fill in all the holes in the struct to make Valgrind happy.
|
||||
memset(&context, 0, sizeof(context));
|
||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
||||
memset(&g_crash_context_, 0, sizeof(g_crash_context_));
|
||||
memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&g_crash_context_.context, uc, sizeof(struct ucontext));
|
||||
#if defined(__aarch64__)
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
struct fpsimd_context* fp_ptr =
|
||||
(struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
|
||||
if (fp_ptr->head.magic == FPSIMD_MAGIC) {
|
||||
memcpy(&context.float_state, fp_ptr, sizeof(context.float_state));
|
||||
memcpy(&g_crash_context_.float_state, fp_ptr,
|
||||
sizeof(g_crash_context_.float_state));
|
||||
}
|
||||
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
// FP state is not part of user ABI on ARM Linux.
|
||||
@ -418,18 +454,18 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
// and 'float_state' is not a member of CrashContext.
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
if (uc_ptr->uc_mcontext.fpregs) {
|
||||
memcpy(&context.float_state,
|
||||
uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(g_crash_context_.float_state));
|
||||
}
|
||||
#endif
|
||||
context.tid = syscall(__NR_gettid);
|
||||
g_crash_context_.tid = syscall(__NR_gettid);
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&context, sizeof(context), callback_context_)) {
|
||||
if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_),
|
||||
callback_context_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GenerateDump(&context);
|
||||
return GenerateDump(&g_crash_context_);
|
||||
}
|
||||
|
||||
// This is a public interface to HandleSignal that allows the client to
|
||||
@ -551,10 +587,12 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size) {
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||
return google_breakpad::WriteMicrodump(crashing_process,
|
||||
return google_breakpad::WriteMicrodump(
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_);
|
||||
mapping_list_,
|
||||
*minidump_descriptor_.microdump_extra_info());
|
||||
}
|
||||
if (minidump_descriptor_.IsFD()) {
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
|
||||
|
@ -80,7 +80,11 @@ void FlushInstructionCache(const char* memory, uint32_t memory_size) {
|
||||
// Provided by Android's <unistd.h>
|
||||
long begin = reinterpret_cast<long>(memory);
|
||||
long end = begin + static_cast<long>(memory_size);
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
cacheflush(begin, end, 0);
|
||||
#else
|
||||
syscall(__NR_cacheflush, begin, end, ICACHE);
|
||||
#endif
|
||||
# elif defined(__linux__)
|
||||
// See http://www.linux-mips.org/wiki/Cacheflush_Syscall.
|
||||
cacheflush(const_cast<char*>(memory), memory_size, ICACHE);
|
||||
@ -258,8 +262,6 @@ TEST(ExceptionHandlerTest, ChildCrashWithFD) {
|
||||
ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
|
||||
}
|
||||
|
||||
#endif // !ADDRESS_SANITIZER
|
||||
|
||||
static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
|
||||
void* context,
|
||||
bool succeeded) {
|
||||
@ -301,8 +303,6 @@ static bool InstallRaiseSIGKILL() {
|
||||
return sigaction(SIGSEGV, &sa, NULL) != -1;
|
||||
}
|
||||
|
||||
#ifndef ADDRESS_SANITIZER
|
||||
|
||||
static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter,
|
||||
ExceptionHandler::MinidumpCallback done,
|
||||
string path) {
|
||||
@ -770,8 +770,13 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
|
||||
true, -1);
|
||||
// Try calling a NULL pointer.
|
||||
typedef void (*void_function)(void);
|
||||
void_function memory_function = reinterpret_cast<void_function>(NULL);
|
||||
// Volatile markings are needed to keep Clang from generating invalid
|
||||
// opcodes. See http://crbug.com/498354 for details.
|
||||
volatile void_function memory_function =
|
||||
reinterpret_cast<void_function>(NULL);
|
||||
memory_function();
|
||||
// not reached
|
||||
exit(1);
|
||||
}
|
||||
close(fds[1]);
|
||||
|
||||
@ -869,6 +874,8 @@ TEST(ExceptionHandlerTest, ModuleInfo) {
|
||||
unlink(minidump_desc.path());
|
||||
}
|
||||
|
||||
#ifndef ADDRESS_SANITIZER
|
||||
|
||||
static const unsigned kControlMsgSize =
|
||||
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
|
||||
|
||||
@ -921,8 +928,6 @@ CrashHandler(const void* crash_context, size_t crash_context_size,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef ADDRESS_SANITIZER
|
||||
|
||||
TEST(ExceptionHandlerTest, ExternalDumper) {
|
||||
int fds[2];
|
||||
ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
|
||||
@ -955,7 +960,7 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
|
||||
const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
|
||||
ASSERT_EQ(static_cast<ssize_t>(kCrashContextSize), n);
|
||||
ASSERT_EQ(kControlMsgSize, msg.msg_controllen);
|
||||
ASSERT_EQ(static_cast<typeof(msg.msg_flags)>(0), msg.msg_flags);
|
||||
ASSERT_EQ(static_cast<__typeof__(msg.msg_flags)>(0), msg.msg_flags);
|
||||
ASSERT_EQ(0, close(fds[0]));
|
||||
|
||||
pid_t crashing_pid = -1;
|
||||
|
48
src/client/linux/handler/microdump_extra_info.h
Normal file
48
src/client/linux/handler/microdump_extra_info.h
Normal file
@ -0,0 +1,48 @@
|
||||
// Copyright 2015 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_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
||||
#define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct MicrodumpExtraInfo {
|
||||
// Strings pointed to by this struct are not copied, and are
|
||||
// expected to remain valid for the lifetime of the process.
|
||||
const char* build_fingerprint;
|
||||
const char* product_info;
|
||||
const char* gpu_fingerprint;
|
||||
|
||||
MicrodumpExtraInfo()
|
||||
: build_fingerprint(NULL), product_info(NULL), gpu_fingerprint(NULL) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_
|
@ -36,14 +36,16 @@
|
||||
namespace google_breakpad {
|
||||
|
||||
//static
|
||||
const MinidumpDescriptor::MicrodumpOnConsole kMicrodumpOnConsole = {};
|
||||
const MinidumpDescriptor::MicrodumpOnConsole
|
||||
MinidumpDescriptor::kMicrodumpOnConsole = {};
|
||||
|
||||
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
||||
: mode_(descriptor.mode_),
|
||||
fd_(descriptor.fd_),
|
||||
directory_(descriptor.directory_),
|
||||
c_path_(NULL),
|
||||
size_limit_(descriptor.size_limit_) {
|
||||
size_limit_(descriptor.size_limit_),
|
||||
microdump_extra_info_(descriptor.microdump_extra_info_) {
|
||||
// The copy constructor is not allowed to be called on a MinidumpDescriptor
|
||||
// with a valid path_, as getting its c_path_ would require the heap which
|
||||
// can cause problems in compromised environments.
|
||||
@ -64,6 +66,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=(
|
||||
UpdatePath();
|
||||
}
|
||||
size_limit_ = descriptor.size_limit_;
|
||||
microdump_extra_info_ = descriptor.microdump_extra_info_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
// This class describes how a crash dump should be generated, either:
|
||||
@ -49,7 +50,8 @@ class MinidumpDescriptor {
|
||||
struct MicrodumpOnConsole {};
|
||||
static const MicrodumpOnConsole kMicrodumpOnConsole;
|
||||
|
||||
MinidumpDescriptor() : mode_(kUninitialized),
|
||||
MinidumpDescriptor()
|
||||
: mode_(kUninitialized),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
|
||||
@ -99,6 +101,11 @@ class MinidumpDescriptor {
|
||||
off_t size_limit() const { return size_limit_; }
|
||||
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
||||
|
||||
MicrodumpExtraInfo* microdump_extra_info() {
|
||||
assert(IsMicrodumpOnConsole());
|
||||
return µdump_extra_info_;
|
||||
};
|
||||
|
||||
private:
|
||||
enum DumpMode {
|
||||
kUninitialized = 0,
|
||||
@ -115,13 +122,26 @@ class MinidumpDescriptor {
|
||||
|
||||
// The directory where the minidump should be generated.
|
||||
string directory_;
|
||||
|
||||
// The full path to the generated minidump.
|
||||
string path_;
|
||||
|
||||
// The C string of |path_|. Precomputed so it can be access from a compromised
|
||||
// context.
|
||||
const char* c_path_;
|
||||
|
||||
off_t size_limit_;
|
||||
|
||||
// The extra microdump data (e.g. product name/version, build
|
||||
// fingerprint, gpu fingerprint) that should be appended to the dump
|
||||
// (microdump only). Microdumps don't have the ability of appending
|
||||
// extra metadata after the dump is generated (as opposite to
|
||||
// minidumps MIME fields), therefore the extra data must be provided
|
||||
// upfront. Any memory pointed to by members of the
|
||||
// MicrodumpExtraInfo struct must be valid for the lifetime of the
|
||||
// process (read: the caller has to guarantee that it is stored in
|
||||
// global static storage.)
|
||||
MicrodumpExtraInfo microdump_extra_info_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -31,15 +31,51 @@
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/log.h>
|
||||
#include <dlfcn.h>
|
||||
#else
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#endif
|
||||
|
||||
namespace logger {
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
namespace {
|
||||
|
||||
// __android_log_buf_write() is not exported in the NDK and is being used by
|
||||
// dynamic runtime linking. Its declaration is taken from Android's
|
||||
// system/core/include/log/log.h.
|
||||
using AndroidLogBufferWriteFunc = int (*)(int bufID, int prio, const char *tag,
|
||||
const char *text);
|
||||
const int kAndroidCrashLogId = 4; // From LOG_ID_CRASH in log.h.
|
||||
const char kAndroidLogTag[] = "google-breakpad";
|
||||
|
||||
bool g_crash_log_initialized = false;
|
||||
AndroidLogBufferWriteFunc g_android_log_buf_write = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
void initializeCrashLogWriter() {
|
||||
if (g_crash_log_initialized)
|
||||
return;
|
||||
g_android_log_buf_write = reinterpret_cast<AndroidLogBufferWriteFunc>(
|
||||
dlsym(RTLD_DEFAULT, "__android_log_buf_write"));
|
||||
g_crash_log_initialized = true;
|
||||
}
|
||||
|
||||
int writeToCrashLog(const char* buf) {
|
||||
// Try writing to the crash log ring buffer. If not available, fall back to
|
||||
// the standard log buffer.
|
||||
if (g_android_log_buf_write) {
|
||||
return g_android_log_buf_write(kAndroidCrashLogId, ANDROID_LOG_FATAL,
|
||||
kAndroidLogTag, buf);
|
||||
}
|
||||
return __android_log_write(ANDROID_LOG_FATAL, kAndroidLogTag, buf);
|
||||
}
|
||||
#endif
|
||||
|
||||
int write(const char* buf, size_t nbytes) {
|
||||
#if defined(__ANDROID__)
|
||||
return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf);
|
||||
return __android_log_write(ANDROID_LOG_WARN, kAndroidLogTag, buf);
|
||||
#else
|
||||
return sys_write(2, buf, nbytes);
|
||||
#endif
|
||||
|
@ -36,6 +36,20 @@ namespace logger {
|
||||
|
||||
int write(const char* buf, size_t nbytes);
|
||||
|
||||
// In the case of Android the log can be written to the default system log
|
||||
// (default behavior of write() above, or to the crash log (see
|
||||
// writeToCrashLog() below).
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
// The logger must be initialized in a non-compromised context.
|
||||
void initializeCrashLogWriter();
|
||||
|
||||
// Once initialized, writeToCrashLog is safe to use in a compromised context,
|
||||
// even if the initialization failed, in which case this will silently fall
|
||||
// back on write().
|
||||
int writeToCrashLog(const char* buf);
|
||||
#endif
|
||||
|
||||
} // namespace logger
|
||||
|
||||
#endif // CLIENT_LINUX_LOG_LOG_H_
|
||||
|
@ -34,13 +34,13 @@
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/seccomp_unwinder.h"
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "client/linux/log/log.h"
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "client/linux/log/log.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@ -49,36 +49,50 @@ using google_breakpad::LinuxDumper;
|
||||
using google_breakpad::LinuxPtraceDumper;
|
||||
using google_breakpad::MappingInfo;
|
||||
using google_breakpad::MappingList;
|
||||
using google_breakpad::MicrodumpExtraInfo;
|
||||
using google_breakpad::RawContextCPU;
|
||||
using google_breakpad::SeccompUnwinder;
|
||||
using google_breakpad::ThreadInfo;
|
||||
using google_breakpad::UContextReader;
|
||||
|
||||
const size_t kLineBufferSize = 2048;
|
||||
|
||||
class MicrodumpWriter {
|
||||
public:
|
||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
LinuxDumper* dumper)
|
||||
: ucontext_(context ? &context->context : NULL),
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
float_state_(context ? &context->float_state : NULL),
|
||||
#endif
|
||||
dumper_(dumper),
|
||||
mapping_list_(mappings) { }
|
||||
mapping_list_(mappings),
|
||||
microdump_extra_info_(microdump_extra_info),
|
||||
log_line_(NULL) {
|
||||
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
|
||||
if (log_line_)
|
||||
log_line_[0] = '\0'; // Clear out the log line buffer.
|
||||
}
|
||||
|
||||
~MicrodumpWriter() { dumper_->ThreadsResume(); }
|
||||
|
||||
bool Init() {
|
||||
if (!dumper_->Init())
|
||||
// In the exceptional case where the system was out of memory and there
|
||||
// wasn't even room to allocate the line buffer, bail out. There is nothing
|
||||
// useful we can possibly achieve without the ability to Log. At least let's
|
||||
// try to not crash.
|
||||
if (!dumper_->Init() || !log_line_)
|
||||
return false;
|
||||
return dumper_->ThreadsSuspend();
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
bool success;
|
||||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||
success = DumpOSInformation();
|
||||
if (success)
|
||||
DumpProductInformation();
|
||||
DumpOSInformation();
|
||||
DumpGPUInformation();
|
||||
success = DumpCrashingThread();
|
||||
if (success)
|
||||
success = DumpMappings();
|
||||
@ -90,12 +104,17 @@ class MicrodumpWriter {
|
||||
private:
|
||||
// Writes one line to the system log.
|
||||
void LogLine(const char* msg) {
|
||||
#if defined(__ANDROID__)
|
||||
logger::writeToCrashLog(msg);
|
||||
#else
|
||||
logger::write(msg, my_strlen(msg));
|
||||
logger::write("\n", 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Stages the given string in the current line buffer.
|
||||
void LogAppend(const char* str) {
|
||||
my_strlcat(log_line_, str, sizeof(log_line_));
|
||||
my_strlcat(log_line_, str, kLineBufferSize);
|
||||
}
|
||||
|
||||
// As above (required to take precedence over template specialization below).
|
||||
@ -125,14 +144,22 @@ class MicrodumpWriter {
|
||||
|
||||
// Writes out the current line buffer on the system log.
|
||||
void LogCommitLine() {
|
||||
logger::write(log_line_, my_strlen(log_line_));
|
||||
my_strlcpy(log_line_, "", sizeof(log_line_));
|
||||
LogLine(log_line_);
|
||||
my_strlcpy(log_line_, "", kLineBufferSize);
|
||||
}
|
||||
|
||||
bool DumpOSInformation() {
|
||||
struct utsname uts;
|
||||
if (uname(&uts))
|
||||
return false;
|
||||
void DumpProductInformation() {
|
||||
LogAppend("V ");
|
||||
if (microdump_extra_info_.product_info) {
|
||||
LogAppend(microdump_extra_info_.product_info);
|
||||
} else {
|
||||
LogAppend("UNKNOWN:0.0.0.0");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpOSInformation() {
|
||||
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
const char kOSId[] = "A";
|
||||
@ -140,17 +167,60 @@ class MicrodumpWriter {
|
||||
const char kOSId[] = "L";
|
||||
#endif
|
||||
|
||||
// Dump the runtime architecture. On multiarch devices it might not match the
|
||||
// hw architecture (the one returned by uname()), for instance in the case of
|
||||
// a 32-bit app running on a aarch64 device.
|
||||
#if defined(__aarch64__)
|
||||
const char kArch[] = "arm64";
|
||||
#elif defined(__ARMEL__)
|
||||
const char kArch[] = "arm";
|
||||
#elif defined(__x86_64__)
|
||||
const char kArch[] = "x86_64";
|
||||
#elif defined(__i386__)
|
||||
const char kArch[] = "x86";
|
||||
#elif defined(__mips__)
|
||||
const char kArch[] = "mips";
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet"
|
||||
#endif
|
||||
|
||||
LogAppend("O ");
|
||||
LogAppend(kOSId);
|
||||
LogAppend(" \"");
|
||||
LogAppend(uts.machine);
|
||||
LogAppend("\" \"");
|
||||
LogAppend(" ");
|
||||
LogAppend(kArch);
|
||||
LogAppend(" ");
|
||||
LogAppend(n_cpus);
|
||||
LogAppend(" ");
|
||||
|
||||
// Dump the HW architecture (e.g., armv7l, aarch64).
|
||||
struct utsname uts;
|
||||
const bool has_uts_info = (uname(&uts) == 0);
|
||||
const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch";
|
||||
LogAppend(hwArch);
|
||||
LogAppend(" ");
|
||||
|
||||
// If the client has attached a build fingerprint to the MinidumpDescriptor
|
||||
// use that one. Otherwise try to get some basic info from uname().
|
||||
if (microdump_extra_info_.build_fingerprint) {
|
||||
LogAppend(microdump_extra_info_.build_fingerprint);
|
||||
} else if (has_uts_info) {
|
||||
LogAppend(uts.release);
|
||||
LogAppend(" \"");
|
||||
LogAppend(" ");
|
||||
LogAppend(uts.version);
|
||||
LogAppend("\"");
|
||||
} else {
|
||||
LogAppend("no build fingerprint available");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpGPUInformation() {
|
||||
LogAppend("G ");
|
||||
if (microdump_extra_info_.gpu_fingerprint) {
|
||||
LogAppend(microdump_extra_info_.gpu_fingerprint);
|
||||
} else {
|
||||
LogAppend("UNKNOWN");
|
||||
}
|
||||
LogCommitLine();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpThreadStack(uint32_t thread_id,
|
||||
@ -162,8 +232,9 @@ class MicrodumpWriter {
|
||||
size_t stack_len;
|
||||
|
||||
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||
assert(false);
|
||||
return false;
|
||||
// The stack pointer might not be available. In this case we don't hard
|
||||
// fail, just produce a (almost useless) microdump w/o a stack section.
|
||||
return true;
|
||||
}
|
||||
|
||||
LogAppend("S 0 ");
|
||||
@ -225,8 +296,6 @@ class MicrodumpWriter {
|
||||
#else
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||
#endif
|
||||
if (stack_copy)
|
||||
SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy);
|
||||
DumpCPUState(&cpu);
|
||||
}
|
||||
return true;
|
||||
@ -296,7 +365,7 @@ class MicrodumpWriter {
|
||||
LogAppend(module_identifier.data4[5]);
|
||||
LogAppend(module_identifier.data4[6]);
|
||||
LogAppend(module_identifier.data4[7]);
|
||||
LogAppend(" ");
|
||||
LogAppend("0 "); // Age is always 0 on Linux.
|
||||
LogAppend(file_name);
|
||||
LogCommitLine();
|
||||
}
|
||||
@ -306,15 +375,13 @@ class MicrodumpWriter {
|
||||
// First write all the mappings from the dumper
|
||||
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
|
||||
const MappingInfo& mapping = *dumper_->mappings()[i];
|
||||
// Skip mappings which don't look like libraries.
|
||||
if (!strstr(mapping.name, ".so") || // dump only libs (skip fonts, apks).
|
||||
mapping.size < 4096) { // too small to get a signature for.
|
||||
if (mapping.name[0] == 0 || // only want modules with filenames.
|
||||
!mapping.exec || // only want executable mappings.
|
||||
mapping.size < 4096 || // too small to get a signature for.
|
||||
HaveMappingInfo(mapping)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (HaveMappingInfo(mapping))
|
||||
continue;
|
||||
|
||||
DumpModule(mapping, true, i, NULL);
|
||||
}
|
||||
// Next write all the mappings provided by the caller
|
||||
@ -334,7 +401,8 @@ class MicrodumpWriter {
|
||||
#endif
|
||||
LinuxDumper* dumper_;
|
||||
const MappingList& mapping_list_;
|
||||
char log_line_[512];
|
||||
const MicrodumpExtraInfo microdump_extra_info_;
|
||||
char* log_line_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -343,7 +411,8 @@ namespace google_breakpad {
|
||||
bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings) {
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info) {
|
||||
LinuxPtraceDumper dumper(crashing_process);
|
||||
const ExceptionHandler::CrashContext* context = NULL;
|
||||
if (blob) {
|
||||
@ -355,7 +424,7 @@ bool WriteMicrodump(pid_t crashing_process,
|
||||
dumper.set_crash_signal(context->siginfo.si_signo);
|
||||
dumper.set_crash_thread(context->tid);
|
||||
}
|
||||
MicrodumpWriter writer(context, mappings, &dumper);
|
||||
MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
|
@ -37,6 +37,8 @@
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct MicrodumpExtraInfo;
|
||||
|
||||
// Writes a microdump (a reduced dump containing only the state of the crashing
|
||||
// thread) on the console (logcat on Android). These functions do not malloc nor
|
||||
// use libc functions which may. Thus, it can be used in contexts where the
|
||||
@ -46,12 +48,17 @@ namespace google_breakpad {
|
||||
// blob: a blob of data from the crashing process. See exception_handler.h
|
||||
// blob_size: the length of |blob| in bytes.
|
||||
// mappings: a list of additional mappings provided by the application.
|
||||
// build_fingerprint: a (optional) C string which determines the OS
|
||||
// build fingerprint (e.g., aosp/occam/mako:5.1.1/LMY47W/1234:eng/dev-keys).
|
||||
// product_info: a (optional) C string which determines the product name and
|
||||
// version (e.g., WebView:42.0.2311.136).
|
||||
//
|
||||
// Returns true iff successful.
|
||||
bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings);
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
257
src/client/linux/microdump_writer/microdump_writer_unittest.cc
Normal file
257
src/client/linux/microdump_writer/microdump_writer_unittest.cc
Normal file
@ -0,0 +1,257 @@
|
||||
// Copyright (c) 2014 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 <ctype.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test MicrodumpWriterTest;
|
||||
|
||||
MicrodumpExtraInfo MakeMicrodumpExtraInfo(
|
||||
const char* build_fingerprint,
|
||||
const char* product_info,
|
||||
const char* gpu_fingerprint) {
|
||||
MicrodumpExtraInfo info;
|
||||
info.build_fingerprint = build_fingerprint;
|
||||
info.product_info = product_info;
|
||||
info.gpu_fingerprint = gpu_fingerprint;
|
||||
return info;
|
||||
}
|
||||
|
||||
void CrashAndGetMicrodump(
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
scoped_array<char>* buf) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string stderr_file = temp_dir.path() + "/stderr.log";
|
||||
int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
ASSERT_NE(-1, err_fd);
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
|
||||
// Set a non-zero tid to avoid tripping asserts.
|
||||
context.tid = child;
|
||||
|
||||
// Redirect temporarily stderr to the stderr.log file.
|
||||
int save_err = dup(STDERR_FILENO);
|
||||
ASSERT_NE(-1, save_err);
|
||||
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
|
||||
|
||||
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
|
||||
microdump_extra_info));
|
||||
|
||||
// Revert stderr back to the console.
|
||||
dup2(save_err, STDERR_FILENO);
|
||||
close(save_err);
|
||||
|
||||
// Read back the stderr file and check for the microdump marker.
|
||||
fsync(err_fd);
|
||||
lseek(err_fd, 0, SEEK_SET);
|
||||
const size_t kBufSize = 64 * 1024;
|
||||
buf->reset(new char[kBufSize]);
|
||||
ASSERT_GT(read(err_fd, buf->get(), kBufSize), 0);
|
||||
|
||||
close(err_fd);
|
||||
close(fds[1]);
|
||||
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----BEGIN BREAKPAD MICRODUMP-----"));
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf->get(), "-----END BREAKPAD MICRODUMP-----"));
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
const MicrodumpExtraInfo& expected_info) {
|
||||
std::istringstream iss(microdump_content);
|
||||
bool did_find_os_info = false;
|
||||
bool did_find_product_info = false;
|
||||
bool did_find_gpu_info = false;
|
||||
for (string line; std::getline(iss, line);) {
|
||||
if (line.find("O ") == 0) {
|
||||
std::istringstream os_info_tokens(line);
|
||||
string token;
|
||||
os_info_tokens.ignore(2); // Ignore the "O " preamble.
|
||||
// Check the OS descriptor char (L=Linux, A=Android).
|
||||
os_info_tokens >> token;
|
||||
ASSERT_TRUE(token == "L" || token == "A");
|
||||
|
||||
os_info_tokens >> token; // HW architecture.
|
||||
os_info_tokens >> token; // Number of cpus.
|
||||
for (size_t i = 0; i < token.size(); ++i)
|
||||
ASSERT_TRUE(isxdigit(token[i]));
|
||||
os_info_tokens >> token; // SW architecture.
|
||||
|
||||
// Check that the build fingerprint is in the right place.
|
||||
os_info_tokens >> token;
|
||||
if (expected_info.build_fingerprint)
|
||||
ASSERT_EQ(expected_info.build_fingerprint, token);
|
||||
did_find_os_info = true;
|
||||
} else if (line.find("V ") == 0) {
|
||||
if (expected_info.product_info)
|
||||
ASSERT_EQ(string("V ") + expected_info.product_info, line);
|
||||
did_find_product_info = true;
|
||||
} else if (line.find("G ") == 0) {
|
||||
if (expected_info.gpu_fingerprint)
|
||||
ASSERT_EQ(string("G ") + expected_info.gpu_fingerprint, line);
|
||||
did_find_gpu_info = true;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(did_find_os_info);
|
||||
ASSERT_TRUE(did_find_product_info);
|
||||
ASSERT_TRUE(did_find_gpu_info);
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
const string& expected_fingerprint,
|
||||
const string& expected_product_info,
|
||||
const string& expected_gpu_fingerprint) {
|
||||
CheckMicrodumpContents(
|
||||
microdump_content,
|
||||
MakeMicrodumpExtraInfo(expected_fingerprint.c_str(),
|
||||
expected_product_info.c_str(),
|
||||
expected_gpu_fingerprint.c_str()));
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, BasicWithMappings) {
|
||||
// Push some extra mapping to check the MappingList logic.
|
||||
const uint32_t memory_size = sysconf(_SC_PAGESIZE);
|
||||
const char* kMemoryName = "libfoo.so";
|
||||
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
|
||||
MappingInfo info;
|
||||
info.start_addr = memory_size;
|
||||
info.size = memory_size;
|
||||
info.offset = 42;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
MappingEntry mapping;
|
||||
mapping.first = info;
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
|
||||
scoped_array<char> buf;
|
||||
CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
|
||||
|
||||
#ifdef __LP64__
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#else
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 00001000 0000002A 00001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#endif
|
||||
|
||||
// In absence of a product info in the minidump, the writer should just write
|
||||
// an unknown marker.
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "V UNKNOWN:0.0.0.0"));
|
||||
}
|
||||
|
||||
// Ensure that the product info and build fingerprint metadata show up in the
|
||||
// final microdump if present.
|
||||
TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
|
||||
const char kProductInfo[] = "MockProduct:42.0.2311.99";
|
||||
const char kBuildFingerprint[] =
|
||||
"aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys";
|
||||
const char kGPUFingerprint[] =
|
||||
"Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)";
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint));
|
||||
scoped_array<char> buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kMicrodumpExtraInfo);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoProductInfo) {
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
const char kGPUFingerprint[] = "bazqux";
|
||||
scoped_array<char> buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint));
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
|
||||
"UNKNOWN:0.0.0.0", kGPUFingerprint);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoGPUInfo) {
|
||||
const char kProductInfo[] = "bazqux";
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
scoped_array<char> buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
|
||||
kProductInfo, "UNKNOWN");
|
||||
}
|
||||
} // namespace
|
@ -38,6 +38,10 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/procfs.h>
|
||||
#if defined(__mips__) && defined(__ANDROID__)
|
||||
// To get register definitions.
|
||||
#include <asm/reg.h>
|
||||
#endif
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
|
||||
@ -74,7 +78,7 @@ bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
const void* src, size_t length) {
|
||||
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
|
||||
// TODO(benchan): Investigate whether the data to be copied could span
|
||||
@ -84,7 +88,9 @@ void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
// If the data segment is not found in the core dump, fill the result
|
||||
// with marker characters.
|
||||
memset(dest, 0xab, length);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
@ -103,7 +109,7 @@ bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
||||
#elif defined(__mips__)
|
||||
stack_pointer =
|
||||
reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]);
|
||||
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
@ -189,18 +195,19 @@ bool LinuxCoreDumper::EnumerateThreads() {
|
||||
info.tgid = status->pr_pgrp;
|
||||
info.ppid = status->pr_ppid;
|
||||
#if defined(__mips__)
|
||||
#if defined(__ANDROID__)
|
||||
for (int i = EF_R0; i <= EF_R31; i++)
|
||||
info.mcontext.gregs[i - EF_R0] = status->pr_reg[i];
|
||||
#else // __ANDROID__
|
||||
for (int i = EF_REG0; i <= EF_REG31; i++)
|
||||
info.regs.regs[i - EF_REG0] = status->pr_reg[i];
|
||||
|
||||
info.regs.lo = status->pr_reg[EF_LO];
|
||||
info.regs.hi = status->pr_reg[EF_HI];
|
||||
info.regs.epc = status->pr_reg[EF_CP0_EPC];
|
||||
info.regs.badvaddr = status->pr_reg[EF_CP0_BADVADDR];
|
||||
info.regs.status = status->pr_reg[EF_CP0_STATUS];
|
||||
info.regs.cause = status->pr_reg[EF_CP0_CAUSE];
|
||||
#else
|
||||
info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i];
|
||||
#endif // __ANDROID__
|
||||
info.mcontext.mdlo = status->pr_reg[EF_LO];
|
||||
info.mcontext.mdhi = status->pr_reg[EF_HI];
|
||||
info.mcontext.pc = status->pr_reg[EF_CP0_EPC];
|
||||
#else // __mips__
|
||||
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
|
||||
#endif
|
||||
#endif // __mips__
|
||||
if (first_thread) {
|
||||
crash_thread_ = pid;
|
||||
crash_signal_ = status->pr_info.si_signo;
|
||||
|
@ -68,8 +68,9 @@ class LinuxCoreDumper : public LinuxDumper {
|
||||
// Copies content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|. This method extracts the content
|
||||
// the core dump and fills |dest| with a sequence of marker bytes
|
||||
// if the expected data is not found in the core dump.
|
||||
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
// if the expected data is not found in the core dump. Returns true if
|
||||
// the expected data is found in the core dump.
|
||||
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length);
|
||||
|
||||
// Implements LinuxDumper::GetThreadInfoByIndex().
|
||||
|
@ -52,6 +52,22 @@
|
||||
#include "common/linux/safe_readlink.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
// Android packed relocations definitions are not yet available from the
|
||||
// NDK header files, so we have to provide them manually here.
|
||||
#ifndef DT_LOOS
|
||||
#define DT_LOOS 0x6000000d
|
||||
#endif
|
||||
#ifndef DT_ANDROID_REL
|
||||
static const int DT_ANDROID_REL = DT_LOOS + 2;
|
||||
#endif
|
||||
#ifndef DT_ANDROID_RELA
|
||||
static const int DT_ANDROID_RELA = DT_LOOS + 4;
|
||||
#endif
|
||||
|
||||
#endif // __ANDROID __
|
||||
|
||||
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
||||
static const char kDeletedSuffix[] = " (deleted)";
|
||||
static const char kReservedFlags[] = " ---p";
|
||||
@ -92,6 +108,13 @@ bool LinuxDumper::Init() {
|
||||
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
|
||||
}
|
||||
|
||||
bool LinuxDumper::LateInit() {
|
||||
#if defined(__ANDROID__)
|
||||
LatePostprocessMappings();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
bool member,
|
||||
@ -395,6 +418,113 @@ bool LinuxDumper::EnumerateMappings() {
|
||||
return !mappings_.empty();
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) {
|
||||
CopyFromProcess(ehdr, pid_,
|
||||
reinterpret_cast<const void*>(start_addr),
|
||||
sizeof(*ehdr));
|
||||
return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0;
|
||||
}
|
||||
|
||||
void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
|
||||
uintptr_t start_addr,
|
||||
uintptr_t* min_vaddr_ptr,
|
||||
uintptr_t* dyn_vaddr_ptr,
|
||||
size_t* dyn_count_ptr) {
|
||||
uintptr_t phdr_addr = start_addr + ehdr->e_phoff;
|
||||
|
||||
const uintptr_t max_addr = UINTPTR_MAX;
|
||||
uintptr_t min_vaddr = max_addr;
|
||||
uintptr_t dyn_vaddr = 0;
|
||||
size_t dyn_count = 0;
|
||||
|
||||
for (size_t i = 0; i < ehdr->e_phnum; ++i) {
|
||||
ElfW(Phdr) phdr;
|
||||
CopyFromProcess(&phdr, pid_,
|
||||
reinterpret_cast<const void*>(phdr_addr),
|
||||
sizeof(phdr));
|
||||
if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) {
|
||||
min_vaddr = phdr.p_vaddr;
|
||||
}
|
||||
if (phdr.p_type == PT_DYNAMIC) {
|
||||
dyn_vaddr = phdr.p_vaddr;
|
||||
dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn));
|
||||
}
|
||||
phdr_addr += sizeof(phdr);
|
||||
}
|
||||
|
||||
*min_vaddr_ptr = min_vaddr;
|
||||
*dyn_vaddr_ptr = dyn_vaddr;
|
||||
*dyn_count_ptr = dyn_count;
|
||||
}
|
||||
|
||||
bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias,
|
||||
uintptr_t dyn_vaddr,
|
||||
size_t dyn_count) {
|
||||
uintptr_t dyn_addr = load_bias + dyn_vaddr;
|
||||
for (size_t i = 0; i < dyn_count; ++i) {
|
||||
ElfW(Dyn) dyn;
|
||||
CopyFromProcess(&dyn, pid_,
|
||||
reinterpret_cast<const void*>(dyn_addr),
|
||||
sizeof(dyn));
|
||||
if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) {
|
||||
return true;
|
||||
}
|
||||
dyn_addr += sizeof(dyn);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr,
|
||||
uintptr_t start_addr) {
|
||||
uintptr_t min_vaddr = 0;
|
||||
uintptr_t dyn_vaddr = 0;
|
||||
size_t dyn_count = 0;
|
||||
ParseLoadedElfProgramHeaders(ehdr, start_addr,
|
||||
&min_vaddr, &dyn_vaddr, &dyn_count);
|
||||
// If |min_vaddr| is non-zero and we find Android packed relocation tags,
|
||||
// return the effective load bias.
|
||||
if (min_vaddr != 0) {
|
||||
const uintptr_t load_bias = start_addr - min_vaddr;
|
||||
if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) {
|
||||
return load_bias;
|
||||
}
|
||||
}
|
||||
// Either |min_vaddr| is zero, or it is non-zero but we did not find the
|
||||
// expected Android packed relocations tags.
|
||||
return start_addr;
|
||||
}
|
||||
|
||||
void LinuxDumper::LatePostprocessMappings() {
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
// Only consider exec mappings that indicate a file path was mapped, and
|
||||
// where the ELF header indicates a mapped shared library.
|
||||
MappingInfo* mapping = mappings_[i];
|
||||
if (!(mapping->exec && mapping->name[0] == '/')) {
|
||||
continue;
|
||||
}
|
||||
ElfW(Ehdr) ehdr;
|
||||
if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) {
|
||||
continue;
|
||||
}
|
||||
if (ehdr.e_type == ET_DYN) {
|
||||
// Compute the effective load bias for this mapped library, and update
|
||||
// the mapping to hold that rather than |start_addr|, at the same time
|
||||
// adjusting |size| to account for the change in |start_addr|. Where
|
||||
// the library does not contain Android packed relocations,
|
||||
// GetEffectiveLoadBias() returns |start_addr| and the mapping entry
|
||||
// is not changed.
|
||||
const uintptr_t load_bias = GetEffectiveLoadBias(&ehdr,
|
||||
mapping->start_addr);
|
||||
mapping->size += mapping->start_addr - load_bias;
|
||||
mapping->start_addr = load_bias;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __ANDROID__
|
||||
|
||||
// Get information about the stack, given the stack pointer. We don't try to
|
||||
// walk the stack since we might not have all the information needed to do
|
||||
// unwind. So we just grab, up to, 32k of stack.
|
||||
|
@ -39,6 +39,9 @@
|
||||
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
|
||||
|
||||
#include <elf.h>
|
||||
#if defined(__ANDROID__)
|
||||
#include <link.h>
|
||||
#endif
|
||||
#include <linux/limits.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
@ -52,13 +55,15 @@
|
||||
namespace google_breakpad {
|
||||
|
||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||
#if defined(__i386) || defined(__ARM_EABI__) || defined(__mips__)
|
||||
#if defined(__i386) || defined(__ARM_EABI__) || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABIO32)
|
||||
typedef Elf32_auxv_t elf_aux_entry;
|
||||
#elif defined(__x86_64) || defined(__aarch64__)
|
||||
#elif defined(__x86_64) || defined(__aarch64__) || \
|
||||
(defined(__mips__) && _MIPS_SIM != _ABIO32)
|
||||
typedef Elf64_auxv_t elf_aux_entry;
|
||||
#endif
|
||||
|
||||
typedef typeof(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
||||
typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
||||
|
||||
// When we find the VDSO mapping in the process's address space, this
|
||||
// is the name we use for it when writing it to the minidump.
|
||||
@ -74,6 +79,12 @@ class LinuxDumper {
|
||||
// Parse the data for |threads| and |mappings|.
|
||||
virtual bool Init();
|
||||
|
||||
// Take any actions that could not be taken in Init(). LateInit() is
|
||||
// called after all other caller's initialization is complete, and in
|
||||
// particular after it has called ThreadsSuspend(), so that ptrace is
|
||||
// available.
|
||||
virtual bool LateInit();
|
||||
|
||||
// Return true if the dumper performs a post-mortem dump.
|
||||
virtual bool IsPostMortem() const = 0;
|
||||
|
||||
@ -100,8 +111,8 @@ class LinuxDumper {
|
||||
PageAllocator* allocator() { return &allocator_; }
|
||||
|
||||
// Copy content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|.
|
||||
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
// starting from |src|, into |dest|. Returns true on success.
|
||||
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length) = 0;
|
||||
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
@ -180,6 +191,62 @@ class LinuxDumper {
|
||||
|
||||
// Info from /proc/<pid>/auxv
|
||||
wasteful_vector<elf_aux_val_t> auxv_;
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
private:
|
||||
// Android M and later support packed ELF relocations in shared libraries.
|
||||
// Packing relocations changes the vaddr of the LOAD segments, such that
|
||||
// the effective load bias is no longer the same as the start address of
|
||||
// the memory mapping containing the executable parts of the library. The
|
||||
// packing is applied to the stripped library run on the target, but not to
|
||||
// any other library, and in particular not to the library used to generate
|
||||
// breakpad symbols. As a result, we need to adjust the |start_addr| for
|
||||
// any mapping that results from a shared library that contains Android
|
||||
// packed relocations, so that it properly represents the effective library
|
||||
// load bias. The following functions support this adjustment.
|
||||
|
||||
// Check that a given mapping at |start_addr| is for an ELF shared library.
|
||||
// If it is, place the ELF header in |ehdr| and return true.
|
||||
// The first LOAD segment in an ELF shared library has offset zero, so the
|
||||
// ELF file header is at the start of this map entry, and in already mapped
|
||||
// memory.
|
||||
bool GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr);
|
||||
|
||||
// For the ELF file mapped at |start_addr|, iterate ELF program headers to
|
||||
// find the min vaddr of all program header LOAD segments, the vaddr for
|
||||
// the DYNAMIC segment, and a count of DYNAMIC entries. Return values in
|
||||
// |min_vaddr_ptr|, |dyn_vaddr_ptr|, and |dyn_count_ptr|.
|
||||
// The program header table is also in already mapped memory.
|
||||
void ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr,
|
||||
uintptr_t start_addr,
|
||||
uintptr_t* min_vaddr_ptr,
|
||||
uintptr_t* dyn_vaddr_ptr,
|
||||
size_t* dyn_count_ptr);
|
||||
|
||||
// Search the DYNAMIC tags for the ELF file with the given |load_bias|, and
|
||||
// return true if the tags indicate that the file contains Android packed
|
||||
// relocations. Dynamic tags are found at |dyn_vaddr| past the |load_bias|.
|
||||
bool HasAndroidPackedRelocations(uintptr_t load_bias,
|
||||
uintptr_t dyn_vaddr,
|
||||
size_t dyn_count);
|
||||
|
||||
// If the ELF file mapped at |start_addr| contained Android packed
|
||||
// relocations, return the load bias that the system linker (or Chromium
|
||||
// crazy linker) will have used. If the file did not contain Android
|
||||
// packed relocations, returns |start_addr|, indicating that no adjustment
|
||||
// is necessary.
|
||||
// The effective load bias is |start_addr| adjusted downwards by the
|
||||
// min vaddr in the library LOAD segments.
|
||||
uintptr_t GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, uintptr_t start_addr);
|
||||
|
||||
// Called from LateInit(). Iterates |mappings_| and rewrites the |start_addr|
|
||||
// field of any that represent ELF shared libraries with Android packed
|
||||
// relocations, so that |start_addr| is the load bias that the system linker
|
||||
// (or Chromium crazy linker) used. This value matches the addresses produced
|
||||
// when the non-relocation-packed library is used for breakpad symbol
|
||||
// generation.
|
||||
void LatePostprocessMappings();
|
||||
#endif // __ANDROID__
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -130,7 +130,7 @@ bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
const void* src, size_t length) {
|
||||
unsigned long tmp = 55;
|
||||
size_t done = 0;
|
||||
@ -146,6 +146,7 @@ void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
|
||||
my_memcpy(local + done, &tmp, l);
|
||||
done += l;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
@ -189,26 +190,34 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
|
||||
#ifdef PTRACE_GETREGSET
|
||||
struct iovec io;
|
||||
io.iov_base = &info->regs;
|
||||
io.iov_len = sizeof(info->regs);
|
||||
info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
io.iov_base = &info->fpregs;
|
||||
io.iov_len = sizeof(info->fpregs);
|
||||
info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len);
|
||||
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
|
||||
#else // PTRACE_GETREGSET
|
||||
void* gp_addr;
|
||||
info->GetGeneralPurposeRegisters(&gp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
|
||||
#if !(defined(__ANDROID__) && defined(__ARM_EABI__))
|
||||
// When running an arm build on an arm64 device, attempting to get the
|
||||
// floating point registers fails. On Android, the floating point registers
|
||||
// aren't written to the cpu context anyway, so just don't get them here.
|
||||
// See http://crbug.com/508324
|
||||
void* fp_addr;
|
||||
info->GetFloatingPointRegisters(&fp_addr, NULL);
|
||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#endif // PTRACE_GETREGSET
|
||||
|
||||
#if defined(__i386)
|
||||
#if !defined(bit_FXSAVE) // e.g. Clang
|
||||
@ -240,14 +249,20 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
#endif
|
||||
|
||||
#if defined(__mips__)
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + (i * 2)), &info->hi[i]);
|
||||
reinterpret_cast<void*>(DSP_BASE), &info->mcontext.hi1);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + (i * 2) + 1), &info->lo[i]);
|
||||
}
|
||||
reinterpret_cast<void*>(DSP_BASE + 1), &info->mcontext.lo1);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_CONTROL), &info->dsp_control);
|
||||
reinterpret_cast<void*>(DSP_BASE + 2), &info->mcontext.hi2);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 3), &info->mcontext.lo2);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 4), &info->mcontext.hi3);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_BASE + 5), &info->mcontext.lo3);
|
||||
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||
reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp);
|
||||
#endif
|
||||
|
||||
const uint8_t* stack_pointer;
|
||||
@ -261,7 +276,7 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
||||
my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
||||
#elif defined(__mips__)
|
||||
stack_pointer =
|
||||
reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]);
|
||||
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
@ -282,8 +297,10 @@ bool LinuxPtraceDumper::ThreadsSuspend() {
|
||||
// If the thread either disappeared before we could attach to it, or if
|
||||
// it was part of the seccomp sandbox's trusted code, it is OK to
|
||||
// silently drop it from the minidump.
|
||||
if (i < threads_.size() - 1) {
|
||||
my_memmove(&threads_[i], &threads_[i + 1],
|
||||
(threads_.size() - i - 1) * sizeof(threads_[i]));
|
||||
}
|
||||
threads_.resize(threads_.size() - 1);
|
||||
--i;
|
||||
}
|
||||
|
@ -55,8 +55,8 @@ class LinuxPtraceDumper : public LinuxDumper {
|
||||
// Implements LinuxDumper::CopyFromProcess().
|
||||
// Copies content of |length| bytes from a given process |child|,
|
||||
// starting from |src|, into |dest|. This method uses ptrace to extract
|
||||
// the content from the target process.
|
||||
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
// the content from the target process. Always returns true.
|
||||
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length);
|
||||
|
||||
// Implements LinuxDumper::GetThreadInfoByIndex().
|
||||
|
@ -441,7 +441,7 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
|
||||
#elif defined(__mips__)
|
||||
pid_t* process_tid_location =
|
||||
reinterpret_cast<pid_t*>(one_thread.regs.regs[1]);
|
||||
reinterpret_cast<pid_t*>(one_thread.mcontext.gregs[1]);
|
||||
#else
|
||||
#error This test has not been ported to this platform.
|
||||
#endif
|
||||
|
@ -64,7 +64,6 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "client/linux/dump_writer_common/seccomp_unwinder.h"
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
@ -75,6 +74,7 @@
|
||||
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
|
||||
#include "client/minidump_file_writer.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/minidump_type_helper.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
@ -86,6 +86,7 @@ using google_breakpad::CpuSet;
|
||||
using google_breakpad::LineReader;
|
||||
using google_breakpad::LinuxDumper;
|
||||
using google_breakpad::LinuxPtraceDumper;
|
||||
using google_breakpad::MDTypeHelper;
|
||||
using google_breakpad::MappingEntry;
|
||||
using google_breakpad::MappingInfo;
|
||||
using google_breakpad::MappingList;
|
||||
@ -93,13 +94,14 @@ using google_breakpad::MinidumpFileWriter;
|
||||
using google_breakpad::PageAllocator;
|
||||
using google_breakpad::ProcCpuInfoReader;
|
||||
using google_breakpad::RawContextCPU;
|
||||
using google_breakpad::SeccompUnwinder;
|
||||
using google_breakpad::ThreadInfo;
|
||||
using google_breakpad::TypedMDRVA;
|
||||
using google_breakpad::UContextReader;
|
||||
using google_breakpad::UntypedMDRVA;
|
||||
using google_breakpad::wasteful_vector;
|
||||
|
||||
typedef MDTypeHelper<sizeof(void*)>::MDRawDebug MDRawDebug;
|
||||
typedef MDTypeHelper<sizeof(void*)>::MDRawLinkMap MDRawLinkMap;
|
||||
|
||||
class MinidumpWriter {
|
||||
public:
|
||||
@ -150,7 +152,7 @@ class MinidumpWriter {
|
||||
else if (!minidump_writer_.Open(path_))
|
||||
return false;
|
||||
|
||||
return dumper_->ThreadsSuspend();
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
}
|
||||
|
||||
~MinidumpWriter() {
|
||||
@ -379,8 +381,6 @@ class MinidumpWriter {
|
||||
#else
|
||||
UContextReader::FillCPUContext(cpu.get(), ucontext_);
|
||||
#endif
|
||||
if (stack_copy)
|
||||
SeccompUnwinder::PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||
thread.thread_context = cpu.location();
|
||||
crashing_thread_context_ = cpu.location();
|
||||
} else {
|
||||
@ -401,8 +401,6 @@ class MinidumpWriter {
|
||||
return false;
|
||||
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
||||
info.FillCPUContext(cpu.get());
|
||||
if (stack_copy)
|
||||
SeccompUnwinder::PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||
thread.thread_context = cpu.location();
|
||||
if (dumper_->threads()[i] == GetCrashThread()) {
|
||||
crashing_thread_context_ = cpu.location();
|
||||
@ -511,7 +509,7 @@ class MinidumpWriter {
|
||||
continue;
|
||||
|
||||
MDRawModule mod;
|
||||
if (!FillRawModule(mapping, true, i, mod, NULL))
|
||||
if (!FillRawModule(mapping, true, i, &mod, NULL))
|
||||
return false;
|
||||
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
|
||||
}
|
||||
@ -520,7 +518,7 @@ class MinidumpWriter {
|
||||
iter != mapping_list_.end();
|
||||
++iter) {
|
||||
MDRawModule mod;
|
||||
if (!FillRawModule(iter->first, false, 0, mod, iter->second))
|
||||
if (!FillRawModule(iter->first, false, 0, &mod, iter->second))
|
||||
return false;
|
||||
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
|
||||
}
|
||||
@ -534,12 +532,12 @@ class MinidumpWriter {
|
||||
bool FillRawModule(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
MDRawModule& mod,
|
||||
MDRawModule* mod,
|
||||
const uint8_t* identifier) {
|
||||
my_memset(&mod, 0, MD_MODULE_SIZE);
|
||||
my_memset(mod, 0, MD_MODULE_SIZE);
|
||||
|
||||
mod.base_of_image = mapping.start_addr;
|
||||
mod.size_of_image = mapping.size;
|
||||
mod->base_of_image = mapping.start_addr;
|
||||
mod->size_of_image = mapping.size;
|
||||
|
||||
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
|
||||
uint8_t* cv_ptr = cv_buf;
|
||||
@ -574,12 +572,12 @@ class MinidumpWriter {
|
||||
my_memcpy(cv_ptr, file_name, file_name_len + 1);
|
||||
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + file_name_len + 1);
|
||||
|
||||
mod.cv_record = cv.location();
|
||||
mod->cv_record = cv.location();
|
||||
|
||||
MDLocationDescriptor ld;
|
||||
if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld))
|
||||
return false;
|
||||
mod.module_name_rva = ld.rva;
|
||||
mod->module_name_rva = ld.rva;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -654,7 +652,9 @@ class MinidumpWriter {
|
||||
ElfW(Addr) dyn_addr = 0;
|
||||
for (; phnum >= 0; phnum--, phdr++) {
|
||||
ElfW(Phdr) ph;
|
||||
dumper_->CopyFromProcess(&ph, GetCrashThread(), phdr, sizeof(ph));
|
||||
if (!dumper_->CopyFromProcess(&ph, GetCrashThread(), phdr, sizeof(ph)))
|
||||
return false;
|
||||
|
||||
// Adjust base address with the virtual address of the PT_LOAD segment
|
||||
// corresponding to offset 0
|
||||
if (ph.p_type == PT_LOAD && ph.p_offset == 0) {
|
||||
@ -675,12 +675,20 @@ class MinidumpWriter {
|
||||
struct r_debug* r_debug = NULL;
|
||||
uint32_t dynamic_length = 0;
|
||||
|
||||
for (int i = 0;;) {
|
||||
for (int i = 0; ; ++i) {
|
||||
ElfW(Dyn) dyn;
|
||||
dynamic_length += sizeof(dyn);
|
||||
dumper_->CopyFromProcess(&dyn, GetCrashThread(), dynamic+i++,
|
||||
sizeof(dyn));
|
||||
if (dyn.d_tag == DT_DEBUG) {
|
||||
if (!dumper_->CopyFromProcess(&dyn, GetCrashThread(), dynamic + i,
|
||||
sizeof(dyn))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef __mips__
|
||||
const int32_t debug_tag = DT_MIPS_RLD_MAP;
|
||||
#else
|
||||
const int32_t debug_tag = DT_DEBUG;
|
||||
#endif
|
||||
if (dyn.d_tag == debug_tag) {
|
||||
r_debug = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr);
|
||||
continue;
|
||||
} else if (dyn.d_tag == DT_NULL) {
|
||||
@ -699,11 +707,15 @@ class MinidumpWriter {
|
||||
// Count the number of loaded DSOs
|
||||
int dso_count = 0;
|
||||
struct r_debug debug_entry;
|
||||
dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug,
|
||||
sizeof(debug_entry));
|
||||
if (!dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug,
|
||||
sizeof(debug_entry))) {
|
||||
return false;
|
||||
}
|
||||
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
|
||||
struct link_map map;
|
||||
dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map));
|
||||
if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)))
|
||||
return false;
|
||||
|
||||
ptr = map.l_next;
|
||||
dso_count++;
|
||||
}
|
||||
@ -721,7 +733,9 @@ class MinidumpWriter {
|
||||
// Iterate over DSOs and write their information to mini dump
|
||||
for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
|
||||
struct link_map map;
|
||||
dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map));
|
||||
if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)))
|
||||
return false;
|
||||
|
||||
ptr = map.l_next;
|
||||
char filename[257] = { 0 };
|
||||
if (map.l_name) {
|
||||
@ -733,8 +747,8 @@ class MinidumpWriter {
|
||||
return false;
|
||||
MDRawLinkMap entry;
|
||||
entry.name = location.rva;
|
||||
entry.addr = reinterpret_cast<void*>(map.l_addr);
|
||||
entry.ld = reinterpret_cast<void*>(map.l_ld);
|
||||
entry.addr = map.l_addr;
|
||||
entry.ld = reinterpret_cast<uintptr_t>(map.l_ld);
|
||||
linkmap.CopyIndex(idx++, &entry);
|
||||
}
|
||||
}
|
||||
@ -750,9 +764,9 @@ class MinidumpWriter {
|
||||
debug.get()->version = debug_entry.r_version;
|
||||
debug.get()->map = linkmap_rva;
|
||||
debug.get()->dso_count = dso_count;
|
||||
debug.get()->brk = reinterpret_cast<void*>(debug_entry.r_brk);
|
||||
debug.get()->ldbase = reinterpret_cast<void*>(debug_entry.r_ldbase);
|
||||
debug.get()->dynamic = dynamic;
|
||||
debug.get()->brk = debug_entry.r_brk;
|
||||
debug.get()->ldbase = debug_entry.r_ldbase;
|
||||
debug.get()->dynamic = reinterpret_cast<uintptr_t>(dynamic);
|
||||
|
||||
wasteful_vector<char> dso_debug_data(dumper_->allocator(), dynamic_length);
|
||||
// The passed-in size to the constructor (above) is only a hint.
|
||||
@ -819,15 +833,14 @@ class MinidumpWriter {
|
||||
ProcCpuInfoReader* const reader = new(allocator) ProcCpuInfoReader(fd);
|
||||
const char* field;
|
||||
while (reader->GetNextField(&field)) {
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
|
||||
i++) {
|
||||
CpuInfoEntry* entry = &cpu_info_table[i];
|
||||
if (i > 0 && entry->found) {
|
||||
bool is_first_entry = true;
|
||||
for (CpuInfoEntry& entry : cpu_info_table) {
|
||||
if (!is_first_entry && entry.found) {
|
||||
// except for the 'processor' field, ignore repeated values.
|
||||
continue;
|
||||
}
|
||||
if (!my_strcmp(field, entry->info_name)) {
|
||||
is_first_entry = false;
|
||||
if (!my_strcmp(field, entry.info_name)) {
|
||||
size_t value_len;
|
||||
const char* value = reader->GetValueAndLen(&value_len);
|
||||
if (value_len == 0)
|
||||
@ -837,8 +850,8 @@ class MinidumpWriter {
|
||||
if (my_read_decimal_ptr(&val, value) == value)
|
||||
continue;
|
||||
|
||||
entry->value = static_cast<int>(val);
|
||||
entry->found = true;
|
||||
entry.value = static_cast<int>(val);
|
||||
entry.found = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -854,10 +867,8 @@ class MinidumpWriter {
|
||||
}
|
||||
|
||||
// make sure we got everything we wanted
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
|
||||
i++) {
|
||||
if (!cpu_info_table[i].found) {
|
||||
for (const CpuInfoEntry& entry : cpu_info_table) {
|
||||
if (!entry.found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -993,18 +1004,15 @@ class MinidumpWriter {
|
||||
new(allocator) ProcCpuInfoReader(fd);
|
||||
const char* field;
|
||||
while (reader->GetNextField(&field)) {
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_id_entries)/sizeof(cpu_id_entries[0]);
|
||||
++i) {
|
||||
const CpuIdEntry* entry = &cpu_id_entries[i];
|
||||
if (my_strcmp(entry->field, field) != 0)
|
||||
for (const CpuIdEntry& entry : cpu_id_entries) {
|
||||
if (my_strcmp(entry.field, field) != 0)
|
||||
continue;
|
||||
uintptr_t result = 0;
|
||||
const char* value = reader->GetValue();
|
||||
const char* p = value;
|
||||
if (value[0] == '0' && value[1] == 'x') {
|
||||
p = my_read_hex_ptr(&result, value+2);
|
||||
} else if (entry->format == 'x') {
|
||||
} else if (entry.format == 'x') {
|
||||
p = my_read_hex_ptr(&result, value);
|
||||
} else {
|
||||
p = my_read_decimal_ptr(&result, value);
|
||||
@ -1012,8 +1020,8 @@ class MinidumpWriter {
|
||||
if (p == value)
|
||||
continue;
|
||||
|
||||
result &= (1U << entry->bit_length)-1;
|
||||
result <<= entry->bit_lshift;
|
||||
result &= (1U << entry.bit_length)-1;
|
||||
result <<= entry.bit_lshift;
|
||||
sys_info->cpu.arm_cpu_info.cpuid |=
|
||||
static_cast<uint32_t>(result);
|
||||
}
|
||||
@ -1067,7 +1075,7 @@ class MinidumpWriter {
|
||||
const char* tag = value;
|
||||
size_t tag_len = value_len;
|
||||
const char* p = my_strchr(tag, ' ');
|
||||
if (p != NULL) {
|
||||
if (p) {
|
||||
tag_len = static_cast<size_t>(p - tag);
|
||||
value += tag_len + 1;
|
||||
value_len -= tag_len + 1;
|
||||
@ -1075,14 +1083,10 @@ class MinidumpWriter {
|
||||
tag_len = strlen(tag);
|
||||
value_len = 0;
|
||||
}
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_features_entries)/
|
||||
sizeof(cpu_features_entries[0]);
|
||||
++i) {
|
||||
const CpuFeaturesEntry* entry = &cpu_features_entries[i];
|
||||
if (tag_len == strlen(entry->tag) &&
|
||||
!memcmp(tag, entry->tag, tag_len)) {
|
||||
sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry->hwcaps;
|
||||
for (const CpuFeaturesEntry& entry : cpu_features_entries) {
|
||||
if (tag_len == strlen(entry.tag) &&
|
||||
!memcmp(tag, entry.tag, tag_len)) {
|
||||
sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry.hwcaps;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@
|
||||
#import "common/mac/MachIPC.h"
|
||||
#import "common/simple_string_dictionary.h"
|
||||
|
||||
#ifndef __EXCEPTIONS
|
||||
#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions))
|
||||
// This file uses C++ try/catch (but shouldn't). Duplicate the macros from
|
||||
// <c++/4.2.1/exception_defines.h> allowing this file to work properly with
|
||||
// exceptions disabled even when other C++ libraries are used. #undef the try
|
||||
|
@ -364,7 +364,7 @@ static uint64_t LookupSymbol(const char* symbol_name,
|
||||
return list.n_value;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
|
||||
static bool HasTaskDyldInfo() {
|
||||
return true;
|
||||
}
|
||||
@ -381,13 +381,9 @@ static SInt32 GetOSVersion() {
|
||||
}
|
||||
|
||||
static bool HasTaskDyldInfo() {
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
|
||||
return true;
|
||||
#else
|
||||
return GetOSVersion() >= 0x1060;
|
||||
#endif
|
||||
}
|
||||
#endif // TARGET_OS_IPHONE
|
||||
#endif // TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= 10_6
|
||||
|
||||
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
|
||||
if (HasTaskDyldInfo()) {
|
||||
|
@ -133,25 +133,47 @@ void MinidumpGenerator::GatherSystemInformation() {
|
||||
vers_path,
|
||||
kCFURLPOSIXPathStyle,
|
||||
false);
|
||||
CFDataRef data;
|
||||
SInt32 error;
|
||||
CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
|
||||
&error);
|
||||
|
||||
if (!data) {
|
||||
CFReadStreamRef read_stream = CFReadStreamCreateWithFile(NULL, sys_vers);
|
||||
CFRelease(sys_vers);
|
||||
if (!read_stream) {
|
||||
return;
|
||||
}
|
||||
if (!CFReadStreamOpen(read_stream)) {
|
||||
CFRelease(read_stream);
|
||||
return;
|
||||
}
|
||||
CFMutableDataRef data = NULL;
|
||||
while (true) {
|
||||
// Actual data file tests: Mac at 480 bytes and iOS at 413 bytes.
|
||||
const CFIndex kMaxBufferLength = 1024;
|
||||
UInt8 data_bytes[kMaxBufferLength];
|
||||
CFIndex num_bytes_read =
|
||||
CFReadStreamRead(read_stream, data_bytes, kMaxBufferLength);
|
||||
if (num_bytes_read < 0) {
|
||||
if (data) {
|
||||
CFRelease(data);
|
||||
data = NULL;
|
||||
}
|
||||
break;
|
||||
} else if (num_bytes_read == 0) {
|
||||
break;
|
||||
} else if (!data) {
|
||||
data = CFDataCreateMutable(NULL, 0);
|
||||
}
|
||||
CFDataAppendBytes(data, data_bytes, num_bytes_read);
|
||||
}
|
||||
CFReadStreamClose(read_stream);
|
||||
CFRelease(read_stream);
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
CFDictionaryRef list = static_cast<CFDictionaryRef>
|
||||
(CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
|
||||
NULL));
|
||||
if (!list) {
|
||||
CFRelease(sys_vers);
|
||||
CFRelease(data);
|
||||
if (!list) {
|
||||
return;
|
||||
}
|
||||
|
||||
CFStringRef build_version = static_cast<CFStringRef>
|
||||
(CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
|
||||
CFStringRef product_version = static_cast<CFStringRef>
|
||||
@ -160,8 +182,6 @@ void MinidumpGenerator::GatherSystemInformation() {
|
||||
string product_str = ConvertToString(product_version);
|
||||
|
||||
CFRelease(list);
|
||||
CFRelease(sys_vers);
|
||||
CFRelease(data);
|
||||
|
||||
strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
|
||||
|
||||
|
@ -610,7 +610,9 @@ TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
|
||||
ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL);
|
||||
// Try calling a NULL pointer.
|
||||
typedef void (*void_function)(void);
|
||||
void_function memory_function =
|
||||
// Volatile markings are needed to keep Clang from generating invalid
|
||||
// opcodes. See http://crbug.com/498354 for details.
|
||||
volatile void_function memory_function =
|
||||
reinterpret_cast<void_function>(NULL);
|
||||
memory_function();
|
||||
// not reached
|
||||
|
@ -44,6 +44,47 @@
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <errno.h>
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_need_ftruncate_workaround = false;
|
||||
bool g_checked_need_ftruncate_workaround = false;
|
||||
|
||||
void CheckNeedsFTruncateWorkAround(int file) {
|
||||
if (g_checked_need_ftruncate_workaround) {
|
||||
return;
|
||||
}
|
||||
g_checked_need_ftruncate_workaround = true;
|
||||
|
||||
// Attempt an idempotent truncate that chops off nothing and see if we
|
||||
// run into any sort of errors.
|
||||
off_t offset = sys_lseek(file, 0, SEEK_END);
|
||||
if (offset == -1) {
|
||||
// lseek failed. Don't apply work around. It's unlikely that we can write
|
||||
// to a minidump with either method.
|
||||
return;
|
||||
}
|
||||
|
||||
int result = ftruncate(file, offset);
|
||||
if (result == -1 && errno == EACCES) {
|
||||
// It very likely that we are running into the kernel bug in M devices.
|
||||
// We are going to deploy the workaround for writing minidump files
|
||||
// without uses of ftruncate(). This workaround should be fine even
|
||||
// for kernels without the bug.
|
||||
// See http://crbug.com/542840 for more details.
|
||||
g_need_ftruncate_workaround = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool NeedsFTruncateWorkAround() {
|
||||
return g_need_ftruncate_workaround;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif // defined(__ANDROID__)
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
|
||||
@ -75,15 +116,24 @@ void MinidumpFileWriter::SetFile(const int file) {
|
||||
assert(file_ == -1);
|
||||
file_ = file;
|
||||
close_file_when_destroyed_ = false;
|
||||
#if defined(__ANDROID__)
|
||||
CheckNeedsFTruncateWorkAround(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::Close() {
|
||||
bool result = true;
|
||||
|
||||
if (file_ != -1) {
|
||||
if (-1 == ftruncate(file_, position_)) {
|
||||
#if defined(__ANDROID__)
|
||||
if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (ftruncate(file_, position_)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(__linux__) && __linux__
|
||||
result = (sys_close(file_) == 0);
|
||||
#else
|
||||
@ -220,6 +270,20 @@ bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
|
||||
MDRVA MinidumpFileWriter::Allocate(size_t size) {
|
||||
assert(size);
|
||||
assert(file_ != -1);
|
||||
#if defined(__ANDROID__)
|
||||
if (NeedsFTruncateWorkAround()) {
|
||||
// If ftruncate() is not available. We simply increase the size beyond the
|
||||
// current file size. sys_write() will expand the file when data is written
|
||||
// to it. Because we did not over allocate to fit memory pages, we also
|
||||
// do not need to ftruncate() the file once we are done.
|
||||
size_ += size;
|
||||
|
||||
// We don't need to seek since the file is unchanged.
|
||||
MDRVA current_position = position_;
|
||||
position_ += static_cast<MDRVA>(size);
|
||||
return current_position;
|
||||
}
|
||||
#endif
|
||||
size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
|
||||
|
||||
if (position_ + aligned_size > size_) {
|
||||
@ -256,14 +320,16 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
|
||||
#if defined(__linux__) && __linux__
|
||||
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
||||
if (sys_write(file_, src, size) == size) {
|
||||
#else
|
||||
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
||||
if (write(file_, src, size) == size) {
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
||||
if (write(file_, src, size) == size) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,14 +96,14 @@ CrashGenerationClient::CrashGenerationClient(
|
||||
const CustomClientInfo* custom_info)
|
||||
: pipe_name_(pipe_name),
|
||||
pipe_handle_(NULL),
|
||||
custom_info_(),
|
||||
dump_type_(dump_type),
|
||||
thread_id_(0),
|
||||
server_process_id_(0),
|
||||
crash_event_(NULL),
|
||||
crash_generated_(NULL),
|
||||
server_alive_(NULL),
|
||||
exception_pointers_(NULL),
|
||||
custom_info_() {
|
||||
server_process_id_(0),
|
||||
thread_id_(0),
|
||||
exception_pointers_(NULL) {
|
||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
||||
if (custom_info) {
|
||||
custom_info_ = *custom_info;
|
||||
@ -116,14 +116,14 @@ CrashGenerationClient::CrashGenerationClient(
|
||||
const CustomClientInfo* custom_info)
|
||||
: pipe_name_(),
|
||||
pipe_handle_(pipe_handle),
|
||||
custom_info_(),
|
||||
dump_type_(dump_type),
|
||||
thread_id_(0),
|
||||
server_process_id_(0),
|
||||
crash_event_(NULL),
|
||||
crash_generated_(NULL),
|
||||
server_alive_(NULL),
|
||||
exception_pointers_(NULL),
|
||||
custom_info_() {
|
||||
server_process_id_(0),
|
||||
thread_id_(0),
|
||||
exception_pointers_(NULL) {
|
||||
memset(&assert_info_, 0, sizeof(assert_info_));
|
||||
if (custom_info) {
|
||||
custom_info_ = *custom_info;
|
||||
|
@ -85,7 +85,7 @@ static bool IsClientRequestValid(const ProtocolMessage& msg) {
|
||||
msg.assert_info != NULL);
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
#ifndef NDEBUG
|
||||
static bool CheckForIOIncomplete(bool success) {
|
||||
// We should never get an I/O incomplete since we should not execute this
|
||||
// unless the operation has finished and the overlapped event is signaled. If
|
||||
@ -121,12 +121,12 @@ CrashGenerationServer::CrashGenerationServer(
|
||||
upload_request_callback_(upload_request_callback),
|
||||
upload_context_(upload_context),
|
||||
generate_dumps_(generate_dumps),
|
||||
pre_fetch_custom_info_(true),
|
||||
dump_path_(dump_path ? *dump_path : L""),
|
||||
server_state_(IPC_SERVER_STATE_UNINITIALIZED),
|
||||
shutting_down_(false),
|
||||
overlapped_(),
|
||||
client_info_(NULL),
|
||||
pre_fetch_custom_info_(true) {
|
||||
client_info_(NULL) {
|
||||
InitializeCriticalSection(&sync_);
|
||||
}
|
||||
|
||||
|
@ -259,8 +259,9 @@ MinidumpGenerator::MinidumpGenerator(
|
||||
const MINIDUMP_TYPE dump_type,
|
||||
const bool is_client_pointers)
|
||||
: dbghelp_module_(NULL),
|
||||
write_dump_(NULL),
|
||||
rpcrt4_module_(NULL),
|
||||
dump_path_(dump_path),
|
||||
create_uuid_(NULL),
|
||||
process_handle_(process_handle),
|
||||
process_id_(process_id),
|
||||
thread_id_(thread_id),
|
||||
@ -269,14 +270,13 @@ MinidumpGenerator::MinidumpGenerator(
|
||||
assert_info_(assert_info),
|
||||
dump_type_(dump_type),
|
||||
is_client_pointers_(is_client_pointers),
|
||||
dump_path_(dump_path),
|
||||
dump_file_(INVALID_HANDLE_VALUE),
|
||||
full_dump_file_(INVALID_HANDLE_VALUE),
|
||||
dump_file_is_internal_(false),
|
||||
full_dump_file_is_internal_(false),
|
||||
additional_streams_(NULL),
|
||||
callback_info_(NULL),
|
||||
write_dump_(NULL),
|
||||
create_uuid_(NULL) {
|
||||
callback_info_(NULL) {
|
||||
InitializeCriticalSection(&module_load_sync_);
|
||||
InitializeCriticalSection(&get_proc_address_sync_);
|
||||
}
|
||||
|
@ -174,6 +174,7 @@ void ExceptionHandler::Initialize(
|
||||
assertion_ = NULL;
|
||||
handler_return_value_ = false;
|
||||
handle_debug_exceptions_ = false;
|
||||
consume_invalid_handle_exceptions_ = false;
|
||||
|
||||
// Attempt to use out-of-process if user has specified a pipe or a
|
||||
// crash generation client.
|
||||
@ -481,6 +482,11 @@ LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
|
||||
bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
|
||||
(code == EXCEPTION_SINGLE_STEP);
|
||||
|
||||
if (code == EXCEPTION_INVALID_HANDLE &&
|
||||
current_handler->consume_invalid_handle_exceptions_) {
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
bool success = false;
|
||||
|
||||
if (!is_debug_exception ||
|
||||
|
@ -263,6 +263,15 @@ class ExceptionHandler {
|
||||
handle_debug_exceptions_ = handle_debug_exceptions;
|
||||
}
|
||||
|
||||
// Controls behavior of EXCEPTION_INVALID_HANDLE.
|
||||
bool get_consume_invalid_handle_exceptions() const {
|
||||
return consume_invalid_handle_exceptions_;
|
||||
}
|
||||
void set_consume_invalid_handle_exceptions(
|
||||
bool consume_invalid_handle_exceptions) {
|
||||
consume_invalid_handle_exceptions_ = consume_invalid_handle_exceptions;
|
||||
}
|
||||
|
||||
// Returns whether out-of-process dump generation is used or not.
|
||||
bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; }
|
||||
|
||||
@ -472,6 +481,10 @@ class ExceptionHandler {
|
||||
// to not interfere with debuggers.
|
||||
bool handle_debug_exceptions_;
|
||||
|
||||
// If true, the handler will consume any EXCEPTION_INVALID_HANDLE exceptions.
|
||||
// Leave this false (the default) to handle these exceptions as normal.
|
||||
bool consume_invalid_handle_exceptions_;
|
||||
|
||||
// Callers can request additional memory regions to be included in
|
||||
// the dump.
|
||||
AppMemoryList app_memory_info_;
|
||||
|
@ -59,7 +59,7 @@ CrashReportSender::CrashReportSender(const wstring &checkpoint_file)
|
||||
|
||||
ReportResult CrashReportSender::SendCrashReport(
|
||||
const wstring &url, const map<wstring, wstring> ¶meters,
|
||||
const wstring &dump_file_name, wstring *report_code) {
|
||||
const map<wstring, wstring> &files, wstring *report_code) {
|
||||
int today = GetCurrentDate();
|
||||
if (today == last_sent_date_ &&
|
||||
max_reports_per_day_ != -1 &&
|
||||
@ -69,7 +69,7 @@ ReportResult CrashReportSender::SendCrashReport(
|
||||
|
||||
int http_response = 0;
|
||||
bool result = HTTPUpload::SendRequest(
|
||||
url, parameters, dump_file_name, L"upload_file_minidump", NULL, report_code,
|
||||
url, parameters, files, NULL, report_code,
|
||||
&http_response);
|
||||
|
||||
if (result) {
|
||||
|
@ -77,7 +77,7 @@ class CrashReportSender {
|
||||
|
||||
int max_reports_per_day() const { return max_reports_per_day_; }
|
||||
|
||||
// Sends the specified minidump file, along with the map of
|
||||
// Sends the specified files, along with the map of
|
||||
// name value pairs, as a multipart POST request to the given URL.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
@ -89,7 +89,7 @@ class CrashReportSender {
|
||||
// (Otherwise, report_code will be unchanged.)
|
||||
ReportResult SendCrashReport(const wstring &url,
|
||||
const map<wstring, wstring> ¶meters,
|
||||
const wstring &dump_file_name,
|
||||
const map<wstring, wstring> &files,
|
||||
wstring *report_code);
|
||||
|
||||
private:
|
||||
|
@ -73,7 +73,7 @@ BOOL InitInstance(HINSTANCE, int);
|
||||
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
static int kCustomInfoCount = 2;
|
||||
static size_t kCustomInfoCount = 2;
|
||||
static CustomInfoEntry kCustomInfoEntries[] = {
|
||||
CustomInfoEntry(L"prod", L"CrashTestApp"),
|
||||
CustomInfoEntry(L"ver", L"1.0"),
|
||||
@ -197,7 +197,7 @@ bool ShowDumpResults(const wchar_t* dump_path,
|
||||
return succeeded;
|
||||
}
|
||||
|
||||
static void _cdecl ShowClientConnected(void* context,
|
||||
static void ShowClientConnected(void* context,
|
||||
const ClientInfo* client_info) {
|
||||
TCHAR* line = new TCHAR[kMaximumLineLength];
|
||||
line[0] = _T('\0');
|
||||
@ -214,7 +214,7 @@ static void _cdecl ShowClientConnected(void* context,
|
||||
QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT);
|
||||
}
|
||||
|
||||
static void _cdecl ShowClientCrashed(void* context,
|
||||
static void ShowClientCrashed(void* context,
|
||||
const ClientInfo* client_info,
|
||||
const wstring* dump_path) {
|
||||
TCHAR* line = new TCHAR[kMaximumLineLength];
|
||||
@ -259,7 +259,7 @@ static void _cdecl ShowClientCrashed(void* context,
|
||||
QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT);
|
||||
}
|
||||
|
||||
static void _cdecl ShowClientExited(void* context,
|
||||
static void ShowClientExited(void* context,
|
||||
const ClientInfo* client_info) {
|
||||
TCHAR* line = new TCHAR[kMaximumLineLength];
|
||||
line[0] = _T('\0');
|
||||
|
@ -73,6 +73,7 @@
|
||||
'<(DEPTH)/processor/logging.cc',
|
||||
'<(DEPTH)/processor/minidump.cc',
|
||||
'<(DEPTH)/processor/pathname_stripper.cc',
|
||||
'<(DEPTH)/processor/proc_maps_linux.cc',
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -50,9 +50,8 @@ const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
|
||||
|
||||
const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
|
||||
|
||||
int kCustomInfoCount = 2;
|
||||
|
||||
google_breakpad::CustomInfoEntry kCustomInfoEntries[] = {
|
||||
#define arraysize(f) (sizeof(f) / sizeof(*f))
|
||||
const google_breakpad::CustomInfoEntry kCustomInfoEntries[] = {
|
||||
google_breakpad::CustomInfoEntry(L"prod", L"CrashGenerationServerTest"),
|
||||
google_breakpad::CustomInfoEntry(L"ver", L"1.0"),
|
||||
};
|
||||
@ -165,7 +164,7 @@ class CrashGenerationServerTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
google_breakpad::CustomClientInfo custom_info = {kCustomInfoEntries,
|
||||
kCustomInfoCount};
|
||||
arraysize(kCustomInfoEntries)};
|
||||
|
||||
google_breakpad::ProtocolMessage msg(
|
||||
fault_type == SEND_INVALID_REGISTRATION ?
|
||||
|
@ -229,14 +229,27 @@ breakpad_getcontext:
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
#if _MIPS_SIM != _ABIO32
|
||||
#error "Unsupported mips ISA. Only mips o32 is supported."
|
||||
#endif
|
||||
|
||||
// This implementation is inspired by implementation of getcontext in glibc.
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
#include <asm/asm.h>
|
||||
#include <asm/regdef.h>
|
||||
#include <asm/fpregdef.h>
|
||||
#else
|
||||
#include <machine/asm.h>
|
||||
#include <machine/regdef.h>
|
||||
#endif
|
||||
|
||||
// from asm/asm.h
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
#define ALSZ 7
|
||||
#define ALMASK ~7
|
||||
#define SZREG 4
|
||||
#else // _MIPS_SIM != _ABIO32
|
||||
#define ALSZ 15
|
||||
#define ALMASK ~15
|
||||
#define SZREG 8
|
||||
#endif
|
||||
|
||||
#include <asm/unistd.h> // for __NR_rt_sigprocmask
|
||||
|
||||
#define _NSIG8 128 / 8
|
||||
@ -244,12 +257,14 @@ breakpad_getcontext:
|
||||
|
||||
|
||||
.text
|
||||
LOCALS_NUM = 2 // save gp and ra on stack
|
||||
LOCALS_NUM = 1 // save gp on stack
|
||||
FRAME_SIZE = ((LOCALS_NUM * SZREG) + ALSZ) & ALMASK
|
||||
RA_FRAME_OFFSET = FRAME_SIZE - (1 * SZREG)
|
||||
GP_FRAME_OFFSET = FRAME_SIZE - (2 * SZREG)
|
||||
|
||||
GP_FRAME_OFFSET = FRAME_SIZE - (1 * SZREG)
|
||||
MCONTEXT_REG_SIZE = 8
|
||||
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
|
||||
NESTED (breakpad_getcontext, FRAME_SIZE, ra)
|
||||
.mask 0x00000000, 0
|
||||
.fmask 0x00000000, 0
|
||||
@ -262,8 +277,7 @@ NESTED (breakpad_getcontext, FRAME_SIZE, ra)
|
||||
#define _SP a2
|
||||
|
||||
addiu sp, -FRAME_SIZE
|
||||
sw ra, RA_FRAME_OFFSET(sp)
|
||||
sw gp, GP_FRAME_OFFSET(sp)
|
||||
.cprestore GP_FRAME_OFFSET
|
||||
|
||||
sw s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sw s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
@ -298,12 +312,95 @@ NESTED (breakpad_getcontext, FRAME_SIZE, ra)
|
||||
li v0, __NR_rt_sigprocmask
|
||||
syscall
|
||||
|
||||
lw ra, RA_FRAME_OFFSET(sp)
|
||||
lw gp, GP_FRAME_OFFSET(sp)
|
||||
addiu sp, FRAME_SIZE
|
||||
jr ra
|
||||
|
||||
END (breakpad_getcontext)
|
||||
#else
|
||||
|
||||
#ifndef NESTED
|
||||
/*
|
||||
* NESTED - declare nested routine entry point
|
||||
*/
|
||||
#define NESTED(symbol, framesize, rpc) \
|
||||
.globl symbol; \
|
||||
.align 2; \
|
||||
.type symbol,@function; \
|
||||
.ent symbol,0; \
|
||||
symbol: .frame sp, framesize, rpc;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* END - mark end of function
|
||||
*/
|
||||
#ifndef END
|
||||
# define END(function) \
|
||||
.end function; \
|
||||
.size function,.-function
|
||||
#endif
|
||||
|
||||
/* int getcontext (ucontext_t *ucp) */
|
||||
|
||||
NESTED (breakpad_getcontext, FRAME_SIZE, ra)
|
||||
.mask 0x10000000, 0
|
||||
.fmask 0x00000000, 0
|
||||
|
||||
move a2, sp
|
||||
#define _SP a2
|
||||
move a3, gp
|
||||
#define _GP a3
|
||||
|
||||
daddiu sp, -FRAME_SIZE
|
||||
.cpsetup $25, GP_FRAME_OFFSET, breakpad_getcontext
|
||||
|
||||
/* Store a magic flag. */
|
||||
li v1, 1
|
||||
sd v1, (0 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0) /* zero */
|
||||
|
||||
sd s0, (16 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s1, (17 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s2, (18 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s3, (19 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s4, (20 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s5, (21 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s6, (22 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s7, (23 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd _GP, (28 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd _SP, (29 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd s8, (30 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd ra, (31 * MCONTEXT_REG_SIZE + MCONTEXT_GREGS_OFFSET)(a0)
|
||||
sd ra, MCONTEXT_PC_OFFSET(a0)
|
||||
|
||||
#ifdef __mips_hard_float
|
||||
s.d $f24, (24 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
s.d $f25, (25 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
s.d $f26, (26 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
s.d $f27, (27 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
s.d $f28, (28 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
s.d $f29, (29 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
s.d $f30, (30 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
s.d $f31, (31 * MCONTEXT_REG_SIZE + MCONTEXT_FPREGS_OFFSET)(a0)
|
||||
|
||||
cfc1 v1, $31
|
||||
sw v1, MCONTEXT_FPC_CSR(a0)
|
||||
#endif /* __mips_hard_float */
|
||||
|
||||
/* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
|
||||
li a3, _NSIG8
|
||||
daddu a2, a0, UCONTEXT_SIGMASK_OFFSET
|
||||
move a1, zero
|
||||
li a0, SIG_BLOCK
|
||||
|
||||
li v0, __NR_rt_sigprocmask
|
||||
syscall
|
||||
|
||||
.cpreturn
|
||||
daddiu sp, FRAME_SIZE
|
||||
move v0, zero
|
||||
jr ra
|
||||
|
||||
END (breakpad_getcontext)
|
||||
#endif // _MIPS_SIM == _ABIO32
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
/* The x64 implementation of breakpad_getcontext was derived in part
|
||||
|
@ -36,7 +36,8 @@
|
||||
|
||||
// TODO(rmcilroy): Remove this file once the ndk is updated for other
|
||||
// architectures - crbug.com/358831
|
||||
#if !defined(__aarch64__) && !defined(__x86_64__)
|
||||
#if !defined(__aarch64__) && !defined(__x86_64__) && \
|
||||
!(defined(__mips__) && _MIPS_SIM == _ABI64)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -38,6 +38,9 @@
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
#include <sys/cdefs.h>
|
||||
#if defined (__mips__)
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#include <sys/user.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
@ -33,85 +33,12 @@
|
||||
// The purpose of this file is to glue the mismatching headers (Android NDK vs
|
||||
// glibc) and therefore avoid doing otherwise awkward #ifdefs in the code.
|
||||
// The following quirks are currently handled by this file:
|
||||
// - MIPS: Keep using forked definitions of user.h structs. The definition in
|
||||
// the NDK is completely different.
|
||||
// Internal bug b/18097715
|
||||
// - i386: Use the Android NDK but alias user_fxsr_struct > user_fpxregs_struct.
|
||||
// - aarch64: Add missing user_regs_struct and user_fpsimd_struct structs.
|
||||
// - Other platforms: Just use the Android NDK unchanged.
|
||||
|
||||
#ifdef __mips__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
#define EF_REG0 6
|
||||
#define EF_REG1 7
|
||||
#define EF_REG2 8
|
||||
#define EF_REG3 9
|
||||
#define EF_REG4 10
|
||||
#define EF_REG5 11
|
||||
#define EF_REG6 12
|
||||
#define EF_REG7 13
|
||||
#define EF_REG8 14
|
||||
#define EF_REG9 15
|
||||
#define EF_REG10 16
|
||||
#define EF_REG11 17
|
||||
#define EF_REG12 18
|
||||
#define EF_REG13 19
|
||||
#define EF_REG14 20
|
||||
#define EF_REG15 21
|
||||
#define EF_REG16 22
|
||||
#define EF_REG17 23
|
||||
#define EF_REG18 24
|
||||
#define EF_REG19 25
|
||||
#define EF_REG20 26
|
||||
#define EF_REG21 27
|
||||
#define EF_REG22 28
|
||||
#define EF_REG23 29
|
||||
#define EF_REG24 30
|
||||
#define EF_REG25 31
|
||||
|
||||
/*
|
||||
* k0/k1 unsaved
|
||||
*/
|
||||
#define EF_REG26 32
|
||||
#define EF_REG27 33
|
||||
#define EF_REG28 34
|
||||
#define EF_REG29 35
|
||||
#define EF_REG30 36
|
||||
#define EF_REG31 37
|
||||
|
||||
/*
|
||||
* Saved special registers
|
||||
*/
|
||||
#define EF_LO 38
|
||||
#define EF_HI 39
|
||||
#define EF_CP0_EPC 40
|
||||
#define EF_CP0_BADVADDR 41
|
||||
#define EF_CP0_STATUS 42
|
||||
#define EF_CP0_CAUSE 43
|
||||
|
||||
struct user_regs_struct {
|
||||
unsigned long long regs[32];
|
||||
unsigned long long lo;
|
||||
unsigned long long hi;
|
||||
unsigned long long epc;
|
||||
unsigned long long badvaddr;
|
||||
unsigned long long status;
|
||||
unsigned long long cause;
|
||||
};
|
||||
|
||||
struct user_fpregs_struct {
|
||||
unsigned long long regs[32];
|
||||
unsigned int fpcsr;
|
||||
unsigned int fir;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
|
||||
#else // __mips__
|
||||
// TODO(primiano): remove these changes after Chromium has stably rolled to
|
||||
// an NDK with the appropriate fixes.
|
||||
|
||||
#include_next <sys/user.h>
|
||||
|
||||
@ -125,6 +52,24 @@ typedef struct user_fxsr_struct user_fpxregs_struct;
|
||||
#endif // __cplusplus
|
||||
#endif // __i386__
|
||||
|
||||
#endif // __mips__
|
||||
#ifdef __aarch64__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
struct user_regs_struct {
|
||||
__u64 regs[31];
|
||||
__u64 sp;
|
||||
__u64 pc;
|
||||
__u64 pstate;
|
||||
};
|
||||
struct user_fpsimd_struct {
|
||||
__uint128_t vregs[32];
|
||||
__u32 fpsr;
|
||||
__u32 fpcr;
|
||||
};
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
#endif // __aarch64__
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H
|
||||
|
@ -38,6 +38,9 @@
|
||||
|
||||
#include_next <wchar.h>
|
||||
|
||||
#if !defined(__aarch64__) && !defined(__x86_64__) && \
|
||||
!(defined(__mips__) && _MIPS_SIM == _ABI64)
|
||||
|
||||
// This needs to be in an extern "C" namespace, or Googletest will not
|
||||
// compile against it.
|
||||
#ifdef __cplusplus
|
||||
@ -68,5 +71,6 @@ static int inline wcscasecmp(const wchar_t* s1, const wchar_t* s2) {
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
#endif
|
||||
|
||||
#endif // GOOGLEBREAKPAD_COMMON_ANDROID_INCLUDE_WCHAR_H
|
||||
|
@ -97,11 +97,19 @@
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
#if _MIPS_SIM == _ABIO32
|
||||
#define MCONTEXT_PC_OFFSET 32
|
||||
#define MCONTEXT_GREGS_OFFSET 40
|
||||
#define MCONTEXT_FPREGS_OFFSET 296
|
||||
#define MCONTEXT_FPC_CSR 556
|
||||
#define UCONTEXT_SIGMASK_OFFSET 616
|
||||
#else
|
||||
#define MCONTEXT_GREGS_OFFSET 40
|
||||
#define MCONTEXT_FPREGS_OFFSET 296
|
||||
#define MCONTEXT_PC_OFFSET 616
|
||||
#define MCONTEXT_FPC_CSR 624
|
||||
#define UCONTEXT_SIGMASK_OFFSET 640
|
||||
#endif
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
|
@ -120,7 +120,7 @@
|
||||
'mac/bootstrap_compat.h',
|
||||
'mac/byteswap.h',
|
||||
'mac/dump_syms.h',
|
||||
'mac/dump_syms.mm',
|
||||
'mac/dump_syms.cc',
|
||||
'mac/file_id.cc',
|
||||
'mac/file_id.h',
|
||||
'mac/GTMDefines.h',
|
||||
@ -141,6 +141,7 @@
|
||||
'mac/scoped_task_suspend-inl.h',
|
||||
'mac/string_utilities.cc',
|
||||
'mac/string_utilities.h',
|
||||
'mac/super_fat_arch.h',
|
||||
'md5.cc',
|
||||
'md5.h',
|
||||
'memory.h',
|
||||
|
@ -1,23 +1,39 @@
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
* Copyright © 1991-2015 Unicode, Inc. All rights reserved.
|
||||
* Distributed under the Terms of Use in
|
||||
* http://www.unicode.org/copyright.html.
|
||||
*
|
||||
* Disclaimer
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of the Unicode data files and any associated documentation
|
||||
* (the "Data Files") or Unicode software and any associated documentation
|
||||
* (the "Software") to deal in the Data Files or Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, and/or sell copies of
|
||||
* the Data Files or Software, and to permit persons to whom the Data Files
|
||||
* or Software are furnished to do so, provided that
|
||||
* (a) this copyright and permission notice appear with all copies
|
||||
* of the Data Files or Software,
|
||||
* (b) this copyright and permission notice appear in associated
|
||||
* documentation, and
|
||||
* (c) there is clear notice in each modified Data File or in the Software
|
||||
* as well as in the documentation associated with the Data File(s) or
|
||||
* Software that the data or software has been modified.
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
* THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
* ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
|
||||
* NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
|
||||
* DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THE DATA FILES OR SOFTWARE.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
* Except as contained in this notice, the name of a copyright holder
|
||||
* shall not be used in advertising or otherwise to promote the sale,
|
||||
* use or other dealings in these Data Files or Software without prior
|
||||
* written authorization of the copyright holder.
|
||||
*/
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
@ -1,23 +1,39 @@
|
||||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
* Copyright © 1991-2015 Unicode, Inc. All rights reserved.
|
||||
* Distributed under the Terms of Use in
|
||||
* http://www.unicode.org/copyright.html.
|
||||
*
|
||||
* Disclaimer
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of the Unicode data files and any associated documentation
|
||||
* (the "Data Files") or Unicode software and any associated documentation
|
||||
* (the "Software") to deal in the Data Files or Software
|
||||
* without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, and/or sell copies of
|
||||
* the Data Files or Software, and to permit persons to whom the Data Files
|
||||
* or Software are furnished to do so, provided that
|
||||
* (a) this copyright and permission notice appear with all copies
|
||||
* of the Data Files or Software,
|
||||
* (b) this copyright and permission notice appear in associated
|
||||
* documentation, and
|
||||
* (c) there is clear notice in each modified Data File or in the Software
|
||||
* as well as in the documentation associated with the Data File(s) or
|
||||
* Software that the data or software has been modified.
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
* THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
* ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS
|
||||
* NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL
|
||||
* DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
|
||||
* DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
* PERFORMANCE OF THE DATA FILES OR SOFTWARE.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
* Except as contained in this notice, the name of a copyright holder
|
||||
* shall not be used in advertising or otherwise to promote the sale,
|
||||
* use or other dealings in these Data Files or Software without prior
|
||||
* written authorization of the copyright holder.
|
||||
*/
|
||||
|
||||
#ifndef COMMON_CONVERT_UTF_H_
|
||||
|
@ -183,14 +183,15 @@ const char* CompilationUnit::SkipAttribute(const char* start,
|
||||
case DW_FORM_addr:
|
||||
return start + reader_->AddressSize();
|
||||
case DW_FORM_ref_addr:
|
||||
// DWARF2 and 3 differ on whether ref_addr is address size or
|
||||
// DWARF2 and 3/4 differ on whether ref_addr is address size or
|
||||
// offset size.
|
||||
assert(header_.version == 2 || header_.version == 3);
|
||||
assert(header_.version >= 2);
|
||||
if (header_.version == 2) {
|
||||
return start + reader_->AddressSize();
|
||||
} else if (header_.version == 3) {
|
||||
} else if (header_.version >= 3) {
|
||||
return start + reader_->OffsetSize();
|
||||
}
|
||||
break;
|
||||
|
||||
case DW_FORM_block1:
|
||||
return start + 1 + reader_->ReadOneByte(start);
|
||||
@ -390,14 +391,14 @@ const char* CompilationUnit::ProcessAttribute(
|
||||
+ offset_from_section_start_);
|
||||
return start + len;
|
||||
case DW_FORM_ref_addr:
|
||||
// DWARF2 and 3 differ on whether ref_addr is address size or
|
||||
// DWARF2 and 3/4 differ on whether ref_addr is address size or
|
||||
// offset size.
|
||||
assert(header_.version == 2 || header_.version == 3);
|
||||
assert(header_.version >= 2);
|
||||
if (header_.version == 2) {
|
||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||
reader_->ReadAddress(start));
|
||||
return start + reader_->AddressSize();
|
||||
} else if (header_.version == 3) {
|
||||
} else if (header_.version >= 3) {
|
||||
handler_->ProcessAttributeReference(dieoffset, attr, form,
|
||||
reader_->ReadOffset(start));
|
||||
return start + reader_->OffsetSize();
|
||||
@ -516,8 +517,10 @@ void CompilationUnit::ProcessDIEs() {
|
||||
|
||||
LineInfo::LineInfo(const char* buffer, uint64 buffer_length,
|
||||
ByteReader* reader, LineInfoHandler* handler):
|
||||
handler_(handler), reader_(reader), buffer_(buffer),
|
||||
buffer_length_(buffer_length) {
|
||||
handler_(handler), reader_(reader), buffer_(buffer) {
|
||||
#ifndef NDEBUG
|
||||
buffer_length_ = buffer_length;
|
||||
#endif
|
||||
header_.std_opcode_lengths = NULL;
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,9 @@ class LineInfo {
|
||||
// the line info to read is. after_header is the place right after
|
||||
// the end of the line information header.
|
||||
const char* buffer_;
|
||||
#ifndef NDEBUG
|
||||
uint64 buffer_length_;
|
||||
#endif
|
||||
const char* after_header_;
|
||||
};
|
||||
|
||||
|
@ -351,9 +351,15 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||
break;
|
||||
case dwarf2reader::DW_AT_MIPS_linkage_name: {
|
||||
char* demangled = NULL;
|
||||
#if !defined(__ANDROID__)
|
||||
demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, NULL);
|
||||
int status = -1;
|
||||
#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle.
|
||||
demangled = abi::__cxa_demangle(data.c_str(), NULL, NULL, &status);
|
||||
#endif
|
||||
if (status != 0) {
|
||||
cu_context_->reporter->DemangleError(data, status);
|
||||
demangled_name_ = "";
|
||||
break;
|
||||
}
|
||||
if (demangled) {
|
||||
demangled_name_ = AddStringToPool(demangled);
|
||||
free(reinterpret_cast<void*>(demangled));
|
||||
@ -396,6 +402,18 @@ string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
||||
enclosing_name = &parent_context_->name;
|
||||
}
|
||||
|
||||
// Prepare the return value before upcoming mutations possibly invalidate the
|
||||
// existing pointers.
|
||||
string return_value;
|
||||
if (qualified_name) {
|
||||
return_value = *qualified_name;
|
||||
} else {
|
||||
// Combine the enclosing name and unqualified name to produce our
|
||||
// own fully-qualified name.
|
||||
return_value = cu_context_->language->MakeQualifiedName(*enclosing_name,
|
||||
*unqualified_name);
|
||||
}
|
||||
|
||||
// If this DIE was marked as a declaration, record its names in the
|
||||
// specification table.
|
||||
if (declaration_) {
|
||||
@ -409,13 +427,7 @@ string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
||||
cu_context_->file_context->file_private_->specifications[offset_] = spec;
|
||||
}
|
||||
|
||||
if (qualified_name)
|
||||
return *qualified_name;
|
||||
|
||||
// Combine the enclosing name and unqualified name to produce our
|
||||
// own fully-qualified name.
|
||||
return cu_context_->language->MakeQualifiedName(*enclosing_name,
|
||||
*unqualified_name);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
// A handler class for DW_TAG_subprogram DIEs.
|
||||
@ -528,18 +540,19 @@ void DwarfCUToModule::FuncHandler::Finish() {
|
||||
// functions that were never used), but all the ones we're
|
||||
// interested in cover a non-empty range of bytes.
|
||||
if (low_pc_ < high_pc_) {
|
||||
// Create a Module::Function based on the data we've gathered, and
|
||||
// add it to the functions_ list.
|
||||
scoped_ptr<Module::Function> func(new Module::Function);
|
||||
// Malformed DWARF may omit the name, but all Module::Functions must
|
||||
// have names.
|
||||
string name;
|
||||
if (!name_.empty()) {
|
||||
func->name = name_;
|
||||
name = name_;
|
||||
} else {
|
||||
cu_context_->reporter->UnnamedFunction(offset_);
|
||||
func->name = "<name omitted>";
|
||||
name = "<name omitted>";
|
||||
}
|
||||
func->address = low_pc_;
|
||||
|
||||
// Create a Module::Function based on the data we've gathered, and
|
||||
// add it to the functions_ list.
|
||||
scoped_ptr<Module::Function> func(new Module::Function(name, low_pc_));
|
||||
func->size = high_pc_ - low_pc_;
|
||||
func->parameter_size = 0;
|
||||
if (func->address) {
|
||||
@ -661,6 +674,13 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
|
||||
filename_.c_str(), offset);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::DemangleError(
|
||||
const string &input, int error) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: failed to demangle %s with error %d\n",
|
||||
filename_.c_str(), input.c_str(), error);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnhandledInterCUReference(
|
||||
uint64 offset, uint64 target) {
|
||||
CUHeading();
|
||||
|
@ -199,6 +199,9 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||
// link.
|
||||
virtual void UnnamedFunction(uint64 offset);
|
||||
|
||||
// __cxa_demangle() failed to demangle INPUT.
|
||||
virtual void DemangleError(const string &input, int error);
|
||||
|
||||
// The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
|
||||
// FilePrivate did not retain the inter-CU specification data.
|
||||
virtual void UnhandledInterCUReference(uint64 offset, uint64 target);
|
||||
|
@ -81,6 +81,7 @@ class MockWarningReporter: public DwarfCUToModule::WarningReporter {
|
||||
MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function));
|
||||
MOCK_METHOD1(UncoveredLine, void(const Module::Line &line));
|
||||
MOCK_METHOD1(UnnamedFunction, void(uint64 offset));
|
||||
MOCK_METHOD2(DemangleError, void(const string &input, int error));
|
||||
MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target));
|
||||
};
|
||||
|
||||
@ -1712,16 +1713,14 @@ TEST_F(CUErrors, BadCURootDIETag) {
|
||||
// produce) output, so their results need to be checked by hand.
|
||||
struct Reporter: public Test {
|
||||
Reporter()
|
||||
: reporter("filename", 0x123456789abcdef0ULL) {
|
||||
: reporter("filename", 0x123456789abcdef0ULL),
|
||||
function("function name", 0x19c45c30770c1eb0ULL),
|
||||
file("source file name") {
|
||||
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;
|
||||
|
@ -95,6 +95,15 @@ using google_breakpad::scoped_ptr;
|
||||
#define EM_AARCH64 183
|
||||
#endif
|
||||
|
||||
// Define SHT_ANDROID_REL and SHT_ANDROID_RELA if not defined by the host.
|
||||
// Sections with this type contain Android packed relocations.
|
||||
#ifndef SHT_ANDROID_REL
|
||||
#define SHT_ANDROID_REL (SHT_LOOS + 1)
|
||||
#endif
|
||||
#ifndef SHT_ANDROID_RELA
|
||||
#define SHT_ANDROID_RELA (SHT_LOOS + 2)
|
||||
#endif
|
||||
|
||||
//
|
||||
// FDWrapper
|
||||
//
|
||||
@ -413,6 +422,15 @@ bool ElfEndianness(const typename ElfClass::Ehdr* elf_header,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Given |left_abspath|, find the absolute path for |right_path| and see if the
|
||||
// two absolute paths are the same.
|
||||
bool IsSameFile(const char* left_abspath, const string& right_path) {
|
||||
char right_abspath[PATH_MAX];
|
||||
if (!realpath(right_path.c_str(), right_abspath))
|
||||
return false;
|
||||
return strcmp(left_abspath, right_abspath) == 0;
|
||||
}
|
||||
|
||||
// Read the .gnu_debuglink and get the debug file name. If anything goes
|
||||
// wrong, return an empty string.
|
||||
string ReadDebugLink(const char* debuglink,
|
||||
@ -430,14 +448,27 @@ string ReadDebugLink(const char* debuglink,
|
||||
return string();
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
int debuglink_fd = -1;
|
||||
char obj_file_abspath[PATH_MAX];
|
||||
if (!realpath(obj_file.c_str(), obj_file_abspath)) {
|
||||
fprintf(stderr, "Cannot resolve absolute path for %s\n", obj_file.c_str());
|
||||
return string();
|
||||
}
|
||||
|
||||
std::vector<string> searched_paths;
|
||||
string debuglink_path;
|
||||
std::vector<string>::const_iterator it;
|
||||
for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) {
|
||||
const string& debug_dir = *it;
|
||||
debuglink_path = debug_dir + "/" + debuglink;
|
||||
debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
|
||||
|
||||
// There is the annoying case of /path/to/foo.so having foo.so as the
|
||||
// debug link file name. Thus this may end up opening /path/to/foo.so again,
|
||||
// and there is a small chance of the two files having the same CRC.
|
||||
if (IsSameFile(obj_file_abspath, debuglink_path))
|
||||
continue;
|
||||
|
||||
searched_paths.push_back(debug_dir);
|
||||
int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
|
||||
if (debuglink_fd < 0)
|
||||
continue;
|
||||
|
||||
@ -469,23 +500,21 @@ string ReadDebugLink(const char* debuglink,
|
||||
debuglink_path.c_str());
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
break;
|
||||
|
||||
// Found debug file.
|
||||
return debuglink_path;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// Not found case.
|
||||
fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n",
|
||||
obj_file.c_str());
|
||||
for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) {
|
||||
const string debug_dir = *it;
|
||||
for (it = searched_paths.begin(); it < searched_paths.end(); ++it) {
|
||||
const string& debug_dir = *it;
|
||||
fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink);
|
||||
}
|
||||
return string();
|
||||
}
|
||||
|
||||
return debuglink_path;
|
||||
}
|
||||
|
||||
//
|
||||
// LoadSymbolsInfo
|
||||
//
|
||||
@ -591,6 +620,28 @@ bool LoadSymbols(const string& obj_file,
|
||||
bool found_debug_info_section = false;
|
||||
bool found_usable_info = false;
|
||||
|
||||
// Reject files that contain Android packed relocations. The pre-packed
|
||||
// version of the file should be symbolized; the packed version is only
|
||||
// intended for use on the target system.
|
||||
if (FindElfSectionByName<ElfClass>(".rel.dyn", SHT_ANDROID_REL,
|
||||
sections, names,
|
||||
names_end, elf_header->e_shnum)) {
|
||||
fprintf(stderr, "%s: file contains a \".rel.dyn\" section "
|
||||
"with type SHT_ANDROID_REL\n", obj_file.c_str());
|
||||
fprintf(stderr, "Files containing Android packed relocations "
|
||||
"may not be symbolized.\n");
|
||||
return false;
|
||||
}
|
||||
if (FindElfSectionByName<ElfClass>(".rela.dyn", SHT_ANDROID_RELA,
|
||||
sections, names,
|
||||
names_end, elf_header->e_shnum)) {
|
||||
fprintf(stderr, "%s: file contains a \".rela.dyn\" section "
|
||||
"with type SHT_ANDROID_RELA\n", obj_file.c_str());
|
||||
fprintf(stderr, "Files containing Android packed relocations "
|
||||
"may not be symbolized.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (options.symbol_data != ONLY_CFI) {
|
||||
#ifndef NO_STABS_SUPPORT
|
||||
// Look for STABS debugging information, and load it if present.
|
||||
@ -628,6 +679,35 @@ bool LoadSymbols(const string& obj_file,
|
||||
"DWARF debugging information\n", obj_file.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// See if there are export symbols available.
|
||||
const Shdr* dynsym_section =
|
||||
FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
const Shdr* dynstr_section =
|
||||
FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
if (dynsym_section && dynstr_section) {
|
||||
info->LoadedSection(".dynsym");
|
||||
|
||||
const uint8_t* dynsyms =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header,
|
||||
dynsym_section->sh_offset);
|
||||
const uint8_t* dynstrs =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header,
|
||||
dynstr_section->sh_offset);
|
||||
bool result =
|
||||
ELFSymbolsToModule(dynsyms,
|
||||
dynsym_section->sh_size,
|
||||
dynstrs,
|
||||
dynstr_section->sh_size,
|
||||
big_endian,
|
||||
ElfClass::kAddrSize,
|
||||
module);
|
||||
found_usable_info = found_usable_info || result;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.symbol_data != NO_CFI) {
|
||||
@ -689,8 +769,6 @@ bool LoadSymbols(const string& obj_file,
|
||||
names_end, elf_header->e_shnum);
|
||||
if (gnu_debuglink_section) {
|
||||
if (!info->debug_dirs().empty()) {
|
||||
found_debug_info_section = true;
|
||||
|
||||
const char* debuglink_contents =
|
||||
GetOffset<ElfClass, char>(elf_header,
|
||||
gnu_debuglink_section->sh_offset);
|
||||
@ -709,47 +787,20 @@ bool LoadSymbols(const string& obj_file,
|
||||
fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n",
|
||||
obj_file.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.symbol_data != ONLY_CFI) {
|
||||
const Shdr* dynsym_section =
|
||||
FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
const Shdr* dynstr_section =
|
||||
FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB,
|
||||
sections, names, names_end,
|
||||
elf_header->e_shnum);
|
||||
if (dynsym_section && dynstr_section) {
|
||||
info->LoadedSection(".dynsym");
|
||||
|
||||
const uint8_t* dynsyms =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header,
|
||||
dynsym_section->sh_offset);
|
||||
const uint8_t* dynstrs =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header,
|
||||
dynstr_section->sh_offset);
|
||||
bool result =
|
||||
ELFSymbolsToModule(dynsyms,
|
||||
dynsym_section->sh_size,
|
||||
dynstrs,
|
||||
dynstr_section->sh_size,
|
||||
big_endian,
|
||||
ElfClass::kAddrSize,
|
||||
module);
|
||||
found_usable_info = found_usable_info || result;
|
||||
}
|
||||
}
|
||||
|
||||
if (read_gnu_debug_link) {
|
||||
return found_debug_info_section;
|
||||
}
|
||||
|
||||
// Return true if some usable information was found
|
||||
} else {
|
||||
// Return true if some usable information was found, since the caller
|
||||
// doesn't want to use .gnu_debuglink.
|
||||
return found_usable_info;
|
||||
}
|
||||
|
||||
// No debug info was found, let the user try again with .gnu_debuglink
|
||||
// if present.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the breakpad symbol file identifier for the architecture of
|
||||
// ELF_HEADER.
|
||||
template<typename ElfClass>
|
||||
@ -800,6 +851,37 @@ string BaseFileName(const string &filename) {
|
||||
return base;
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
bool SanitizeDebugFile(const typename ElfClass::Ehdr* debug_elf_header,
|
||||
const string& debuglink_file,
|
||||
const string& obj_filename,
|
||||
const char* obj_file_architecture,
|
||||
const bool obj_file_is_big_endian) {
|
||||
const char* debug_architecture =
|
||||
ElfArchitecture<ElfClass>(debug_elf_header);
|
||||
if (!debug_architecture) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
|
||||
debuglink_file.c_str(), debug_elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
if (strcmp(obj_file_architecture, debug_architecture)) {
|
||||
fprintf(stderr, "%s with ELF machine architecture %s does not match "
|
||||
"%s with ELF architecture %s\n",
|
||||
debuglink_file.c_str(), debug_architecture,
|
||||
obj_filename.c_str(), obj_file_architecture);
|
||||
return false;
|
||||
}
|
||||
bool debug_big_endian;
|
||||
if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian))
|
||||
return false;
|
||||
if (debug_big_endian != obj_file_is_big_endian) {
|
||||
fprintf(stderr, "%s and %s does not match in endianness\n",
|
||||
obj_filename.c_str(), debuglink_file.c_str());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
|
||||
const string& obj_filename,
|
||||
@ -807,7 +889,6 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
|
||||
const DumpOptions& options,
|
||||
Module** out_module) {
|
||||
typedef typename ElfClass::Ehdr Ehdr;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
|
||||
*out_module = NULL;
|
||||
|
||||
@ -849,34 +930,13 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
|
||||
MmapWrapper debug_map_wrapper;
|
||||
Ehdr* debug_elf_header = NULL;
|
||||
if (!LoadELF(debuglink_file, &debug_map_wrapper,
|
||||
reinterpret_cast<void**>(&debug_elf_header)))
|
||||
return false;
|
||||
// Sanity checks to make sure everything matches up.
|
||||
const char *debug_architecture =
|
||||
ElfArchitecture<ElfClass>(debug_elf_header);
|
||||
if (!debug_architecture) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
|
||||
debuglink_file.c_str(), debug_elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
if (strcmp(architecture, debug_architecture)) {
|
||||
fprintf(stderr, "%s with ELF machine architecture %s does not match "
|
||||
"%s with ELF architecture %s\n",
|
||||
debuglink_file.c_str(), debug_architecture,
|
||||
obj_filename.c_str(), architecture);
|
||||
reinterpret_cast<void**>(&debug_elf_header)) ||
|
||||
!SanitizeDebugFile<ElfClass>(debug_elf_header, debuglink_file,
|
||||
obj_filename, architecture, big_endian)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool debug_big_endian;
|
||||
if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian))
|
||||
return false;
|
||||
if (debug_big_endian != big_endian) {
|
||||
fprintf(stderr, "%s and %s does not match in endianness\n",
|
||||
obj_filename.c_str(), debuglink_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian,
|
||||
if (!LoadSymbols<ElfClass>(debuglink_file, big_endian,
|
||||
debug_elf_header, false, &info,
|
||||
options, module.get())) {
|
||||
return false;
|
||||
|
@ -37,7 +37,7 @@
|
||||
//
|
||||
|
||||
#define HANDLE_EINTR(x) ({ \
|
||||
typeof(x) eintr_wrapper_result; \
|
||||
__typeof__(x) eintr_wrapper_result; \
|
||||
do { \
|
||||
eintr_wrapper_result = (x); \
|
||||
} while (eintr_wrapper_result == -1 && errno == EINTR); \
|
||||
@ -45,7 +45,7 @@
|
||||
})
|
||||
|
||||
#define IGNORE_EINTR(x) ({ \
|
||||
typeof(x) eintr_wrapper_result; \
|
||||
__typeof__(x) eintr_wrapper_result; \
|
||||
do { \
|
||||
eintr_wrapper_result = (x); \
|
||||
if (eintr_wrapper_result == -1 && errno == EINTR) { \
|
||||
|
@ -32,6 +32,7 @@
|
||||
|
||||
#include "common/linux/elf_symbols_to_module.h"
|
||||
|
||||
#include <cxxabi.h>
|
||||
#include <elf.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -155,9 +156,18 @@ bool ELFSymbolsToModule(const uint8_t *symtab_section,
|
||||
while(!iterator->at_end) {
|
||||
if (ELF32_ST_TYPE(iterator->info) == STT_FUNC &&
|
||||
iterator->shndx != SHN_UNDEF) {
|
||||
Module::Extern *ext = new Module::Extern;
|
||||
Module::Extern *ext = new Module::Extern(iterator->value);
|
||||
ext->name = SymbolString(iterator->name_offset, strings);
|
||||
ext->address = iterator->value;
|
||||
#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle.
|
||||
int status = 0;
|
||||
char* demangled =
|
||||
abi::__cxa_demangle(ext->name.c_str(), NULL, NULL, &status);
|
||||
if (demangled) {
|
||||
if (status == 0)
|
||||
ext->name = demangled;
|
||||
free(demangled);
|
||||
}
|
||||
#endif
|
||||
module->AddExtern(ext);
|
||||
}
|
||||
++iterator;
|
||||
|
@ -56,8 +56,7 @@ static const char kUserAgent[] = "Breakpad/1.0 (Linux)";
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const string &url,
|
||||
const map<string, string> ¶meters,
|
||||
const string &upload_file,
|
||||
const string &file_part_name,
|
||||
const map<string, string> &files,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
const string &ca_certificate_file,
|
||||
@ -135,11 +134,13 @@ bool HTTPUpload::SendRequest(const string &url,
|
||||
CURLFORM_COPYCONTENTS, iter->second.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
// Add form file.
|
||||
// Add form files.
|
||||
for (iter = files.begin(); iter != files.end(); ++iter) {
|
||||
(*curl_formadd)(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, file_part_name.c_str(),
|
||||
CURLFORM_FILE, upload_file.c_str(),
|
||||
CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
CURLFORM_FILE, iter->second.c_str(),
|
||||
CURLFORM_END);
|
||||
}
|
||||
|
||||
(*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost);
|
||||
|
||||
|
@ -45,9 +45,9 @@ using std::map;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
// Sends the given set of parameters, along with the contents of
|
||||
// upload_file, as a multipart POST request to the given URL.
|
||||
// file_part_name contains the name of the file part of the request
|
||||
// Sends the given sets of parameters and files as a multipart POST
|
||||
// request to the given URL.
|
||||
// Each key in |files| is the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
@ -60,8 +60,7 @@ class HTTPUpload {
|
||||
// returned in error_description.
|
||||
static bool SendRequest(const string &url,
|
||||
const map<string, string> ¶meters,
|
||||
const string &upload_file,
|
||||
const string &file_part_name,
|
||||
const map<string, string> &files,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
const string &ca_certificate_file,
|
||||
|
@ -43,7 +43,7 @@ namespace google_breakpad {
|
||||
class LibcurlWrapper {
|
||||
public:
|
||||
LibcurlWrapper();
|
||||
~LibcurlWrapper();
|
||||
virtual ~LibcurlWrapper();
|
||||
virtual bool Init();
|
||||
virtual bool SetProxy(const string& proxy_host,
|
||||
const string& proxy_userpwd);
|
||||
|
@ -54,6 +54,8 @@ MemoryMappedFile::~MemoryMappedFile() {
|
||||
Unmap();
|
||||
}
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
bool MemoryMappedFile::Map(const char* path, size_t offset) {
|
||||
Unmap();
|
||||
|
||||
@ -62,7 +64,9 @@ bool MemoryMappedFile::Map(const char* path, size_t offset) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
#if defined(__x86_64__) || defined(__aarch64__) || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABI64)
|
||||
|
||||
struct kernel_stat st;
|
||||
if (sys_fstat(fd, &st) == -1 || st.st_size < 0) {
|
||||
#else
|
||||
@ -83,7 +87,8 @@ bool MemoryMappedFile::Map(const char* path, size_t offset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(__aarch64__)
|
||||
#if defined(__x86_64__) || defined(__aarch64__) || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABI64)
|
||||
void* data = sys_mmap(NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset);
|
||||
#else
|
||||
if ((offset & 4095) != 0) {
|
||||
|
@ -213,8 +213,10 @@ void ELF::Finish() {
|
||||
SymbolTable::SymbolTable(Endianness endianness,
|
||||
size_t addr_size,
|
||||
StringTable& table) : Section(endianness),
|
||||
addr_size_(addr_size),
|
||||
table_(table) {
|
||||
#ifndef NDEBUG
|
||||
addr_size_ = addr_size;
|
||||
#endif
|
||||
assert(addr_size_ == 4 || addr_size_ == 8);
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,9 @@ class SymbolTable : public Section {
|
||||
uint64_t size, unsigned info, uint16_t shndx);
|
||||
|
||||
private:
|
||||
#ifndef NDEBUG
|
||||
size_t addr_size_;
|
||||
#endif
|
||||
StringTable& table_;
|
||||
};
|
||||
|
||||
|
@ -195,10 +195,11 @@
|
||||
if ([[req URL] isFileURL]) {
|
||||
[[req HTTPBody] writeToURL:[req URL] options:0 error:error];
|
||||
} else {
|
||||
NSURLResponse *response = nil;
|
||||
data = [NSURLConnection sendSynchronousRequest:req
|
||||
returningResponse:&response_
|
||||
returningResponse:&response
|
||||
error:error];
|
||||
[response_ retain];
|
||||
response_ = (NSHTTPURLResponse *)[response retain];
|
||||
}
|
||||
[req release];
|
||||
|
||||
|
@ -30,27 +30,20 @@
|
||||
#include "common/mac/arch_utilities.h"
|
||||
|
||||
#include <mach-o/arch.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef CPU_TYPE_ARM
|
||||
#define CPU_TYPE_ARM (static_cast<cpu_type_t>(12))
|
||||
#endif // CPU_TYPE_ARM
|
||||
|
||||
#ifndef CPU_SUBTYPE_ARM_V7
|
||||
#define CPU_SUBTYPE_ARM_V7 (static_cast<cpu_subtype_t>(9))
|
||||
#endif // CPU_SUBTYPE_ARM_V7
|
||||
|
||||
#ifndef CPU_SUBTYPE_ARM_V7S
|
||||
#define CPU_SUBTYPE_ARM_V7S (static_cast<cpu_subtype_t>(11))
|
||||
#endif // CPU_SUBTYPE_ARM_V7S
|
||||
|
||||
#ifndef CPU_TYPE_ARM64
|
||||
#define CPU_TYPE_ARM64 (static_cast<cpu_type_t>(16777228))
|
||||
#define CPU_TYPE_ARM64 (CPU_TYPE_ARM | CPU_ARCH_ABI64)
|
||||
#endif // CPU_TYPE_ARM64
|
||||
|
||||
#ifndef CPU_SUBTYPE_ARM64_ALL
|
||||
#define CPU_SUBTYPE_ARM64_ALL (static_cast<cpu_type_t>(0))
|
||||
#define CPU_SUBTYPE_ARM64_ALL (static_cast<cpu_subtype_t>(0))
|
||||
#endif // CPU_SUBTYPE_ARM64_ALL
|
||||
|
||||
namespace {
|
||||
@ -111,3 +104,108 @@ const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type,
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#ifndef __APPLE__
|
||||
namespace {
|
||||
|
||||
enum Architecture {
|
||||
kArch_i386 = 0,
|
||||
kArch_x86_64,
|
||||
kArch_arm,
|
||||
kArch_arm64,
|
||||
kArch_ppc,
|
||||
// This must be last.
|
||||
kNumArchitectures
|
||||
};
|
||||
|
||||
// enum Architecture above and kKnownArchitectures below
|
||||
// must be kept in sync.
|
||||
const NXArchInfo kKnownArchitectures[] = {
|
||||
{
|
||||
"i386",
|
||||
CPU_TYPE_I386,
|
||||
CPU_SUBTYPE_I386_ALL,
|
||||
NX_LittleEndian,
|
||||
"Intel 80x86"
|
||||
},
|
||||
{
|
||||
"x86_64",
|
||||
CPU_TYPE_X86_64,
|
||||
CPU_SUBTYPE_X86_64_ALL,
|
||||
NX_LittleEndian,
|
||||
"Intel x86-64"
|
||||
},
|
||||
{
|
||||
"arm",
|
||||
CPU_TYPE_ARM,
|
||||
CPU_SUBTYPE_ARM_ALL,
|
||||
NX_LittleEndian,
|
||||
"ARM"
|
||||
},
|
||||
{
|
||||
"arm64",
|
||||
CPU_TYPE_ARM64,
|
||||
CPU_SUBTYPE_ARM64_ALL,
|
||||
NX_LittleEndian,
|
||||
"ARM64"
|
||||
},
|
||||
{
|
||||
"ppc",
|
||||
CPU_TYPE_POWERPC,
|
||||
CPU_SUBTYPE_POWERPC_ALL,
|
||||
NX_BigEndian,
|
||||
"PowerPC"
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
const NXArchInfo *NXGetLocalArchInfo(void) {
|
||||
Architecture arch;
|
||||
#if defined(__i386__)
|
||||
arch = kArch_i386;
|
||||
#elif defined(__x86_64__)
|
||||
arch = kArch_x86_64;
|
||||
#elif defined(__arm64)
|
||||
arch = kArch_arm64;
|
||||
#elif defined(__arm__)
|
||||
arch = kArch_arm;
|
||||
#elif defined(__powerpc__)
|
||||
arch = kArch_ppc;
|
||||
#else
|
||||
#error "Unsupported CPU architecture"
|
||||
#endif
|
||||
return &kKnownArchitectures[arch];
|
||||
}
|
||||
|
||||
const NXArchInfo *NXGetArchInfoFromName(const char *name) {
|
||||
for (int arch = 0; arch < kNumArchitectures; ++arch) {
|
||||
if (!strcmp(name, kKnownArchitectures[arch].name)) {
|
||||
return &kKnownArchitectures[arch];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype,
|
||||
cpu_subtype_t cpusubtype) {
|
||||
for (int arch = 0; arch < kNumArchitectures; ++arch) {
|
||||
if (kKnownArchitectures[arch].cputype == cputype) {
|
||||
return &kKnownArchitectures[arch];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct fat_arch *NXFindBestFatArch(cpu_type_t cputype,
|
||||
cpu_subtype_t cpusubtype,
|
||||
struct fat_arch *fat_archs,
|
||||
uint32_t nfat_archs) {
|
||||
for (uint32_t f = 0; f < nfat_archs; ++f) {
|
||||
if (fat_archs[f].cputype == cputype) {
|
||||
return &fat_archs[f];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif // !__APPLE__
|
||||
|
@ -36,6 +36,7 @@
|
||||
#ifndef COMMON_MAC_BYTESWAP_H_
|
||||
#define COMMON_MAC_BYTESWAP_H_
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
static inline uint16_t ByteSwap(uint16_t v) { return OSSwapInt16(v); }
|
||||
@ -45,4 +46,28 @@ 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); }
|
||||
|
||||
#elif defined(__linux__)
|
||||
// For NXByteOrder
|
||||
#include <architecture/byte_order.h>
|
||||
#include <stdint.h>
|
||||
#include <endian.h>
|
||||
#include_next <byteswap.h>
|
||||
|
||||
static inline uint16_t ByteSwap(uint16_t v) { return bswap_16(v); }
|
||||
static inline uint32_t ByteSwap(uint32_t v) { return bswap_32(v); }
|
||||
static inline uint64_t ByteSwap(uint64_t v) { return bswap_64(v); }
|
||||
static inline int16_t ByteSwap(int16_t v) { return bswap_16(v); }
|
||||
static inline int32_t ByteSwap(int32_t v) { return bswap_32(v); }
|
||||
static inline int64_t ByteSwap(int64_t v) { return bswap_64(v); }
|
||||
|
||||
static inline NXByteOrder NXHostByteOrder() {
|
||||
#ifdef __LITTLE_ENDIAN
|
||||
return NX_LittleEndian;
|
||||
#else
|
||||
return NX_BigEndian;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // __APPLE__
|
||||
|
||||
#endif // COMMON_MAC_BYTESWAP_H_
|
||||
|
@ -35,10 +35,16 @@
|
||||
|
||||
#include "common/mac/dump_syms.h"
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <libgen.h>
|
||||
#include <mach-o/arch.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
@ -82,104 +88,120 @@ using std::pair;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
// Return a vector<string> with absolute paths to all the entries
|
||||
// in directory (excluding . and ..).
|
||||
vector<string> list_directory(const string& directory) {
|
||||
vector<string> entries;
|
||||
DIR* dir = opendir(directory.c_str());
|
||||
if (!dir) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
string path = directory;
|
||||
if (path[path.length() - 1] != '/') {
|
||||
path += '/';
|
||||
}
|
||||
|
||||
struct dirent* entry = NULL;
|
||||
while ((entry = readdir(dir))) {
|
||||
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
|
||||
entries.push_back(path + entry->d_name);
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return entries;
|
||||
}
|
||||
}
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool DumpSymbols::Read(NSString *filename) {
|
||||
if (![[NSFileManager defaultManager] fileExistsAtPath:filename]) {
|
||||
fprintf(stderr, "Object file does not exist: %s\n",
|
||||
[filename fileSystemRepresentation]);
|
||||
bool DumpSymbols::Read(const string &filename) {
|
||||
struct stat st;
|
||||
if (stat(filename.c_str(), &st) == -1) {
|
||||
fprintf(stderr, "Could not access object file %s: %s\n",
|
||||
filename.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
input_pathname_ = [filename retain];
|
||||
input_pathname_ = filename;
|
||||
|
||||
// Does this filename refer to a dSYM bundle?
|
||||
NSBundle *bundle = [NSBundle bundleWithPath:input_pathname_];
|
||||
|
||||
if (bundle) {
|
||||
// Filenames referring to bundles usually have names of the form
|
||||
// "<basename>.dSYM"; however, if the user has specified a wrapper
|
||||
// suffix (the WRAPPER_SUFFIX and WRAPPER_EXTENSION build settings),
|
||||
// then the name may have the form "<basename>.<extension>.dSYM". In
|
||||
// either case, the resource name for the file containing the DWARF
|
||||
// info within the bundle is <basename>.
|
||||
//
|
||||
// Since there's no way to tell how much to strip off, remove one
|
||||
// extension at a time, and use the first one that
|
||||
// pathForResource:ofType:inDirectory likes.
|
||||
NSString *base_name = [input_pathname_ lastPathComponent];
|
||||
NSString *dwarf_resource;
|
||||
|
||||
do {
|
||||
NSString *new_base_name = [base_name stringByDeletingPathExtension];
|
||||
|
||||
// If stringByDeletingPathExtension returned the name unchanged, then
|
||||
// there's nothing more for us to strip off --- lose.
|
||||
if ([new_base_name isEqualToString:base_name]) {
|
||||
string contents_path = input_pathname_ + "/Contents/Resources/DWARF";
|
||||
if (S_ISDIR(st.st_mode) &&
|
||||
access(contents_path.c_str(), F_OK) == 0) {
|
||||
// If there's one file under Contents/Resources/DWARF then use that,
|
||||
// otherwise bail out.
|
||||
const vector<string> entries = list_directory(contents_path);
|
||||
if (entries.size() == 0) {
|
||||
fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n",
|
||||
[input_pathname_ fileSystemRepresentation]);
|
||||
input_pathname_.c_str());
|
||||
return false;
|
||||
}
|
||||
if (entries.size() > 1) {
|
||||
fprintf(stderr, "Too many DWARF files in bundle: %s\n",
|
||||
input_pathname_.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Take the shortened result as our new base_name.
|
||||
base_name = new_base_name;
|
||||
|
||||
// Try to find a DWARF resource in the bundle under the new base_name.
|
||||
dwarf_resource = [bundle pathForResource:base_name
|
||||
ofType:nil inDirectory:@"DWARF"];
|
||||
} while (!dwarf_resource);
|
||||
|
||||
object_filename_ = [dwarf_resource retain];
|
||||
object_filename_ = entries[0];
|
||||
} else {
|
||||
object_filename_ = [input_pathname_ retain];
|
||||
object_filename_ = input_pathname_;
|
||||
}
|
||||
|
||||
// Read the file's contents into memory.
|
||||
//
|
||||
// The documentation for dataWithContentsOfMappedFile says:
|
||||
//
|
||||
// Because of file mapping restrictions, this method should only be
|
||||
// used if the file is guaranteed to exist for the duration of the
|
||||
// data object’s existence. It is generally safer to use the
|
||||
// dataWithContentsOfFile: method.
|
||||
//
|
||||
// I gather this means that OS X doesn't have (or at least, that method
|
||||
// doesn't use) a form of mapping like Linux's MAP_PRIVATE, where the
|
||||
// process appears to get its own copy of the data, and changes to the
|
||||
// file don't affect memory and vice versa).
|
||||
NSError *error;
|
||||
contents_ = [NSData dataWithContentsOfFile:object_filename_
|
||||
options:0
|
||||
error:&error];
|
||||
if (!contents_) {
|
||||
bool read_ok = true;
|
||||
string error;
|
||||
if (stat(object_filename_.c_str(), &st) != -1) {
|
||||
FILE* f = fopen(object_filename_.c_str(), "rb");
|
||||
if (f) {
|
||||
contents_.reset(new uint8_t[st.st_size]);
|
||||
off_t total = 0;
|
||||
while (total < st.st_size && !feof(f)) {
|
||||
size_t read = fread(&contents_[0] + total, 1, st.st_size - total, f);
|
||||
if (read == 0) {
|
||||
if (ferror(f)) {
|
||||
read_ok = false;
|
||||
error = strerror(errno);
|
||||
}
|
||||
break;
|
||||
}
|
||||
total += read;
|
||||
}
|
||||
fclose(f);
|
||||
} else {
|
||||
error = strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
if (!read_ok) {
|
||||
fprintf(stderr, "Error reading object file: %s: %s\n",
|
||||
[object_filename_ fileSystemRepresentation],
|
||||
[[error localizedDescription] UTF8String]);
|
||||
object_filename_.c_str(),
|
||||
error.c_str());
|
||||
return false;
|
||||
}
|
||||
[contents_ retain];
|
||||
|
||||
// Get the list of object files present in the file.
|
||||
FatReader::Reporter fat_reporter([object_filename_
|
||||
fileSystemRepresentation]);
|
||||
FatReader::Reporter fat_reporter(object_filename_);
|
||||
FatReader fat_reader(&fat_reporter);
|
||||
if (!fat_reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes]),
|
||||
[contents_ length])) {
|
||||
if (!fat_reader.Read(&contents_[0],
|
||||
st.st_size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get our own copy of fat_reader's object file list.
|
||||
size_t object_files_count;
|
||||
const struct fat_arch *object_files =
|
||||
const SuperFatArch *object_files =
|
||||
fat_reader.object_files(&object_files_count);
|
||||
if (object_files_count == 0) {
|
||||
fprintf(stderr, "Fat binary file contains *no* architectures: %s\n",
|
||||
[object_filename_ fileSystemRepresentation]);
|
||||
object_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
object_files_.resize(object_files_count);
|
||||
memcpy(&object_files_[0], object_files,
|
||||
sizeof(struct fat_arch) * object_files_count);
|
||||
sizeof(SuperFatArch) * object_files_count);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -187,9 +209,8 @@ bool DumpSymbols::Read(NSString *filename) {
|
||||
bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type,
|
||||
cpu_subtype_t cpu_subtype) {
|
||||
// Find the best match for the architecture the user requested.
|
||||
const struct fat_arch *best_match
|
||||
= NXFindBestFatArch(cpu_type, cpu_subtype, &object_files_[0],
|
||||
static_cast<uint32_t>(object_files_.size()));
|
||||
const SuperFatArch *best_match = FindBestMatchForArchitecture(
|
||||
cpu_type, cpu_subtype);
|
||||
if (!best_match) return false;
|
||||
|
||||
// Record the selected object file.
|
||||
@ -207,14 +228,65 @@ bool DumpSymbols::SetArchitecture(const std::string &arch_name) {
|
||||
return arch_set;
|
||||
}
|
||||
|
||||
SuperFatArch* DumpSymbols::FindBestMatchForArchitecture(
|
||||
cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) {
|
||||
// Check if all the object files can be converted to struct fat_arch.
|
||||
bool can_convert_to_fat_arch = true;
|
||||
vector<struct fat_arch> fat_arch_vector;
|
||||
for (vector<SuperFatArch>::const_iterator it = object_files_.begin();
|
||||
it != object_files_.end();
|
||||
++it) {
|
||||
struct fat_arch arch;
|
||||
bool success = it->ConvertToFatArch(&arch);
|
||||
if (!success) {
|
||||
can_convert_to_fat_arch = false;
|
||||
break;
|
||||
}
|
||||
fat_arch_vector.push_back(arch);
|
||||
}
|
||||
|
||||
// If all the object files can be converted to struct fat_arch, use
|
||||
// NXFindBestFatArch.
|
||||
if (can_convert_to_fat_arch) {
|
||||
const struct fat_arch *best_match
|
||||
= NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0],
|
||||
static_cast<uint32_t>(fat_arch_vector.size()));
|
||||
|
||||
for (size_t i = 0; i < fat_arch_vector.size(); ++i) {
|
||||
if (best_match == &fat_arch_vector[i])
|
||||
return &object_files_[i];
|
||||
}
|
||||
assert(best_match == NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check for an exact match with cpu_type and cpu_subtype.
|
||||
for (vector<SuperFatArch>::iterator it = object_files_.begin();
|
||||
it != object_files_.end();
|
||||
++it) {
|
||||
if (static_cast<cpu_type_t>(it->cputype) == cpu_type &&
|
||||
static_cast<cpu_subtype_t>(it->cpusubtype) == cpu_subtype)
|
||||
return &*it;
|
||||
}
|
||||
|
||||
// No exact match found.
|
||||
// TODO(erikchen): If it becomes necessary, we can copy the implementation of
|
||||
// NXFindBestFatArch, located at
|
||||
// http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c.
|
||||
fprintf(stderr, "Failed to find an exact match for an object file with cpu "
|
||||
"type: %d and cpu subtype: %d. Furthermore, at least one object file is "
|
||||
"larger than 2**32.\n", cpu_type, cpu_subtype);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
string DumpSymbols::Identifier() {
|
||||
FileID file_id([object_filename_ fileSystemRepresentation]);
|
||||
FileID file_id(object_filename_.c_str());
|
||||
unsigned char identifier_bytes[16];
|
||||
cpu_type_t cpu_type = selected_object_file_->cputype;
|
||||
cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype;
|
||||
if (!file_id.MachoIdentifier(cpu_type, cpu_subtype, identifier_bytes)) {
|
||||
fprintf(stderr, "Unable to calculate UUID of mach-o binary %s!\n",
|
||||
[object_filename_ fileSystemRepresentation]);
|
||||
object_filename_.c_str());
|
||||
return "";
|
||||
}
|
||||
|
||||
@ -476,7 +548,7 @@ bool DumpSymbols::ReadSymbolData(Module** out_module) {
|
||||
" architecture, none of which match the current"
|
||||
" architecture; specify an architecture explicitly"
|
||||
" with '-a ARCH' to resolve the ambiguity\n",
|
||||
[object_filename_ fileSystemRepresentation]);
|
||||
object_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -496,14 +568,15 @@ bool DumpSymbols::ReadSymbolData(Module** out_module) {
|
||||
|
||||
// Produce a name to use in error messages that includes the
|
||||
// filename, and the architecture, if there is more than one.
|
||||
selected_object_name_ = [object_filename_ UTF8String];
|
||||
selected_object_name_ = object_filename_;
|
||||
if (object_files_.size() > 1) {
|
||||
selected_object_name_ += ", architecture ";
|
||||
selected_object_name_ + selected_arch_name;
|
||||
}
|
||||
|
||||
// Compute a module name, to appear in the MODULE record.
|
||||
NSString *module_name = [object_filename_ lastPathComponent];
|
||||
string module_name = object_filename_;
|
||||
module_name = basename(&module_name[0]);
|
||||
|
||||
// Choose an identifier string, to appear in the MODULE record.
|
||||
string identifier = Identifier();
|
||||
@ -512,7 +585,7 @@ bool DumpSymbols::ReadSymbolData(Module** out_module) {
|
||||
identifier += "0";
|
||||
|
||||
// Create a module to hold the debugging information.
|
||||
scoped_ptr<Module> module(new Module([module_name UTF8String],
|
||||
scoped_ptr<Module> module(new Module(module_name,
|
||||
"mac",
|
||||
selected_arch_name,
|
||||
identifier));
|
||||
@ -520,7 +593,7 @@ bool DumpSymbols::ReadSymbolData(Module** out_module) {
|
||||
// Parse the selected object file.
|
||||
mach_o::Reader::Reporter reporter(selected_object_name_);
|
||||
mach_o::Reader reader(&reporter);
|
||||
if (!reader.Read(reinterpret_cast<const uint8_t *>([contents_ bytes])
|
||||
if (!reader.Read(&contents_[0]
|
||||
+ selected_object_file_->offset,
|
||||
selected_object_file_->size,
|
||||
selected_object_file_->cputype,
|
@ -35,7 +35,6 @@
|
||||
// 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -46,7 +45,9 @@
|
||||
|
||||
#include "common/byte_cursor.h"
|
||||
#include "common/mac/macho_reader.h"
|
||||
#include "common/mac/super_fat_arch.h"
|
||||
#include "common/module.h"
|
||||
#include "common/scoped_ptr.h"
|
||||
#include "common/symbol_data.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -59,23 +60,17 @@ class DumpSymbols {
|
||||
input_pathname_(),
|
||||
object_filename_(),
|
||||
contents_(),
|
||||
object_files_(),
|
||||
selected_object_file_(),
|
||||
selected_object_name_() { }
|
||||
~DumpSymbols() {
|
||||
[input_pathname_ release];
|
||||
[object_filename_ release];
|
||||
[contents_ release];
|
||||
}
|
||||
|
||||
// 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);
|
||||
bool Read(const std::string &filename);
|
||||
|
||||
// If this dumper's file includes an object file for |cpu_type| and
|
||||
// |cpu_subtype|, then select that object file for dumping, and return
|
||||
@ -98,14 +93,14 @@ class DumpSymbols {
|
||||
// architecture matches that of this dumper program.
|
||||
bool SetArchitecture(const std::string &arch_name);
|
||||
|
||||
// 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.
|
||||
// Return a pointer to an array of SuperFatArch 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) {
|
||||
const SuperFatArch* AvailableArchitectures(size_t *count) {
|
||||
*count = object_files_.size();
|
||||
if (object_files_.size() > 0)
|
||||
return &object_files_[0];
|
||||
@ -127,6 +122,11 @@ class DumpSymbols {
|
||||
class DumperLineToModule;
|
||||
class LoadCommandDumper;
|
||||
|
||||
// This method behaves similarly to NXFindBestFatArch, but it supports
|
||||
// SuperFatArch.
|
||||
SuperFatArch* FindBestMatchForArchitecture(
|
||||
cpu_type_t cpu_type, cpu_subtype_t cpu_subtype);
|
||||
|
||||
// Return an identifier string for the file this DumpSymbols is dumping.
|
||||
std::string Identifier();
|
||||
|
||||
@ -156,26 +156,26 @@ class DumpSymbols {
|
||||
|
||||
// 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_;
|
||||
std::string 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_;
|
||||
std::string object_filename_;
|
||||
|
||||
// The complete contents of object_filename_, mapped into memory.
|
||||
NSData *contents_;
|
||||
scoped_array<uint8_t> contents_;
|
||||
|
||||
// A vector of fat_arch structures describing the object files
|
||||
// A vector of SuperFatArch 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_;
|
||||
vector<SuperFatArch> 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_;
|
||||
const SuperFatArch *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
|
||||
|
@ -34,6 +34,7 @@
|
||||
// Author: Dan Waylonis
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -45,7 +46,7 @@ using MacFileUtilities::MachoID;
|
||||
namespace google_breakpad {
|
||||
|
||||
FileID::FileID(const char *path) {
|
||||
strlcpy(path_, path, sizeof(path_));
|
||||
snprintf(path_, sizeof(path_), "%s", path);
|
||||
}
|
||||
|
||||
bool FileID::FileIdentifier(unsigned char identifier[16]) {
|
||||
|
@ -33,17 +33,15 @@
|
||||
//
|
||||
// Author: Dan Waylonis
|
||||
|
||||
extern "C" { // necessary for Leopard
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach-o/swap.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
}
|
||||
|
||||
#include "common/mac/macho_id.h"
|
||||
#include "common/mac/macho_walker.h"
|
||||
@ -61,7 +59,7 @@ MachoID::MachoID(const char *path)
|
||||
crc_(0),
|
||||
md5_context_(),
|
||||
update_function_(NULL) {
|
||||
strlcpy(path_, path, sizeof(path_));
|
||||
snprintf(path_, sizeof(path_), "%s", path);
|
||||
}
|
||||
|
||||
MachoID::MachoID(const char *path, void *memory, size_t size)
|
||||
@ -70,7 +68,7 @@ MachoID::MachoID(const char *path, void *memory, size_t size)
|
||||
crc_(0),
|
||||
md5_context_(),
|
||||
update_function_(NULL) {
|
||||
strlcpy(path_, path, sizeof(path_));
|
||||
snprintf(path_, sizeof(path_), "%s", path);
|
||||
}
|
||||
|
||||
MachoID::~MachoID() {
|
||||
@ -261,7 +259,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
||||
return false;
|
||||
|
||||
if (swap)
|
||||
swap_segment_command(&seg, NXHostByteOrder());
|
||||
breakpad_swap_segment_command(&seg);
|
||||
|
||||
struct mach_header_64 header;
|
||||
off_t header_offset;
|
||||
@ -278,7 +276,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
||||
return false;
|
||||
|
||||
if (swap)
|
||||
swap_section(&sec, 1, NXHostByteOrder());
|
||||
breakpad_swap_section(&sec, 1);
|
||||
|
||||
// sections of type S_ZEROFILL are "virtual" and contain no data
|
||||
// in the file itself
|
||||
@ -294,7 +292,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
||||
return false;
|
||||
|
||||
if (swap)
|
||||
breakpad_swap_segment_command_64(&seg64, NXHostByteOrder());
|
||||
breakpad_swap_segment_command_64(&seg64);
|
||||
|
||||
struct mach_header_64 header;
|
||||
off_t header_offset;
|
||||
@ -311,7 +309,7 @@ bool MachoID::WalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
||||
return false;
|
||||
|
||||
if (swap)
|
||||
breakpad_swap_section_64(&sec64, 1, NXHostByteOrder());
|
||||
breakpad_swap_section_64(&sec64, 1);
|
||||
|
||||
// sections of type S_ZEROFILL are "virtual" and contain no data
|
||||
// in the file itself
|
||||
@ -340,7 +338,7 @@ bool MachoID::UUIDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
||||
return false;
|
||||
|
||||
if (swap)
|
||||
breakpad_swap_uuid_command(uuid_cmd, NXHostByteOrder());
|
||||
breakpad_swap_uuid_command(uuid_cmd);
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -359,7 +357,7 @@ bool MachoID::IDWalkerCB(MachoWalker *walker, load_command *cmd, off_t offset,
|
||||
return false;
|
||||
|
||||
if (swap)
|
||||
swap_dylib_command(dylib_cmd, NXHostByteOrder());
|
||||
breakpad_swap_dylib_command(dylib_cmd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -101,22 +101,26 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
|
||||
// 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];
|
||||
struct fat_arch objfile;
|
||||
|
||||
// Read this object file entry, byte-swapping as appropriate.
|
||||
cursor >> objfile->cputype
|
||||
>> objfile->cpusubtype
|
||||
>> objfile->offset
|
||||
>> objfile->size
|
||||
>> objfile->align;
|
||||
cursor >> objfile.cputype
|
||||
>> objfile.cpusubtype
|
||||
>> objfile.offset
|
||||
>> objfile.size
|
||||
>> objfile.align;
|
||||
|
||||
SuperFatArch super_fat_arch(objfile);
|
||||
object_files_[i] = super_fat_arch;
|
||||
|
||||
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) {
|
||||
if (objfile.offset > fat_size ||
|
||||
objfile.size > fat_size - objfile.offset) {
|
||||
reporter_->MisplacedObjectFile();
|
||||
return false;
|
||||
}
|
||||
@ -139,16 +143,14 @@ bool FatReader::Read(const uint8_t *buffer, size_t size) {
|
||||
}
|
||||
|
||||
object_files_[0].offset = 0;
|
||||
object_files_[0].size = static_cast<uint32_t>(buffer_.Size());
|
||||
object_files_[0].size = static_cast<uint64_t>(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;
|
||||
}
|
||||
@ -479,7 +481,9 @@ bool Reader::WalkSegmentSections(const Segment &segment,
|
||||
reporter_->SectionsMissing(segment.name);
|
||||
return false;
|
||||
}
|
||||
if ((section.flags & SECTION_TYPE) == S_ZEROFILL) {
|
||||
const uint32_t section_type = section.flags & SECTION_TYPE;
|
||||
if (section_type == S_ZEROFILL || section_type == S_THREAD_LOCAL_ZEROFILL ||
|
||||
section_type == S_GB_ZEROFILL) {
|
||||
// Zero-fill sections have a size, but no contents.
|
||||
section.contents.start = section.contents.end = NULL;
|
||||
} else if (segment.contents.start == NULL &&
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/byte_cursor.h"
|
||||
#include "common/mac/super_fat_arch.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace mach_o {
|
||||
@ -111,13 +112,13 @@ class FatReader {
|
||||
// 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
|
||||
// Return an array of 'SuperFatArch' 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.
|
||||
// Assuming Read returned true, the entries are validated: it is safe to
|
||||
// assume that the offsets and sizes in each SuperFatArch 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.
|
||||
@ -129,7 +130,7 @@ class FatReader {
|
||||
// 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 {
|
||||
const SuperFatArch* object_files(size_t *count) const {
|
||||
*count = object_files_.size();
|
||||
if (object_files_.size() > 0)
|
||||
return &object_files_[0];
|
||||
@ -149,7 +150,7 @@ class FatReader {
|
||||
|
||||
// The list of object files in this binary.
|
||||
// object_files_.size() == fat_header.nfat_arch
|
||||
vector<struct fat_arch> object_files_;
|
||||
vector<SuperFatArch> object_files_;
|
||||
};
|
||||
|
||||
// A segment in a Mach-O file. All these fields have been byte-swapped as
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user