Compare commits
111 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
61abec212c | ||
|
3d81626b4b | ||
|
5a48c16896 | ||
|
23b54a1c19 | ||
|
3b40d48756 | ||
|
08be451e09 | ||
|
616333286c | ||
|
c011273dee | ||
|
0e8ac58357 | ||
|
8c74123cde | ||
|
e69ef58485 | ||
|
d4675ed160 | ||
|
c5b192038d | ||
|
7f5001b97e | ||
|
8e4296e5fc | ||
|
91de4a6b79 | ||
|
88e5b2c880 | ||
|
ccf03c13eb | ||
|
19af23e3c0 | ||
|
4a7e088a27 | ||
|
4af8174278 | ||
|
4e82b6fa11 | ||
|
644e71591b | ||
|
fd28a5bbe9 | ||
|
6cc037526e | ||
|
8ba59492ab | ||
|
96b79e9bf8 | ||
|
d4676b89a0 | ||
|
93c447a0de | ||
|
cc1deb4452 | ||
|
0e45a1268a | ||
|
2b7724245b | ||
|
12f8ca4855 | ||
|
333ed18eb0 | ||
|
8b0a1053e2 | ||
|
64c2eda38a | ||
|
7ba80c7284 | ||
|
6c78460419 | ||
|
8f5741078d | ||
|
cff58cdd0a | ||
|
049a1532e5 | ||
|
24c359d329 | ||
|
2c4c039118 | ||
|
262a3f50fe | ||
|
5c521532fc | ||
|
7c2799f3ba | ||
|
cb94b71d28 | ||
|
76a48f4aa9 | ||
|
0924d424e4 | ||
|
e7dfafc16e | ||
|
833cadc0a1 | ||
|
c2d969cb10 | ||
|
f78d953511 | ||
|
5c42d7288a | ||
|
c13d6fa467 | ||
|
596fbb61bc | ||
|
87e444e17d | ||
|
7a8980997d | ||
|
e6ef06f13d | ||
|
7515ab1376 | ||
|
33c24776f3 | ||
|
11d7510c08 | ||
|
8e2448bceb | ||
|
4eb76cbc9e | ||
|
2f6cb866d6 | ||
|
1f574b52c6 | ||
|
7df62a96eb | ||
|
26ed3386af | ||
|
325120efbd | ||
|
54b524be13 | ||
|
ed7dcced19 | ||
|
2ecb2baba8 | ||
|
e6d1c032ba | ||
|
6b2f69dd10 | ||
|
48a13da168 | ||
|
36d613ef59 | ||
|
98b6a6309f | ||
|
7398ce15b7 | ||
|
138886803c | ||
|
d3b9631f81 | ||
|
b857dfec2b | ||
|
8cb66bcef7 | ||
|
fea963f974 | ||
|
704f41ec90 | ||
|
3fef603b65 | ||
|
968c388922 | ||
|
a2196179cc | ||
|
c9f80bf1a8 | ||
|
13c634f6a1 | ||
|
600ec35c5b | ||
|
0fc6d0c8df | ||
|
f7be4815a2 | ||
|
f5638b8c2e | ||
|
0e4af9d8f9 | ||
|
5f638d5323 | ||
|
b5200a97b7 | ||
|
c99d374dde | ||
|
5adeef6117 | ||
|
0ebdc4a10a | ||
|
41b91d064e | ||
|
965424f183 | ||
|
dfd2da7979 | ||
|
e0f2c17988 | ||
|
af8840d910 | ||
|
1600b6054e | ||
|
f140a0339f | ||
|
5325fd5162 | ||
|
83a5552060 | ||
|
24f5931c5e | ||
|
67f738b7ad | ||
|
c44217f646 |
12
.gitignore
vendored
12
.gitignore
vendored
@ -27,6 +27,7 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Ignore other VCSs.
|
||||
.repo/
|
||||
.svn/
|
||||
|
||||
# Ignore common compiled artifacts.
|
||||
@ -47,6 +48,7 @@ lib*.a
|
||||
/src/tools/linux/symupload/minidump_upload
|
||||
/src/tools/linux/symupload/sym_upload
|
||||
/src/tools/mac/dump_syms/dump_syms
|
||||
/src/tools/mac/dump_syms/dump_syms_mac
|
||||
|
||||
# Ignore autotools generated artifacts.
|
||||
.deps
|
||||
@ -76,8 +78,8 @@ src/Makefile
|
||||
*.pyc
|
||||
|
||||
# Ignore directories gclient syncs.
|
||||
src/testing
|
||||
src/third_party/glog
|
||||
src/third_party/lss
|
||||
src/third_party/protobuf
|
||||
src/tools/gyp
|
||||
#src/testing
|
||||
#src/third_party/glog
|
||||
#src/third_party/lss
|
||||
#src/third_party/protobuf
|
||||
#src/tools/gyp
|
||||
|
15
.gitmodules
vendored
Normal file
15
.gitmodules
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
[submodule "src/third_party/glog"]
|
||||
path = src/third_party/glog
|
||||
url = https://github.com/google/glog.git
|
||||
[submodule "src/testing"]
|
||||
path = src/testing
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "src/third_party/protobuf/protobuf"]
|
||||
path = src/third_party/protobuf/protobuf
|
||||
url = https://github.com/google/protobuf.git
|
||||
[submodule "src/tools/gyp"]
|
||||
path = src/tools/gyp
|
||||
url = https://chromium.googlesource.com/external/gyp
|
||||
[submodule "src/third_party/lss"]
|
||||
path = src/third_party/lss
|
||||
url = https://chromium.googlesource.com/linux-syscall-support/
|
26
.travis.yml
26
.travis.yml
@ -1,26 +1,32 @@
|
||||
# Travis build integration.
|
||||
# https://docs.travis-ci.com/
|
||||
language: cpp
|
||||
# TODO: add a clang build as well.
|
||||
compiler:
|
||||
- gcc
|
||||
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- clang
|
||||
- gcc-4.8
|
||||
- g++-4.8
|
||||
# Travis sets CC/CXX to the system toolchain based on the `compiler`
|
||||
# selection. If clang is added, this should move to be set inside the
|
||||
# matrix.
|
||||
|
||||
env:
|
||||
- USE_CC=gcc-4.8 USE_CXX=g++-4.8
|
||||
global:
|
||||
- secure: "FPczJ1u7FWGXOtUVf5AONeexfQDYnKRtuNs3phLwlPPAbgAlIc/WeTRSHC8DAb1T8IyPdN3Zi7cqLz0dvPol0iX1fWSfr8YdtW0ea8nUYH5ldmmp6H75FEUJUcISmYwL4WN7TldC6Hnzrlbw/0xmBH8gtAgddtBXKc9P9SwEZvM4OiFMHbMPwZEhRj+D95rfH12lgh3D16nbXGnx3rSNbHszvIxrU2VvlLo9Aa+hbmVj5CsBiNJjhDS64ie+VMTkuzcWNqLRYaGOCQ8ftKAlj/fjGfoKjPDN9dSJg9gW1FjOMPeQo93qhSc/hCmTq7sWxBJu48telinUgESdE5q/8gRf5J05ImWPntZAkC/wQkA20K7Kp/fH1CRaYXQMWKjts8c6dQZ5R4WxE4WXUo5rz573Ti9uyVTLys9whnzaib3YbqYv04irkhpgzo3rd8PF8SXpgK99ySQCcv/Dh7UQuXPpcaknOk2mBySXjQDgpQHHXDN2uUek1HEo5xit8fQuQw3TdPIZ9ZgzQ/c5/Dx6sLWAGEfVH9MN+hy6AiZnJ1JI+XF82kAf1pnf8WddHtlE7pAdWRFQt0iOj9T9esV1/o0VCJVzJLRdpKecF0sTpJxDuan6cFI0tNCkNjHFA5wJKYAvdOPAmYkqre7aIIqSOKy3Fjh9JP9CBJFy7eals9U="
|
||||
|
||||
# TODO: Add an OS X config.
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: USE_CC=gcc-4.8 USE_CXX=g++-4.8 COVERITY_SCAN=true
|
||||
- os: linux
|
||||
compiler: clang
|
||||
|
||||
before_install: ./scripts/travis-checkout.sh
|
||||
script: ./scripts/travis-build.sh
|
||||
# TODO: add mac support
|
||||
os:
|
||||
- linux
|
||||
|
||||
notifications:
|
||||
email:
|
||||
- google-breakpad-dev@googlegroups.com
|
||||
|
19
DEPS
19
DEPS
@ -40,11 +40,8 @@ deps = {
|
||||
|
||||
# Testing libraries and utilities.
|
||||
"src/src/testing":
|
||||
"https://github.com/google/googlemock.git" +
|
||||
"@release-1.7.0",
|
||||
"src/src/testing/gtest":
|
||||
"https://github.com/google/googletest.git" +
|
||||
"@release-1.7.0",
|
||||
"@release-1.8.0",
|
||||
|
||||
# Protobuf.
|
||||
"src/src/third_party/protobuf/protobuf":
|
||||
@ -59,7 +56,7 @@ deps = {
|
||||
# Linux syscall support.
|
||||
"src/src/third_party/lss":
|
||||
"https://chromium.googlesource.com/linux-syscall-support/" +
|
||||
"@9292030109847793f7a6689adac1ddafb412fe14"
|
||||
"@3f6478ac95edf86cd3da300c2c0d34a438f5dbeb",
|
||||
}
|
||||
|
||||
hooks = [
|
||||
@ -72,4 +69,16 @@ hooks = [
|
||||
"--no-circular-check",
|
||||
"src/src/client/windows/breakpad_client.gyp"],
|
||||
},
|
||||
{
|
||||
# XXX: this and above should all be wired into build/all.gyp ?
|
||||
"action": ["python",
|
||||
"src/src/tools/gyp/gyp_main.py",
|
||||
"--no-circular-check",
|
||||
"src/src/tools/windows/tools_windows.gyp"],
|
||||
},
|
||||
{
|
||||
# Keep the manifest up to date.
|
||||
"action": ["python", "src/src/tools/python/deps-to-manifest.py",
|
||||
"src/DEPS", "src/default.xml"],
|
||||
},
|
||||
]
|
||||
|
141
Makefile.am
141
Makefile.am
@ -43,17 +43,7 @@ AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/include
|
||||
AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/testing/include
|
||||
endif
|
||||
|
||||
if GCC
|
||||
# These are good warnings to be treated as errors
|
||||
AM_CXXFLAGS += \
|
||||
-Werror=missing-braces \
|
||||
-Werror=non-virtual-dtor \
|
||||
-Werror=overloaded-virtual \
|
||||
-Werror=reorder \
|
||||
-Werror=sign-compare \
|
||||
-Werror=unused-variable \
|
||||
-Werror=vla
|
||||
endif
|
||||
AM_CXXFLAGS += $(WARN_CXXFLAGS)
|
||||
|
||||
if LINUX_HOST
|
||||
# Build as PIC on Linux, for linux_client_unittest_shlib
|
||||
@ -120,8 +110,10 @@ TEST_DEPS =
|
||||
else
|
||||
TEST_CFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing/googletest/include \
|
||||
-I$(top_srcdir)/src/testing/googletest \
|
||||
-I$(top_srcdir)/src/testing/googlemock/include \
|
||||
-I$(top_srcdir)/src/testing/googlemock \
|
||||
-I$(top_srcdir)/src/testing
|
||||
TEST_LIBS = src/testing/libtesting.a
|
||||
TEST_DEPS = $(TEST_LIBS)
|
||||
@ -141,9 +133,9 @@ check_LIBRARIES += src/testing/libtesting.a
|
||||
if !SYSTEM_TEST_LIBS
|
||||
src_testing_libtesting_a_SOURCES = \
|
||||
src/breakpad_googletest_includes.h \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src/testing/googletest/src/gtest-all.cc \
|
||||
src/testing/googletest/src/gtest_main.cc \
|
||||
src/testing/googlemock/src/gmock-all.cc
|
||||
src_testing_libtesting_a_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) $(TEST_CFLAGS)
|
||||
endif
|
||||
@ -193,7 +185,8 @@ src_client_linux_libbreakpad_client_a_SOURCES = \
|
||||
src/common/linux/guid_creator.h \
|
||||
src/common/linux/linux_libc_support.cc \
|
||||
src/common/linux/memory_mapped_file.cc \
|
||||
src/common/linux/safe_readlink.cc
|
||||
src/common/linux/safe_readlink.cc \
|
||||
src/common/linux/http_upload.cc
|
||||
if ANDROID_HOST
|
||||
src_client_linux_libbreakpad_client_a_SOURCES += \
|
||||
src/common/android/breakpad_getcontext.S
|
||||
@ -593,6 +586,10 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
|
||||
src/common/linux/memory_mapped_file.cc \
|
||||
src/common/linux/safe_readlink.cc \
|
||||
src/tools/linux/dump_syms/dump_syms.cc
|
||||
src_tools_linux_dump_syms_dump_syms_CXXFLAGS = \
|
||||
$(RUST_DEMANGLE_CFLAGS)
|
||||
src_tools_linux_dump_syms_dump_syms_LDADD = \
|
||||
$(RUST_DEMANGLE_LIBS)
|
||||
|
||||
src_tools_linux_md2core_minidump_2_core_SOURCES = \
|
||||
src/common/linux/memory_mapped_file.cc \
|
||||
@ -641,7 +638,10 @@ src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \
|
||||
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 \
|
||||
$(RUST_DEMANGLE_CFLAGS) \
|
||||
-DHAVE_MACH_O_NLIST_H
|
||||
src_tools_mac_dump_syms_dump_syms_mac_LDADD= \
|
||||
$(RUST_DEMANGLE_LIBS)
|
||||
|
||||
src_common_dumper_unittest_SOURCES = \
|
||||
src/common/byte_cursor_unittest.cc \
|
||||
@ -698,9 +698,11 @@ src_common_dumper_unittest_SOURCES = \
|
||||
src/common/tests/file_utils.cc
|
||||
src_common_dumper_unittest_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) $(TEST_CFLAGS) \
|
||||
$(RUST_DEMANGLE_CFLAGS) \
|
||||
$(PTHREAD_CFLAGS)
|
||||
src_common_dumper_unittest_LDADD = \
|
||||
$(TEST_LIBS) \
|
||||
$(RUST_DEMANGLE_LIBS) \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_common_mac_macho_reader_unittest_SOURCES = \
|
||||
@ -1321,6 +1323,7 @@ EXTRA_DIST = \
|
||||
src/common/windows/pdb_source_line_writer.h \
|
||||
src/common/windows/string_utils-inl.h \
|
||||
src/common/windows/string_utils.cc \
|
||||
src/processor/microdump_stackwalk_test_vars \
|
||||
src/processor/stackwalk_common.cc \
|
||||
src/processor/stackwalk_common.h \
|
||||
src/processor/stackwalker_selftest_sol.s \
|
||||
@ -1347,6 +1350,7 @@ EXTRA_DIST = \
|
||||
src/processor/testdata/linux_raise_sigabrt.dmp \
|
||||
src/processor/testdata/linux_stack_pointer_in_module.dmp \
|
||||
src/processor/testdata/linux_stack_pointer_in_stack.dmp \
|
||||
src/processor/testdata/linux_stack_pointer_in_stack_alt_name.dmp \
|
||||
src/processor/testdata/linux_stacksmash.dmp \
|
||||
src/processor/testdata/linux_write_to_nonwritable_module.dmp \
|
||||
src/processor/testdata/linux_write_to_nonwritable_region_math.dmp \
|
||||
@ -1391,54 +1395,61 @@ EXTRA_DIST = \
|
||||
src/processor/testdata/symbols/overflow/B0E1FC01EF48E39CAF5C881D2DF0C3840/overflow.sym \
|
||||
src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym \
|
||||
src/processor/testdata/test_app.cc \
|
||||
src/testing/gtest/include/gtest/gtest.h \
|
||||
src/testing/gtest/include/gtest/gtest-death-test.h \
|
||||
src/testing/gtest/include/gtest/gtest-message.h \
|
||||
src/testing/gtest/include/gtest/gtest-param-test.h \
|
||||
src/testing/gtest/include/gtest/gtest-printers.h \
|
||||
src/testing/gtest/include/gtest/gtest-spi.h \
|
||||
src/testing/gtest/include/gtest/gtest-test-part.h \
|
||||
src/testing/gtest/include/gtest/gtest-typed-test.h \
|
||||
src/testing/gtest/include/gtest/gtest_pred_impl.h \
|
||||
src/testing/gtest/include/gtest/gtest_prod.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-death-test-internal.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-filepath.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-internal.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-linked_ptr.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-param-util-generated.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-param-util.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-port.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-string.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-tuple.h \
|
||||
src/testing/gtest/include/gtest/internal/gtest-type-util.h \
|
||||
src/testing/gtest/src/gtest.cc \
|
||||
src/testing/gtest/src/gtest-death-test.cc \
|
||||
src/testing/gtest/src/gtest-filepath.cc \
|
||||
src/testing/gtest/src/gtest-internal-inl.h \
|
||||
src/testing/gtest/src/gtest-port.cc \
|
||||
src/testing/gtest/src/gtest-printers.cc \
|
||||
src/testing/gtest/src/gtest-test-part.cc \
|
||||
src/testing/gtest/src/gtest-typed-test.cc \
|
||||
src/testing/include/gmock/gmock.h \
|
||||
src/testing/include/gmock/gmock-actions.h \
|
||||
src/testing/include/gmock/gmock-cardinalities.h \
|
||||
src/testing/include/gmock/gmock-generated-actions.h \
|
||||
src/testing/include/gmock/gmock-generated-function-mockers.h \
|
||||
src/testing/include/gmock/gmock-generated-matchers.h \
|
||||
src/testing/include/gmock/gmock-generated-nice-strict.h \
|
||||
src/testing/include/gmock/gmock-matchers.h \
|
||||
src/testing/include/gmock/gmock-more-actions.h \
|
||||
src/testing/include/gmock/gmock-more-matchers.h \
|
||||
src/testing/include/gmock/gmock-spec-builders.h \
|
||||
src/testing/include/gmock/internal/gmock-generated-internal-utils.h \
|
||||
src/testing/include/gmock/internal/gmock-internal-utils.h \
|
||||
src/testing/include/gmock/internal/gmock-port.h \
|
||||
src/testing/src/gmock.cc \
|
||||
src/testing/src/gmock-cardinalities.cc \
|
||||
src/testing/src/gmock-internal-utils.cc \
|
||||
src/testing/src/gmock-matchers.cc \
|
||||
src/testing/src/gmock-spec-builders.cc \
|
||||
src/testing/src/gmock_main.cc \
|
||||
src/testing/googletest/include/gtest/gtest.h \
|
||||
src/testing/googletest/include/gtest/gtest-death-test.h \
|
||||
src/testing/googletest/include/gtest/gtest-message.h \
|
||||
src/testing/googletest/include/gtest/gtest-param-test.h \
|
||||
src/testing/googletest/include/gtest/gtest-printers.h \
|
||||
src/testing/googletest/include/gtest/gtest-spi.h \
|
||||
src/testing/googletest/include/gtest/gtest-test-part.h \
|
||||
src/testing/googletest/include/gtest/gtest-typed-test.h \
|
||||
src/testing/googletest/include/gtest/gtest_pred_impl.h \
|
||||
src/testing/googletest/include/gtest/gtest_prod.h \
|
||||
src/testing/googletest/include/gtest/internal/custom/gtest-port.h \
|
||||
src/testing/googletest/include/gtest/internal/custom/gtest-printers.h \
|
||||
src/testing/googletest/include/gtest/internal/custom/gtest.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-death-test-internal.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-filepath.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-internal.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-linked_ptr.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-param-util-generated.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-param-util.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-port-arch.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-port.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-string.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-tuple.h \
|
||||
src/testing/googletest/include/gtest/internal/gtest-type-util.h \
|
||||
src/testing/googletest/src/gtest.cc \
|
||||
src/testing/googletest/src/gtest-death-test.cc \
|
||||
src/testing/googletest/src/gtest-filepath.cc \
|
||||
src/testing/googletest/src/gtest-internal-inl.h \
|
||||
src/testing/googletest/src/gtest-port.cc \
|
||||
src/testing/googletest/src/gtest-printers.cc \
|
||||
src/testing/googletest/src/gtest-test-part.cc \
|
||||
src/testing/googletest/src/gtest-typed-test.cc \
|
||||
src/testing/googlemock/include/gmock/gmock.h \
|
||||
src/testing/googlemock/include/gmock/gmock-actions.h \
|
||||
src/testing/googlemock/include/gmock/gmock-cardinalities.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-actions.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-function-mockers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-matchers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-generated-nice-strict.h \
|
||||
src/testing/googlemock/include/gmock/gmock-matchers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-more-actions.h \
|
||||
src/testing/googlemock/include/gmock/gmock-more-matchers.h \
|
||||
src/testing/googlemock/include/gmock/gmock-spec-builders.h \
|
||||
src/testing/googlemock/include/gmock/internal/custom/gmock-generated-actions.h \
|
||||
src/testing/googlemock/include/gmock/internal/custom/gmock-matchers.h \
|
||||
src/testing/googlemock/include/gmock/internal/custom/gmock-port.h \
|
||||
src/testing/googlemock/include/gmock/internal/gmock-generated-internal-utils.h \
|
||||
src/testing/googlemock/include/gmock/internal/gmock-internal-utils.h \
|
||||
src/testing/googlemock/include/gmock/internal/gmock-port.h \
|
||||
src/testing/googlemock/src/gmock.cc \
|
||||
src/testing/googlemock/src/gmock-cardinalities.cc \
|
||||
src/testing/googlemock/src/gmock-internal-utils.cc \
|
||||
src/testing/googlemock/src/gmock-matchers.cc \
|
||||
src/testing/googlemock/src/gmock-spec-builders.cc \
|
||||
src/testing/googlemock/src/gmock_main.cc \
|
||||
src/third_party/curl/COPYING \
|
||||
src/third_party/curl/curlbuild.h \
|
||||
src/third_party/curl/curl.h \
|
||||
|
875
Makefile.in
875
Makefile.in
File diff suppressed because it is too large
Load Diff
12
README.md
12
README.md
@ -8,12 +8,13 @@ crash-reporting system.
|
||||
* [Bugs](https://bugs.chromium.org/p/google-breakpad/)
|
||||
* Discussion/Questions: [google-breakpad-discuss@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-discuss)
|
||||
* Developer/Reviews: [google-breakpad-dev@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-dev)
|
||||
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad)
|
||||
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad) [![Build status](https://ci.appveyor.com/api/projects/status/eguv4emv2rhq68u2?svg=true)](https://ci.appveyor.com/project/vapier/breakpad)
|
||||
* Coverage [![Coverity Status](https://scan.coverity.com/projects/9215/badge.svg)](https://scan.coverity.com/projects/google-breakpad)
|
||||
|
||||
## Getting started (from master)
|
||||
|
||||
1. First, [download depot_tools](http://dev.chromium.org/developers/how-tos/install-depot-tools)
|
||||
and ensure that they're in your `PATH`.
|
||||
and ensure that they’re in your `PATH`.
|
||||
|
||||
2. Create a new directory for checking out the source code (it must be named
|
||||
breakpad).
|
||||
@ -75,6 +76,7 @@ dependent repos are up-to-date.
|
||||
e.g. `git commit ... && git cl upload ...`
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
4. 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
|
||||
4. At https://chromium-review.googlesource.com/ you'll find your issue listed;
|
||||
click on it, then “Add reviewer”, and enter in the code reviewer. Depending
|
||||
on your settings, you may not see an email, but the reviewer has been
|
||||
notified with google-breakpad-dev@googlegroups.com always CC’d.
|
||||
|
6
aclocal.m4
vendored
6
aclocal.m4
vendored
@ -1295,3 +1295,9 @@ AC_SUBST([am__tar])
|
||||
AC_SUBST([am__untar])
|
||||
]) # _AM_PROG_TAR
|
||||
|
||||
m4_include([m4/ax_append_compile_flags.m4])
|
||||
m4_include([m4/ax_append_flag.m4])
|
||||
m4_include([m4/ax_check_compile_flag.m4])
|
||||
m4_include([m4/ax_cxx_compile_stdcxx.m4])
|
||||
m4_include([m4/ax_pthread.m4])
|
||||
m4_include([m4/ax_require_defined.m4])
|
||||
|
42
appveyor.yml
Normal file
42
appveyor.yml
Normal file
@ -0,0 +1,42 @@
|
||||
version: '{build}'
|
||||
|
||||
environment:
|
||||
GYP_MSVS_VERSION: 2013
|
||||
|
||||
platform:
|
||||
- Win32
|
||||
|
||||
configuration:
|
||||
- Debug
|
||||
- Release
|
||||
|
||||
# Use the source dir expected by gclient.
|
||||
clone_folder: c:\projects\breakpad\src
|
||||
|
||||
# Before checkout.
|
||||
init:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\..\..
|
||||
- appveyor DownloadFile https://storage.googleapis.com/chrome-infra/depot_tools.zip
|
||||
- 7z -bd x depot_tools.zip -odepot_tools
|
||||
- depot_tools\update_depot_tools
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
|
||||
# After checkout.
|
||||
install:
|
||||
- PATH C:\projects\depot_tools;%PATH%
|
||||
- cd %APPVEYOR_BUILD_FOLDER%\..
|
||||
- gclient config https://%APPVEYOR_REPO_PROVIDER%.com/%APPVEYOR_REPO_NAME% --unmanaged --name=src
|
||||
- gclient sync
|
||||
|
||||
build_script:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- msbuild src\client\windows\breakpad_client.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /m /verbosity:normal
|
||||
- msbuild src\tools\windows\tools_windows.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /m /verbosity:normal
|
||||
|
||||
test_script:
|
||||
- src\client\windows\%CONFIGURATION%\client_tests.exe
|
||||
- src\tools\windows\%CONFIGURATION%\dump_syms_unittest.exe
|
||||
|
||||
artifacts:
|
||||
- path: '**\*.exe'
|
||||
- path: '**\*.lib'
|
126
autotools/config.guess
vendored
126
autotools/config.guess
vendored
@ -1,8 +1,8 @@
|
||||
#! /bin/sh
|
||||
# Attempt to guess a canonical system name.
|
||||
# Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2017 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2016-01-01'
|
||||
timestamp='2017-02-07'
|
||||
|
||||
# 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
|
||||
@ -50,7 +50,7 @@ version="\
|
||||
GNU config.guess ($timestamp)
|
||||
|
||||
Originally written by Per Bothner.
|
||||
Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
Copyright 1992-2017 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."
|
||||
@ -186,9 +186,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
*) machine=${UNAME_MACHINE_ARCH}-unknown ;;
|
||||
esac
|
||||
# The Operating System including object format, if it has switched
|
||||
# to ELF recently, or will in the future.
|
||||
# to ELF recently (or will in the future) and ABI.
|
||||
case "${UNAME_MACHINE_ARCH}" in
|
||||
arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax)
|
||||
earm*)
|
||||
os=netbsdelf
|
||||
;;
|
||||
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
|
||||
eval $set_cc_for_build
|
||||
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
|
||||
| grep -q __ELF__
|
||||
@ -237,6 +240,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
|
||||
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
|
||||
exit ;;
|
||||
*:LibertyBSD:*:*)
|
||||
UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
|
||||
echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE}
|
||||
exit ;;
|
||||
*:ekkoBSD:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
|
||||
exit ;;
|
||||
@ -268,42 +275,42 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
|
||||
case "$ALPHA_CPU_TYPE" in
|
||||
"EV4 (21064)")
|
||||
UNAME_MACHINE="alpha" ;;
|
||||
UNAME_MACHINE=alpha ;;
|
||||
"EV4.5 (21064)")
|
||||
UNAME_MACHINE="alpha" ;;
|
||||
UNAME_MACHINE=alpha ;;
|
||||
"LCA4 (21066/21068)")
|
||||
UNAME_MACHINE="alpha" ;;
|
||||
UNAME_MACHINE=alpha ;;
|
||||
"EV5 (21164)")
|
||||
UNAME_MACHINE="alphaev5" ;;
|
||||
UNAME_MACHINE=alphaev5 ;;
|
||||
"EV5.6 (21164A)")
|
||||
UNAME_MACHINE="alphaev56" ;;
|
||||
UNAME_MACHINE=alphaev56 ;;
|
||||
"EV5.6 (21164PC)")
|
||||
UNAME_MACHINE="alphapca56" ;;
|
||||
UNAME_MACHINE=alphapca56 ;;
|
||||
"EV5.7 (21164PC)")
|
||||
UNAME_MACHINE="alphapca57" ;;
|
||||
UNAME_MACHINE=alphapca57 ;;
|
||||
"EV6 (21264)")
|
||||
UNAME_MACHINE="alphaev6" ;;
|
||||
UNAME_MACHINE=alphaev6 ;;
|
||||
"EV6.7 (21264A)")
|
||||
UNAME_MACHINE="alphaev67" ;;
|
||||
UNAME_MACHINE=alphaev67 ;;
|
||||
"EV6.8CB (21264C)")
|
||||
UNAME_MACHINE="alphaev68" ;;
|
||||
UNAME_MACHINE=alphaev68 ;;
|
||||
"EV6.8AL (21264B)")
|
||||
UNAME_MACHINE="alphaev68" ;;
|
||||
UNAME_MACHINE=alphaev68 ;;
|
||||
"EV6.8CX (21264D)")
|
||||
UNAME_MACHINE="alphaev68" ;;
|
||||
UNAME_MACHINE=alphaev68 ;;
|
||||
"EV6.9A (21264/EV69A)")
|
||||
UNAME_MACHINE="alphaev69" ;;
|
||||
UNAME_MACHINE=alphaev69 ;;
|
||||
"EV7 (21364)")
|
||||
UNAME_MACHINE="alphaev7" ;;
|
||||
UNAME_MACHINE=alphaev7 ;;
|
||||
"EV7.9 (21364A)")
|
||||
UNAME_MACHINE="alphaev79" ;;
|
||||
UNAME_MACHINE=alphaev79 ;;
|
||||
esac
|
||||
# A Pn.n version is a patched version.
|
||||
# A Vn.n version is a released version.
|
||||
# A Tn.n version is a released field test version.
|
||||
# A Xn.n version is an unreleased experimental baselevel.
|
||||
# 1.2 uses "1.2" for uname -r.
|
||||
echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
|
||||
echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
|
||||
# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
|
||||
exitcode=$?
|
||||
trap '' 0
|
||||
@ -376,16 +383,16 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
exit ;;
|
||||
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
|
||||
eval $set_cc_for_build
|
||||
SUN_ARCH="i386"
|
||||
SUN_ARCH=i386
|
||||
# If there is a compiler, see if it is configured for 64-bit objects.
|
||||
# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
|
||||
# This test works for both compilers.
|
||||
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
|
||||
if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
|
||||
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
|
||||
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
|
||||
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
|
||||
grep IS_64BIT_ARCH >/dev/null
|
||||
then
|
||||
SUN_ARCH="x86_64"
|
||||
SUN_ARCH=x86_64
|
||||
fi
|
||||
fi
|
||||
echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
|
||||
@ -410,7 +417,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||
exit ;;
|
||||
sun*:*:4.2BSD:*)
|
||||
UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
|
||||
test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
|
||||
test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3
|
||||
case "`/bin/arch`" in
|
||||
sun3)
|
||||
echo m68k-sun-sunos${UNAME_RELEASE}
|
||||
@ -635,13 +642,13 @@ EOF
|
||||
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
|
||||
sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
|
||||
case "${sc_cpu_version}" in
|
||||
523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
|
||||
528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
|
||||
523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
|
||||
528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
|
||||
532) # CPU_PA_RISC2_0
|
||||
case "${sc_kernel_bits}" in
|
||||
32) HP_ARCH="hppa2.0n" ;;
|
||||
64) HP_ARCH="hppa2.0w" ;;
|
||||
'') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
|
||||
32) HP_ARCH=hppa2.0n ;;
|
||||
64) HP_ARCH=hppa2.0w ;;
|
||||
'') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
|
||||
esac ;;
|
||||
esac
|
||||
fi
|
||||
@ -680,11 +687,11 @@ EOF
|
||||
exit (0);
|
||||
}
|
||||
EOF
|
||||
(CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
|
||||
(CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
|
||||
test -z "$HP_ARCH" && HP_ARCH=hppa
|
||||
fi ;;
|
||||
esac
|
||||
if [ ${HP_ARCH} = "hppa2.0w" ]
|
||||
if [ ${HP_ARCH} = hppa2.0w ]
|
||||
then
|
||||
eval $set_cc_for_build
|
||||
|
||||
@ -697,12 +704,12 @@ EOF
|
||||
# $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
|
||||
# => hppa64-hp-hpux11.23
|
||||
|
||||
if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
|
||||
if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
|
||||
grep -q __LP64__
|
||||
then
|
||||
HP_ARCH="hppa2.0w"
|
||||
HP_ARCH=hppa2.0w
|
||||
else
|
||||
HP_ARCH="hppa64"
|
||||
HP_ARCH=hppa64
|
||||
fi
|
||||
fi
|
||||
echo ${HP_ARCH}-hp-hpux${HPUX_REV}
|
||||
@ -807,14 +814,14 @@ EOF
|
||||
echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
|
||||
exit ;;
|
||||
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
|
||||
FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
|
||||
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
|
||||
FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
|
||||
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
|
||||
FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
|
||||
echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
|
||||
exit ;;
|
||||
5000:UNIX_System_V:4.*:*)
|
||||
FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
|
||||
FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
|
||||
FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
|
||||
FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
|
||||
echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
|
||||
exit ;;
|
||||
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
|
||||
@ -896,7 +903,7 @@ EOF
|
||||
exit ;;
|
||||
*:GNU/*:*:*)
|
||||
# other systems with GNU libc and userland
|
||||
echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
|
||||
echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
|
||||
exit ;;
|
||||
i*86:Minix:*:*)
|
||||
echo ${UNAME_MACHINE}-pc-minix
|
||||
@ -919,7 +926,7 @@ EOF
|
||||
EV68*) UNAME_MACHINE=alphaev68 ;;
|
||||
esac
|
||||
objdump --private-headers /bin/sh | grep -q ld.so.1
|
||||
if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
|
||||
if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
arc:Linux:*:* | arceb:Linux:*:*)
|
||||
@ -993,6 +1000,9 @@ EOF
|
||||
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
|
||||
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
|
||||
;;
|
||||
mips64el:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
openrisc*:Linux:*:*)
|
||||
echo or1k-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
@ -1025,6 +1035,9 @@ EOF
|
||||
ppcle:Linux:*:*)
|
||||
echo powerpcle-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
riscv32:Linux:*:* | riscv64:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
s390:Linux:*:* | s390x:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
|
||||
exit ;;
|
||||
@ -1272,6 +1285,9 @@ EOF
|
||||
SX-8R:SUPER-UX:*:*)
|
||||
echo sx8r-nec-superux${UNAME_RELEASE}
|
||||
exit ;;
|
||||
SX-ACE:SUPER-UX:*:*)
|
||||
echo sxace-nec-superux${UNAME_RELEASE}
|
||||
exit ;;
|
||||
Power*:Rhapsody:*:*)
|
||||
echo powerpc-apple-rhapsody${UNAME_RELEASE}
|
||||
exit ;;
|
||||
@ -1285,9 +1301,9 @@ EOF
|
||||
UNAME_PROCESSOR=powerpc
|
||||
fi
|
||||
if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
|
||||
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
|
||||
if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
|
||||
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
|
||||
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
|
||||
(CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
|
||||
grep IS_64BIT_ARCH >/dev/null
|
||||
then
|
||||
case $UNAME_PROCESSOR in
|
||||
@ -1309,7 +1325,7 @@ EOF
|
||||
exit ;;
|
||||
*:procnto*:*:* | *:QNX:[0123456789]*:*)
|
||||
UNAME_PROCESSOR=`uname -p`
|
||||
if test "$UNAME_PROCESSOR" = "x86"; then
|
||||
if test "$UNAME_PROCESSOR" = x86; then
|
||||
UNAME_PROCESSOR=i386
|
||||
UNAME_MACHINE=pc
|
||||
fi
|
||||
@ -1327,6 +1343,9 @@ EOF
|
||||
NSR-?:NONSTOP_KERNEL:*:*)
|
||||
echo nsr-tandem-nsk${UNAME_RELEASE}
|
||||
exit ;;
|
||||
NSX-?:NONSTOP_KERNEL:*:*)
|
||||
echo nsx-tandem-nsk${UNAME_RELEASE}
|
||||
exit ;;
|
||||
*:NonStop-UX:*:*)
|
||||
echo mips-compaq-nonstopux
|
||||
exit ;;
|
||||
@ -1340,7 +1359,7 @@ EOF
|
||||
# "uname -m" is not consistent, so use $cputype instead. 386
|
||||
# is converted to i386 for consistency with other x86
|
||||
# operating systems.
|
||||
if test "$cputype" = "386"; then
|
||||
if test "$cputype" = 386; then
|
||||
UNAME_MACHINE=i386
|
||||
else
|
||||
UNAME_MACHINE="$cputype"
|
||||
@ -1382,7 +1401,7 @@ EOF
|
||||
echo i386-pc-xenix
|
||||
exit ;;
|
||||
i*86:skyos:*:*)
|
||||
echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
|
||||
echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'`
|
||||
exit ;;
|
||||
i*86:rdos:*:*)
|
||||
echo ${UNAME_MACHINE}-pc-rdos
|
||||
@ -1401,18 +1420,17 @@ esac
|
||||
cat >&2 <<EOF
|
||||
$0: unable to guess system type
|
||||
|
||||
This script, last modified $timestamp, has failed to recognize
|
||||
the operating system you are using. It is advised that you
|
||||
download the most up to date version of the config scripts from
|
||||
This script (version $timestamp), has failed to recognize the
|
||||
operating system you are using. If your script is old, overwrite
|
||||
config.guess and config.sub with the latest versions from:
|
||||
|
||||
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
|
||||
and
|
||||
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
|
||||
|
||||
If the version you run ($0) is already up to date, please
|
||||
send the following data and any information you think might be
|
||||
pertinent to <config-patches@gnu.org> in order to provide the needed
|
||||
information to handle your system.
|
||||
If $0 has already been updated, send the following data and any
|
||||
information you think might be pertinent to config-patches@gnu.org to
|
||||
provide the necessary information to handle your system.
|
||||
|
||||
config.guess timestamp = $timestamp
|
||||
|
||||
|
38
autotools/config.sub
vendored
38
autotools/config.sub
vendored
@ -1,8 +1,8 @@
|
||||
#! /bin/sh
|
||||
# Configuration validation subroutine script.
|
||||
# Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2017 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2016-01-01'
|
||||
timestamp='2017-02-07'
|
||||
|
||||
# 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
|
||||
@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
||||
version="\
|
||||
GNU config.sub ($timestamp)
|
||||
|
||||
Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
Copyright 1992-2017 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 @@ 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* | netbsd*-eabi* | \
|
||||
kopensolaris*-gnu* | \
|
||||
kopensolaris*-gnu* | cloudabi*-eabi* | \
|
||||
storm-chaos* | os2-emx* | rtmk-nova*)
|
||||
os=-$maybe_os
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
|
||||
@ -301,6 +301,7 @@ case $basic_machine in
|
||||
| open8 | or1k | or1knd | or32 \
|
||||
| pdp10 | pdp11 | pj | pjl \
|
||||
| powerpc | powerpc64 | powerpc64le | powerpcle \
|
||||
| pru \
|
||||
| pyramid \
|
||||
| riscv32 | riscv64 \
|
||||
| rl78 | rx \
|
||||
@ -428,6 +429,7 @@ case $basic_machine in
|
||||
| orion-* \
|
||||
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
||||
| pru-* \
|
||||
| pyramid-* \
|
||||
| riscv32-* | riscv64-* \
|
||||
| rl78-* | romp-* | rs6000-* | rx-* \
|
||||
@ -643,6 +645,14 @@ case $basic_machine in
|
||||
basic_machine=m68k-bull
|
||||
os=-sysv3
|
||||
;;
|
||||
e500v[12])
|
||||
basic_machine=powerpc-unknown
|
||||
os=$os"spe"
|
||||
;;
|
||||
e500v[12]-*)
|
||||
basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
os=$os"spe"
|
||||
;;
|
||||
ebmon29k)
|
||||
basic_machine=a29k-amd
|
||||
os=-ebmon
|
||||
@ -938,6 +948,9 @@ case $basic_machine in
|
||||
nsr-tandem)
|
||||
basic_machine=nsr-tandem
|
||||
;;
|
||||
nsx-tandem)
|
||||
basic_machine=nsx-tandem
|
||||
;;
|
||||
op50n-* | op60c-*)
|
||||
basic_machine=hppa1.1-oki
|
||||
os=-proelf
|
||||
@ -1022,7 +1035,7 @@ case $basic_machine in
|
||||
ppc-* | ppcbe-*)
|
||||
basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
ppcle | powerpclittle | ppc-le | powerpc-little)
|
||||
ppcle | powerpclittle)
|
||||
basic_machine=powerpcle-unknown
|
||||
;;
|
||||
ppcle-* | powerpclittle-*)
|
||||
@ -1032,7 +1045,7 @@ case $basic_machine in
|
||||
;;
|
||||
ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
|
||||
;;
|
||||
ppc64le | powerpc64little | ppc64-le | powerpc64-little)
|
||||
ppc64le | powerpc64little)
|
||||
basic_machine=powerpc64le-unknown
|
||||
;;
|
||||
ppc64le-* | powerpc64little-*)
|
||||
@ -1382,14 +1395,14 @@ case $os in
|
||||
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
||||
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
|
||||
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
|
||||
| -bitrig* | -openbsd* | -solidbsd* \
|
||||
| -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
|
||||
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
|
||||
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
|
||||
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
|
||||
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
|
||||
| -chorusos* | -chorusrdb* | -cegcc* \
|
||||
| -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
|
||||
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
|
||||
| -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
|
||||
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
|
||||
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
||||
@ -1399,7 +1412,7 @@ case $os in
|
||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
|
||||
| -onefs* | -tirtos*)
|
||||
| -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*)
|
||||
# Remember, each alternative MUST END IN *, to match a version number.
|
||||
;;
|
||||
-qnx*)
|
||||
@ -1531,6 +1544,8 @@ case $os in
|
||||
;;
|
||||
-nacl*)
|
||||
;;
|
||||
-ios)
|
||||
;;
|
||||
-none)
|
||||
;;
|
||||
*)
|
||||
@ -1626,6 +1641,9 @@ case $basic_machine in
|
||||
sparc-* | *-sun)
|
||||
os=-sunos4.1.1
|
||||
;;
|
||||
pru-*)
|
||||
os=-elf
|
||||
;;
|
||||
*-be)
|
||||
os=-beos
|
||||
;;
|
||||
|
@ -1,5 +1,4 @@
|
||||
# This file is used by gcl to get repository specific information.
|
||||
CODE_REVIEW_SERVER: codereview.chromium.org
|
||||
CC_LIST: google-breakpad-dev@googlegroups.com
|
||||
TRY_ON_UPLOAD: False
|
||||
GERRIT_HOST: True
|
||||
GERRIT_SQUASH_UPLOADS: True
|
||||
CODE_REVIEW_SERVER: chromium-review.googlesource.com
|
||||
VIEW_VC: https://chromium.googlesource.com/breakpad/breakpad/+/
|
||||
|
349
configure
vendored
349
configure
vendored
@ -626,6 +626,8 @@ ac_subst_vars='am__EXEEXT_FALSE
|
||||
am__EXEEXT_TRUE
|
||||
LTLIBOBJS
|
||||
LIBOBJS
|
||||
RUST_DEMANGLE_LIBS
|
||||
RUST_DEMANGLE_CFLAGS
|
||||
SELFTEST_FALSE
|
||||
SELFTEST_TRUE
|
||||
GTEST_LIBS
|
||||
@ -646,6 +648,7 @@ ANDROID_HOST_FALSE
|
||||
ANDROID_HOST_TRUE
|
||||
LINUX_HOST_FALSE
|
||||
LINUX_HOST_TRUE
|
||||
WARN_CXXFLAGS
|
||||
HAVE_CXX11
|
||||
PTHREAD_CFLAGS
|
||||
PTHREAD_LIBS
|
||||
@ -653,8 +656,6 @@ PTHREAD_CC
|
||||
ax_pthread_config
|
||||
EGREP
|
||||
GREP
|
||||
GCC_FALSE
|
||||
GCC_TRUE
|
||||
RANLIB
|
||||
am__fastdepCXX_FALSE
|
||||
am__fastdepCXX_TRUE
|
||||
@ -775,6 +776,7 @@ enable_processor
|
||||
enable_tools
|
||||
enable_system_test_libs
|
||||
enable_selftest
|
||||
with_rust_demangle
|
||||
'
|
||||
ac_precious_vars='build_alias
|
||||
host_alias
|
||||
@ -795,7 +797,9 @@ GMOCK_CFLAGS
|
||||
GMOCK_LIBS
|
||||
GTEST_CONFIG
|
||||
GTEST_CFLAGS
|
||||
GTEST_LIBS'
|
||||
GTEST_LIBS
|
||||
RUST_DEMANGLE_CFLAGS
|
||||
RUST_DEMANGLE_LIBS'
|
||||
|
||||
|
||||
# Initialize some variables set by options.
|
||||
@ -1433,6 +1437,14 @@ Optional Features:
|
||||
--enable-selftest Run extra tests with "make check" (may conflict with
|
||||
optimizations) (default is no)
|
||||
|
||||
Optional Packages:
|
||||
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
|
||||
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
|
||||
--with-rust-demangle=/path/to/rust-demangle-capi
|
||||
Link against the rust-demangle library to demangle
|
||||
Rust language symbols during symbol dumping (default
|
||||
is no) Pass the path to the crate root.
|
||||
|
||||
Some influential environment variables:
|
||||
CC C compiler command
|
||||
CFLAGS C compiler flags
|
||||
@ -1456,6 +1468,10 @@ Some influential environment variables:
|
||||
GTEST_CFLAGS
|
||||
Compiler flags for gtest
|
||||
GTEST_LIBS Linker flags for gtest
|
||||
RUST_DEMANGLE_CFLAGS
|
||||
Compiler flags for rust-demangle
|
||||
RUST_DEMANGLE_LIBS
|
||||
Linker flags for rust-demangle
|
||||
|
||||
Use these variables to override the choices made by `configure' or to help
|
||||
it to find libraries and programs with nonstandard names/locations.
|
||||
@ -5720,14 +5736,6 @@ else
|
||||
RANLIB="$ac_cv_prog_RANLIB"
|
||||
fi
|
||||
|
||||
if test "$GCC" = yes; then
|
||||
GCC_TRUE=
|
||||
GCC_FALSE='#'
|
||||
else
|
||||
GCC_TRUE='#'
|
||||
GCC_FALSE=
|
||||
fi
|
||||
# let the Makefile know if we're gcc
|
||||
|
||||
# Check whether --enable-m32 was given.
|
||||
if test "${enable_m32+set}" = set; then :
|
||||
@ -6192,104 +6200,6 @@ rm -rf conftest*
|
||||
|
||||
fi
|
||||
|
||||
# ===========================================================================
|
||||
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# This macro figures out how to build C programs using POSIX threads. It
|
||||
# sets the PTHREAD_LIBS output variable to the threads library and linker
|
||||
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
|
||||
# flags that are needed. (The user can also force certain compiler
|
||||
# flags/libs to be tested by setting these environment variables.)
|
||||
#
|
||||
# Also sets PTHREAD_CC to any special C compiler that is needed for
|
||||
# multi-threaded programs (defaults to the value of CC otherwise). (This
|
||||
# is necessary on AIX to use the special cc_r compiler alias.)
|
||||
#
|
||||
# NOTE: You are assumed to not only compile your program with these flags,
|
||||
# but also link it with them as well. e.g. you should link with
|
||||
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
|
||||
#
|
||||
# If you are only building threads programs, you may wish to use these
|
||||
# variables in your default LIBS, CFLAGS, and CC:
|
||||
#
|
||||
# LIBS="$PTHREAD_LIBS $LIBS"
|
||||
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
|
||||
# CC="$PTHREAD_CC"
|
||||
#
|
||||
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
|
||||
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
|
||||
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
|
||||
#
|
||||
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
|
||||
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
|
||||
# is not found. If ACTION-IF-FOUND is not specified, the default action
|
||||
# will define HAVE_PTHREAD.
|
||||
#
|
||||
# Please let the authors know if this macro fails on any platform, or if
|
||||
# you have any other suggestions or comments. This macro was based on work
|
||||
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
|
||||
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
|
||||
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
|
||||
# grateful for the helpful feedback of numerous users.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 6
|
||||
|
||||
# This is what autoupdate's m4 run will expand. It fires
|
||||
# the warning (with _au_warn_XXX), outputs it into the
|
||||
# updated configure.ac (with AC_DIAGNOSE), and then outputs
|
||||
# the replacement expansion.
|
||||
|
||||
|
||||
# This is an auxiliary macro that is also run when
|
||||
# autoupdate runs m4. It simply calls m4_warning, but
|
||||
# we need a wrapper so that each warning is emitted only
|
||||
# once. We break the quoting in m4_warning's argument in
|
||||
# order to expand this macro's arguments, not AU_DEFUN's.
|
||||
|
||||
|
||||
# Finally, this is the expansion that is picked up by
|
||||
# autoconf. It tells the user to run autoupdate, and
|
||||
# then outputs the replacement expansion. We do not care
|
||||
# about autoupdate's warning because that contains
|
||||
# information on what to do *after* running autoupdate.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -6675,68 +6585,6 @@ fi
|
||||
done
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
ax_cxx_compile_cxx11_required=true
|
||||
ac_ext=cpp
|
||||
@ -7397,6 +7245,136 @@ $as_echo "#define HAVE_CXX11 1" >>confdefs.h
|
||||
fi
|
||||
|
||||
|
||||
WARN_CXXFLAGS=
|
||||
ac_ext=cpp
|
||||
ac_cpp='$CXXCPP $CPPFLAGS'
|
||||
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
|
||||
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Werror=unknown-warning-option" >&5
|
||||
$as_echo_n "checking whether C++ compiler accepts -Werror=unknown-warning-option... " >&6; }
|
||||
if ${ax_cv_check_cxxflags___Werror_unknown_warning_option+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
ax_check_save_flags=$CXXFLAGS
|
||||
CXXFLAGS="$CXXFLAGS -Werror=unknown-warning-option"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
ax_cv_check_cxxflags___Werror_unknown_warning_option=yes
|
||||
else
|
||||
ax_cv_check_cxxflags___Werror_unknown_warning_option=no
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
CXXFLAGS=$ax_check_save_flags
|
||||
fi
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Werror_unknown_warning_option" >&5
|
||||
$as_echo "$ax_cv_check_cxxflags___Werror_unknown_warning_option" >&6; }
|
||||
if test "x$ax_cv_check_cxxflags___Werror_unknown_warning_option" = xyes; then :
|
||||
|
||||
ax_compiler_flags_test="-Werror=unknown-warning-option"
|
||||
|
||||
else
|
||||
|
||||
ax_compiler_flags_test=""
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for flag in -Wmissing-braces -Wnon-virtual-dtor -Woverloaded-virtual -Wreorder -Wsign-compare -Wunused-variable -Wvla ; do
|
||||
as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_${ax_compiler_flags_test}_$flag" | $as_tr_sh`
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts $flag" >&5
|
||||
$as_echo_n "checking whether C++ compiler accepts $flag... " >&6; }
|
||||
if eval \${$as_CACHEVAR+:} false; then :
|
||||
$as_echo_n "(cached) " >&6
|
||||
else
|
||||
|
||||
ax_check_save_flags=$CXXFLAGS
|
||||
CXXFLAGS="$CXXFLAGS ${ax_compiler_flags_test} $flag"
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_cxx_try_compile "$LINENO"; then :
|
||||
eval "$as_CACHEVAR=yes"
|
||||
else
|
||||
eval "$as_CACHEVAR=no"
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
|
||||
CXXFLAGS=$ax_check_save_flags
|
||||
fi
|
||||
eval ac_res=\$$as_CACHEVAR
|
||||
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
|
||||
$as_echo "$ac_res" >&6; }
|
||||
if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then :
|
||||
|
||||
if ${WARN_CXXFLAGS+:} false; then :
|
||||
|
||||
case " $WARN_CXXFLAGS " in #(
|
||||
*" $flag "*) :
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS already contains \$flag"; } >&5
|
||||
(: WARN_CXXFLAGS already contains $flag) 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; } ;; #(
|
||||
*) :
|
||||
|
||||
as_fn_append WARN_CXXFLAGS " $flag"
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5
|
||||
(: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }
|
||||
;;
|
||||
esac
|
||||
|
||||
else
|
||||
|
||||
WARN_CXXFLAGS=$flag
|
||||
{ { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5
|
||||
(: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5
|
||||
ac_status=$?
|
||||
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
|
||||
test $ac_status = 0; }
|
||||
|
||||
fi
|
||||
|
||||
else
|
||||
:
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
as_fn_append WARN_CXXFLAGS " -Werror"
|
||||
ac_ext=c
|
||||
ac_cpp='$CPP $CPPFLAGS'
|
||||
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
|
||||
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
|
||||
ac_compiler_gnu=$ac_cv_c_compiler_gnu
|
||||
|
||||
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
@ -7747,6 +7725,31 @@ else
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check whether --with-rust-demangle was given.
|
||||
if test "${with_rust_demangle+set}" = set; then :
|
||||
withval=$with_rust_demangle; case "${withval}" in
|
||||
yes)
|
||||
as_fn_error $? "You must pass the path to the rust-demangle-capi crate for --with-rust-demangle" "$LINENO" 5
|
||||
;;
|
||||
no)
|
||||
rust_demangle=false
|
||||
;;
|
||||
*)
|
||||
if ! test -f "${withval}/Cargo.toml"; then
|
||||
as_fn_error $? "You must pass the path to the rust-demangle-capi crate for --with-rust-demangle" "$LINENO" 5
|
||||
fi
|
||||
RUST_DEMANGLE_CFLAGS="-DHAVE_RUST_DEMANGLE -I${withval}/target/include"
|
||||
RUST_DEMANGLE_LIBS="-L${withval}/target/release -lrust_demangle -lpthread -ldl"
|
||||
;;
|
||||
esac
|
||||
else
|
||||
rust_demangle=false
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
ac_config_files="$ac_config_files breakpad.pc breakpad-client.pc Makefile"
|
||||
|
||||
|
||||
@ -7898,10 +7901,6 @@ if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then
|
||||
as_fn_error $? "conditional \"am__fastdepCXX\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${GCC_TRUE}" && test -z "${GCC_FALSE}"; then
|
||||
as_fn_error $? "conditional \"GCC\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
fi
|
||||
if test -z "${LINUX_HOST_TRUE}" && test -z "${LINUX_HOST_FALSE}"; then
|
||||
as_fn_error $? "conditional \"LINUX_HOST\" was never defined.
|
||||
Usually this means the macro was only invoked conditionally." "$LINENO" 5
|
||||
|
53
configure.ac
53
configure.ac
@ -28,7 +28,7 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
AC_PREREQ(2.57)
|
||||
AC_PREREQ(2.64)
|
||||
|
||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
|
||||
dnl Sanity check: the argument is just a file that should exist.
|
||||
@ -48,7 +48,6 @@ AM_PROG_CC_C_O
|
||||
AC_PROG_CPP
|
||||
AC_PROG_CXX
|
||||
AC_PROG_RANLIB
|
||||
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
|
||||
|
||||
dnl This must come before all the feature tests below.
|
||||
AC_ARG_ENABLE(m32,
|
||||
@ -72,13 +71,34 @@ AC_ARG_ENABLE(m32,
|
||||
|
||||
AC_HEADER_STDC
|
||||
AC_SYS_LARGEFILE
|
||||
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)
|
||||
|
||||
dnl Test supported warning flags.
|
||||
WARN_CXXFLAGS=
|
||||
dnl This warning flag is used by clang. Its default behavior is to warn when
|
||||
dnl given an unknown flag rather than error out.
|
||||
AC_LANG_PUSH([C++])
|
||||
AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[
|
||||
ax_compiler_flags_test="-Werror=unknown-warning-option"
|
||||
],[
|
||||
ax_compiler_flags_test=""
|
||||
])
|
||||
AX_APPEND_COMPILE_FLAGS(m4_flatten([
|
||||
-Wmissing-braces
|
||||
-Wnon-virtual-dtor
|
||||
-Woverloaded-virtual
|
||||
-Wreorder
|
||||
-Wsign-compare
|
||||
-Wunused-variable
|
||||
-Wvla
|
||||
]), [WARN_CXXFLAGS], [${ax_compiler_flags_test}])
|
||||
AS_VAR_APPEND([WARN_CXXFLAGS], " -Werror")
|
||||
AC_LANG_POP([C++])
|
||||
AC_SUBST([WARN_CXXFLAGS])
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
@ -195,6 +215,31 @@ AC_ARG_ENABLE(selftest,
|
||||
[selftest=false])
|
||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
||||
|
||||
AC_ARG_WITH(rust-demangle,
|
||||
AS_HELP_STRING([--with-rust-demangle=/path/to/rust-demangle-capi],
|
||||
[Link against the rust-demangle library]
|
||||
[to demangle Rust language symbols during]
|
||||
[symbol dumping (default is no)]
|
||||
[Pass the path to the crate root.]),
|
||||
[case "${withval}" in
|
||||
yes)
|
||||
AC_MSG_ERROR(You must pass the path to the rust-demangle-capi crate for --with-rust-demangle)
|
||||
;;
|
||||
no)
|
||||
rust_demangle=false
|
||||
;;
|
||||
*)
|
||||
if ! test -f "${withval}/Cargo.toml"; then
|
||||
AC_MSG_ERROR(You must pass the path to the rust-demangle-capi crate for --with-rust-demangle)
|
||||
fi
|
||||
RUST_DEMANGLE_CFLAGS="-DHAVE_RUST_DEMANGLE -I${withval}/target/include"
|
||||
RUST_DEMANGLE_LIBS="-L${withval}/target/release -lrust_demangle -lpthread -ldl"
|
||||
;;
|
||||
esac],
|
||||
[rust_demangle=false])
|
||||
AC_ARG_VAR([RUST_DEMANGLE_CFLAGS], [Compiler flags for rust-demangle])
|
||||
AC_ARG_VAR([RUST_DEMANGLE_LIBS], [Linker flags for rust-demangle])
|
||||
|
||||
AC_CONFIG_FILES(m4_flatten([
|
||||
breakpad.pc
|
||||
breakpad-client.pc
|
||||
|
48
default.xml
Normal file
48
default.xml
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<!-- AUTOGENERATED BY deps-to-manifest.py; DO NOT EDIT -->
|
||||
<manifest>
|
||||
|
||||
<default revision='refs/heads/master'
|
||||
remote='chromium'
|
||||
sync-c='true'
|
||||
sync-j='8' />
|
||||
|
||||
<remote name='github'
|
||||
fetch='https://github.com/'
|
||||
review='' />
|
||||
|
||||
<remote name='chromium'
|
||||
fetch='https://chromium.googlesource.com/'
|
||||
review='https://chromium-review.googlesource.com' />
|
||||
|
||||
<project path='src'
|
||||
name='breakpad/breakpad'
|
||||
revision='refs/heads/master'
|
||||
remote='chromium' />
|
||||
|
||||
<project path='src/src/tools/gyp'
|
||||
name='external/gyp/'
|
||||
revision='e8ab0833a42691cd2184bd4c45d779e43821d3e0'
|
||||
remote='chromium' />
|
||||
|
||||
<project path='src/src/third_party/glog'
|
||||
name='google/glog.git'
|
||||
revision='refs/tags/v0.3.4'
|
||||
remote='github' />
|
||||
|
||||
<project path='src/src/testing'
|
||||
name='google/googletest.git'
|
||||
revision='refs/tags/release-1.8.0'
|
||||
remote='github' />
|
||||
|
||||
<project path='src/src/third_party/lss'
|
||||
name='linux-syscall-support/'
|
||||
revision='3f6478ac95edf86cd3da300c2c0d34a438f5dbeb'
|
||||
remote='chromium' />
|
||||
|
||||
<project path='src/src/third_party/protobuf/protobuf'
|
||||
name='google/protobuf.git'
|
||||
revision='cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac'
|
||||
remote='github' />
|
||||
|
||||
</manifest>
|
@ -69,8 +69,8 @@ amount of work from a crashed process.
|
||||
|
||||
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)
|
||||
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
|
||||
@ -178,9 +178,8 @@ didn’t actually handle an exception. This function may be useful for developer
|
||||
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/).
|
||||
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.
|
||||
|
@ -9,8 +9,7 @@ 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)
|
||||
![Workflow](breakpad.png)
|
||||
|
||||
Breakpad has three main components:
|
||||
|
||||
@ -90,7 +89,7 @@ known as "out-of-process" exception handling.
|
||||
## 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
|
||||
https://chromium.googlesource.com/breakpad/breakpad. The following directory structure is
|
||||
present in the `src` directory:
|
||||
|
||||
* `processor` Contains minidump-processing code that is used on the server
|
||||
@ -101,14 +100,9 @@ present in the `src` directory:
|
||||
|
||||
(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>
|
||||
* [Windows Integration Guide](windows_client_integration.md)
|
||||
* [Mac Integration Guide](mac_breakpad_starter_guide.md)
|
||||
* [Linux Integration Guide](linux_starter_guide.md)
|
||||
|
||||
## Build process specifics(symbol generation)
|
||||
|
||||
|
@ -75,10 +75,8 @@
|
||||
|
||||
## More Information
|
||||
|
||||
* Project home: http://code.google.com/p/google-breakpad/
|
||||
* Project home: https://chromium.googlesource.com/breakpad/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/)
|
||||
* [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)
|
||||
|
@ -13,8 +13,9 @@ 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
|
||||
[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
|
||||
@ -81,13 +82,12 @@ 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
|
||||
[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
|
||||
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
|
||||
@ -155,11 +155,12 @@ 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),
|
||||
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
|
||||
|
@ -8,16 +8,12 @@ traces from the information contained within a minidump file.
|
||||
## 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)
|
||||
[MinidumpProcessor](../src/processor/minidump_processor.cc)
|
||||
class and calling the [MinidumpProcessor::Process](../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).
|
||||
constructor: a [SymbolSupplier](../src/google_breakpad/processor/symbol_supplier.h)
|
||||
and a [SourceLineResolverInterface](../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
|
||||
@ -26,29 +22,26 @@ 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)),
|
||||
the list of threads from the process
|
||||
([MinidumpThreadList](../src/google_breakpad/processor/minidump.h#335)),
|
||||
the list of modules loaded in the process
|
||||
([MinidumpModuleList](../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)).
|
||||
([MinidumpException](../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))
|
||||
For each thread in the thread list
|
||||
([MinidumpThread](../src/google_breakpad/processor/minidump.h#299)),
|
||||
the thread memory containing the stack for the thread
|
||||
([MinidumpMemoryRegion](../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))
|
||||
dump was written ([MinidumpContext](../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)
|
||||
the [Stackwalker::StackwalkerForCPU](../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
|
||||
@ -57,8 +50,7 @@ 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)
|
||||
[Stackwalker::Walk](../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
|
||||
@ -69,28 +61,24 @@ for each frame in turn:
|
||||
|
||||
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.
|
||||
[GetModuleForAddress](../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)
|
||||
corresponding to the module by calling its
|
||||
[GetCStringSymbolData](../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)
|
||||
digit) as a lookup key. The [SimpleSymbolSupplier](../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)
|
||||
load the symbol file by calling its
|
||||
[LoadModuleUsingMemoryBuffer](../src/google_breakpad/processor/source_line_resolver_interface.h#71)
|
||||
method. The [BasicSourceLineResolver](../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.
|
||||
@ -98,8 +86,7 @@ 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)
|
||||
[FillSourceLineInfo](../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
|
||||
@ -119,17 +106,15 @@ 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)
|
||||
To find the next frame in the stack, the !Stackwalker calls its
|
||||
[GetCallerFrame](../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)
|
||||
[FindWindowsFrameInfo](../src/google_breakpad/processor/source_line_resolver_interface.h#96)
|
||||
and [FindCFIFrameInfo](../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)
|
||||
@ -148,12 +133,11 @@ 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)
|
||||
down to a fixed depth (implemented in the
|
||||
[Stackwalker::ScanForReturnAddress](../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).
|
||||
(implemented in the
|
||||
[Stackwalker::InstructionAddressSeemsValid](../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
|
||||
|
@ -197,10 +197,10 @@ 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.
|
||||
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
|
||||
@ -274,8 +274,7 @@ follows:
|
||||
* 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)
|
||||
[postfix\_evaluator.h](../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:
|
||||
|
67
m4/ax_append_compile_flags.m4
Normal file
67
m4/ax_append_compile_flags.m4
Normal file
@ -0,0 +1,67 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# For every FLAG1, FLAG2 it is checked whether the compiler works with the
|
||||
# flag. If it does, the flag is added FLAGS-VARIABLE
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. During the check the flag is always added to the
|
||||
# current language's flags.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: This macro depends on the AX_APPEND_FLAG and
|
||||
# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with
|
||||
# AX_APPEND_LINK_FLAGS.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 5
|
||||
|
||||
AC_DEFUN([AX_APPEND_COMPILE_FLAGS],
|
||||
[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG])
|
||||
AX_REQUIRE_DEFINED([AX_APPEND_FLAG])
|
||||
for flag in $1; do
|
||||
AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4])
|
||||
done
|
||||
])dnl AX_APPEND_COMPILE_FLAGS
|
71
m4/ax_append_flag.m4
Normal file
71
m4/ax_append_flag.m4
Normal file
@ -0,0 +1,71 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_append_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space
|
||||
# added in between.
|
||||
#
|
||||
# If FLAGS-VARIABLE is not specified, the current language's flags (e.g.
|
||||
# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains
|
||||
# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly
|
||||
# FLAG.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 6
|
||||
|
||||
AC_DEFUN([AX_APPEND_FLAG],
|
||||
[dnl
|
||||
AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF
|
||||
AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])])
|
||||
AS_VAR_SET_IF(FLAGS,[
|
||||
AS_CASE([" AS_VAR_GET(FLAGS) "],
|
||||
[*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])],
|
||||
[
|
||||
AS_VAR_APPEND(FLAGS,[" $1"])
|
||||
AC_RUN_LOG([: FLAGS="$FLAGS"])
|
||||
])
|
||||
],
|
||||
[
|
||||
AS_VAR_SET(FLAGS,[$1])
|
||||
AC_RUN_LOG([: FLAGS="$FLAGS"])
|
||||
])
|
||||
AS_VAR_POPDEF([FLAGS])dnl
|
||||
])dnl AX_APPEND_FLAG
|
74
m4/ax_check_compile_flag.m4
Normal file
74
m4/ax_check_compile_flag.m4
Normal file
@ -0,0 +1,74 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Check whether the given FLAG works with the current language's compiler
|
||||
# or gives an error. (Warnings, however, are ignored)
|
||||
#
|
||||
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
|
||||
# success/failure.
|
||||
#
|
||||
# If EXTRA-FLAGS is defined, it is added to the current language's default
|
||||
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
|
||||
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
|
||||
# force the compiler to issue an error when a bad flag is given.
|
||||
#
|
||||
# INPUT gives an alternative input source to AC_COMPILE_IFELSE.
|
||||
#
|
||||
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
|
||||
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
|
||||
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
# Free Software Foundation, either version 3 of the License, or (at your
|
||||
# option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
||||
# Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# As a special exception, the respective Autoconf Macro's copyright owner
|
||||
# gives unlimited permission to copy, distribute and modify the configure
|
||||
# scripts that are the output of Autoconf when processing the Macro. You
|
||||
# need not follow the terms of the GNU General Public License when using
|
||||
# or distributing such scripts, even though portions of the text of the
|
||||
# Macro appear in them. The GNU General Public License (GPL) does govern
|
||||
# all other use of the material that constitutes the Autoconf Macro.
|
||||
#
|
||||
# This special exception to the GPL applies to versions of the Autoconf
|
||||
# Macro released by the Autoconf Archive. When you make and distribute a
|
||||
# modified version of the Autoconf Macro, you may extend this special
|
||||
# exception to the GPL to apply to your modified version as well.
|
||||
|
||||
#serial 4
|
||||
|
||||
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
|
||||
[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF
|
||||
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
|
||||
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
|
||||
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
|
||||
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
|
||||
AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])],
|
||||
[AS_VAR_SET(CACHEVAR,[yes])],
|
||||
[AS_VAR_SET(CACHEVAR,[no])])
|
||||
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
|
||||
AS_VAR_IF(CACHEVAR,yes,
|
||||
[m4_default([$2], :)],
|
||||
[m4_default([$3], :)])
|
||||
AS_VAR_POPDEF([CACHEVAR])dnl
|
||||
])dnl AX_CHECK_COMPILE_FLAGS
|
@ -280,4 +280,4 @@ else
|
||||
$2
|
||||
fi
|
||||
AC_LANG_RESTORE
|
||||
])dnl AX_PTHREAD
|
||||
])dnl AX_PTHREAD
|
||||
|
37
m4/ax_require_defined.m4
Normal file
37
m4/ax_require_defined.m4
Normal file
@ -0,0 +1,37 @@
|
||||
# ===========================================================================
|
||||
# http://www.gnu.org/software/autoconf-archive/ax_require_defined.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_REQUIRE_DEFINED(MACRO)
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have
|
||||
# been defined and thus are available for use. This avoids random issues
|
||||
# where a macro isn't expanded. Instead the configure script emits a
|
||||
# non-fatal:
|
||||
#
|
||||
# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found
|
||||
#
|
||||
# It's like AC_REQUIRE except it doesn't expand the required macro.
|
||||
#
|
||||
# Here's an example:
|
||||
#
|
||||
# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG])
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2014 Mike Frysinger <vapier@gentoo.org>
|
||||
#
|
||||
# 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
|
||||
|
||||
AC_DEFUN([AX_REQUIRE_DEFINED], [dnl
|
||||
m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])])
|
||||
])dnl AX_REQUIRE_DEFINED
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
set -ex
|
||||
|
||||
@ -19,6 +19,28 @@ setup_env() {
|
||||
export JOBS=$(( $NCPUS < 4 ? $NCPUS : 4 ))
|
||||
}
|
||||
|
||||
# We have to do this by hand rather than use the coverity addon because of
|
||||
# matrix explosion: https://github.com/travis-ci/travis-ci/issues/1975
|
||||
# We also do it by hand because when we're throttled, the addon will exit
|
||||
# the build immediately and skip the main script!
|
||||
coverity_scan() {
|
||||
if [ "${COVERITY_SCAN}" != "true" ] || \
|
||||
[ -n "${TRAVIS_TAG}" ] || \
|
||||
[ "${TRAVIS_PULL_REQUEST}" = "true" ]
|
||||
then
|
||||
echo "Skipping coverity scan."
|
||||
return
|
||||
fi
|
||||
|
||||
export COVERITY_SCAN_PROJECT_NAME="${TRAVIS_REPO_SLUG}"
|
||||
export COVERITY_SCAN_NOTIFICATION_EMAIL="google-breakpad-dev@googlegroups.com"
|
||||
export COVERITY_SCAN_BUILD_COMMAND="make -j${JOBS}"
|
||||
export COVERITY_SCAN_BUILD_COMMAND_PREPEND="git clean -q -x -d -f; git checkout -f; ./configure"
|
||||
export COVERITY_SCAN_BRANCH_PATTERN="master"
|
||||
|
||||
curl -s "https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh" | bash || :
|
||||
}
|
||||
|
||||
# Do an in-tree build and make sure tests pass.
|
||||
build() {
|
||||
./configure
|
||||
@ -29,15 +51,20 @@ build() {
|
||||
# Do an out-of-tree build and make sure we can create a release tarball.
|
||||
build_out_of_tree() {
|
||||
mkdir -p build/native
|
||||
cd build/native
|
||||
pushd build/native >/dev/null
|
||||
../../configure
|
||||
make -j${JOBS} distcheck VERBOSE=1
|
||||
popd >/dev/null
|
||||
}
|
||||
|
||||
main() {
|
||||
setup_env
|
||||
build
|
||||
build_out_of_tree
|
||||
|
||||
# Do scans last as they like to dirty the tree and some tests
|
||||
# expect a clean tree (like code style checks).
|
||||
coverity_scan
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -ex
|
||||
|
||||
get_depot_tools() {
|
||||
|
131
scripts/windows/install_google_breakpad.py
Normal file
131
scripts/windows/install_google_breakpad.py
Normal file
@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import shutil
|
||||
import string
|
||||
import sys
|
||||
|
||||
|
||||
# parse args
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('src', help='google_breakpad root directory where build was done')
|
||||
parser.add_argument('dst', help='destination root directory where google_breakpad have to be installed')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
# test args
|
||||
if not os.path.isdir(args.src):
|
||||
print(args.src + ' not such directory')
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
||||
# customize args
|
||||
google_breakpad_src_dir = os.path.join( args.src, 'src' )
|
||||
dst_dir = os.path.join( args.dst )
|
||||
|
||||
|
||||
# function to define src and dst in copy function
|
||||
def convertSrcAndDst( srcFile, dstFile, subDir ):
|
||||
src = os.path.join(google_breakpad_src_dir, srcFile.replace('/', os.path.sep))
|
||||
dst = os.path.join(dst_dir, subDir, dstFile.replace('/', os.path.sep))
|
||||
return src, dst
|
||||
|
||||
|
||||
def copyFile(src, dst):
|
||||
# print what we will doing
|
||||
print (src + ' ==>> ' + dst )
|
||||
|
||||
# be sure dst directory exist or create it
|
||||
dst_dir = os.path.dirname(dst)
|
||||
if not os.path.exists(dst_dir):
|
||||
os.makedirs(dst_dir)
|
||||
|
||||
# copy
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
|
||||
|
||||
|
||||
google_breakpad_header_files = [
|
||||
'client/windows/common/ipc_protocol.h',
|
||||
'client/windows/crash_generation/client_info.h',
|
||||
'client/windows/crash_generation/crash_generation_client.h',
|
||||
'client/windows/crash_generation/crash_generation_server.h',
|
||||
'client/windows/crash_generation/minidump_generator.h',
|
||||
'client/windows/handler/exception_handler.h',
|
||||
'common/scoped_ptr.h',
|
||||
'common/windows/string_utils-inl.h',
|
||||
'common/windows/http_upload.h',
|
||||
'google_breakpad/common/breakpad_types.h',
|
||||
'google_breakpad/common/minidump_format.h',
|
||||
'google_breakpad/common/minidump_cpu_amd64.h',
|
||||
'google_breakpad/common/minidump_cpu_arm.h',
|
||||
'google_breakpad/common/minidump_cpu_arm64.h',
|
||||
'google_breakpad/common/minidump_cpu_mips.h',
|
||||
'google_breakpad/common/minidump_cpu_ppc.h',
|
||||
'google_breakpad/common/minidump_cpu_ppc64.h',
|
||||
'google_breakpad/common/minidump_cpu_sparc.h',
|
||||
'google_breakpad/common/minidump_cpu_x86.h',
|
||||
'google_breakpad/common/minidump_exception_linux.h',
|
||||
'google_breakpad/common/minidump_exception_mac.h',
|
||||
'google_breakpad/common/minidump_exception_ps3.h',
|
||||
'google_breakpad/common/minidump_exception_solaris.h',
|
||||
'google_breakpad/common/minidump_exception_win32.h'
|
||||
]
|
||||
|
||||
# copy headers
|
||||
for f in google_breakpad_header_files:
|
||||
src, dst = convertSrcAndDst(f, f, os.path.join('include', 'breakpad'))
|
||||
copyFile(src, dst)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
google_breakpad_library_dir_base = [
|
||||
'client/windows',
|
||||
'client/windows/crash_generation',
|
||||
'client/windows/handler'
|
||||
]
|
||||
google_breakpad_library_dir_end = 'lib'
|
||||
google_breakpad_library_files = [
|
||||
'crash_generation_client.lib',
|
||||
'crash_generation_server.lib',
|
||||
'common.lib',
|
||||
'exception_handler.lib'
|
||||
]
|
||||
|
||||
build_type = [
|
||||
'Debug',
|
||||
'Release'
|
||||
]
|
||||
|
||||
# copy libraries
|
||||
for dir in google_breakpad_library_dir_base:
|
||||
for buildType in build_type:
|
||||
for file in google_breakpad_library_files:
|
||||
path = os.path.join(dir, buildType, 'lib', file)
|
||||
src, dst = convertSrcAndDst(path, os.path.basename(path), os.path.join('lib', buildType))
|
||||
if os.path.exists(src):
|
||||
copyFile(src, dst)
|
||||
|
||||
|
||||
|
||||
google_breakpad_binary_dir_base = [
|
||||
'tools/windows/dump_syms'
|
||||
]
|
||||
google_breakpad_binary_files = [
|
||||
'dump_syms.exe'
|
||||
]
|
||||
|
||||
for dir in google_breakpad_binary_dir_base:
|
||||
for buildType in build_type:
|
||||
for file in google_breakpad_binary_files:
|
||||
path = os.path.join(dir, buildType, file)
|
||||
src, dst = convertSrcAndDst(path, os.path.basename(path), 'bin')
|
||||
if os.path.exists(src):
|
||||
copyFile(src, dst)
|
@ -251,8 +251,8 @@
|
||||
'win_release_Optimization%': '2', # 2 = /Os
|
||||
'win_debug_Optimization%': '0', # 0 = /Od
|
||||
# See http://msdn.microsoft.com/en-us/library/aa652367(VS.71).aspx
|
||||
'win_release_RuntimeLibrary%': '0', # 0 = /MT (nondebug static)
|
||||
'win_debug_RuntimeLibrary%': '1', # 1 = /MTd (debug static)
|
||||
'win_release_RuntimeLibrary%': '2', # 2 = /MD (nondebug dynamic)
|
||||
'win_debug_RuntimeLibrary%': '3', # 3 = /MDd (debug dynamic)
|
||||
|
||||
'release_extra_cflags%': '',
|
||||
'debug_extra_cflags%': '',
|
||||
@ -894,7 +894,7 @@
|
||||
],
|
||||
'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'],
|
||||
'msvs_disabled_warnings': [
|
||||
4100, 4127, 4396, 4503, 4512, 4819, 4995, 4702
|
||||
4091, 4100, 4127, 4396, 4503, 4512, 4819, 4995, 4702
|
||||
],
|
||||
'msvs_settings': {
|
||||
'VCCLCompilerTool': {
|
||||
|
@ -93,11 +93,12 @@ NSString* GetPlatform() {
|
||||
@implementation BreakpadController
|
||||
|
||||
+ (BreakpadController*)sharedInstance {
|
||||
@synchronized(self) {
|
||||
static BreakpadController* sharedInstance_ =
|
||||
[[BreakpadController alloc] initSingleton];
|
||||
return sharedInstance_;
|
||||
}
|
||||
static dispatch_once_t onceToken;
|
||||
static BreakpadController* sharedInstance ;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[BreakpadController alloc] initSingleton];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
@ -179,10 +180,9 @@ NSString* GetPlatform() {
|
||||
enableUploads_ = YES;
|
||||
[self sendStoredCrashReports];
|
||||
} else {
|
||||
// disable the enableUpload_ flag.
|
||||
// sendDelay checks this flag and disables the upload of logs by sendStoredCrashReports
|
||||
enableUploads_ = NO;
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
||||
selector:@selector(sendStoredCrashReports)
|
||||
object:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -317,38 +317,35 @@ NSString* GetPlatform() {
|
||||
[userDefaults synchronize];
|
||||
}
|
||||
|
||||
// This method must be called from the breakpad queue.
|
||||
- (void)sendStoredCrashReports {
|
||||
dispatch_async(queue_, ^{
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) == 0)
|
||||
return;
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) == 0)
|
||||
return;
|
||||
|
||||
int timeToWait = [self sendDelay];
|
||||
int timeToWait = [self sendDelay];
|
||||
|
||||
// Unable to ever send report.
|
||||
if (timeToWait == -1)
|
||||
return;
|
||||
// Unable to ever send report.
|
||||
if (timeToWait == -1)
|
||||
return;
|
||||
|
||||
// A report can be sent now.
|
||||
if (timeToWait == 0) {
|
||||
[self reportWillBeSent];
|
||||
BreakpadUploadNextReportWithParameters(breakpadRef_,
|
||||
uploadTimeParameters_);
|
||||
// A report can be sent now.
|
||||
if (timeToWait == 0) {
|
||||
[self reportWillBeSent];
|
||||
BreakpadUploadNextReportWithParameters(breakpadRef_,
|
||||
uploadTimeParameters_);
|
||||
|
||||
// If more reports must be sent, make sure this method is called again.
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) > 0)
|
||||
timeToWait = uploadIntervalInSeconds_;
|
||||
}
|
||||
// If more reports must be sent, make sure this method is called again.
|
||||
if (BreakpadGetCrashReportCount(breakpadRef_) > 0)
|
||||
timeToWait = uploadIntervalInSeconds_;
|
||||
}
|
||||
|
||||
// A report must be sent later.
|
||||
if (timeToWait > 0) {
|
||||
// performSelector: doesn't work on queue_
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self performSelector:@selector(sendStoredCrashReports)
|
||||
withObject:nil
|
||||
afterDelay:timeToWait];
|
||||
});
|
||||
}
|
||||
});
|
||||
// A report must be sent later.
|
||||
if (timeToWait > 0) {
|
||||
dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeToWait * NSEC_PER_SEC));
|
||||
dispatch_after(delay, queue_, ^{
|
||||
[self sendStoredCrashReports];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -255,7 +255,7 @@ CrashGenerationServer::ClientEvent(short revents)
|
||||
}
|
||||
|
||||
if (crashing_pid == -1 || signal_fd == -1) {
|
||||
if (signal_fd)
|
||||
if (signal_fd != -1)
|
||||
close(signal_fd);
|
||||
return true;
|
||||
}
|
||||
|
@ -41,8 +41,21 @@ namespace google_breakpad {
|
||||
// One of these is produced for each mapping in the process (i.e. line in
|
||||
// /proc/$x/maps).
|
||||
struct MappingInfo {
|
||||
// On Android, relocation packing can mean that the reported start
|
||||
// address of the mapping must be adjusted by a bias in order to
|
||||
// compensate for the compression of the relocation section. The
|
||||
// following two members hold (after LateInit) the adjusted mapping
|
||||
// range. See crbug.com/606972 for more information.
|
||||
uintptr_t start_addr;
|
||||
size_t size;
|
||||
// When Android relocation packing causes |start_addr| and |size| to
|
||||
// be modified with a load bias, we need to remember the unbiased
|
||||
// address range. The following structure holds the original mapping
|
||||
// address range as reported by the operating system.
|
||||
struct {
|
||||
uintptr_t start_addr;
|
||||
uintptr_t end_addr;
|
||||
} system_mapping_info;
|
||||
size_t offset; // offset into the backed file.
|
||||
bool exec; // true if the mapping has the execute bit set.
|
||||
char name[NAME_MAX];
|
||||
|
@ -36,19 +36,19 @@ namespace google_breakpad {
|
||||
|
||||
// Minidump defines register structures which are different from the raw
|
||||
// structures which we get from the kernel. These are platform specific
|
||||
// functions to juggle the ucontext and user structures into minidump format.
|
||||
// functions to juggle the ucontext_t and user structures into minidump format.
|
||||
|
||||
#if defined(__i386__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_ESP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_EIP];
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
|
||||
const struct _libc_fpstate* fp) {
|
||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||
|
||||
@ -88,15 +88,15 @@ void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
|
||||
#elif defined(__x86_64)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_RSP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.gregs[REG_RIP];
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
|
||||
const struct _libc_fpstate* fpregs) {
|
||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||
|
||||
@ -145,15 +145,15 @@ void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
|
||||
#elif defined(__ARM_EABI__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.arm_sp;
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.arm_pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc) {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
out->iregs[0] = uc->uc_mcontext.arm_r0;
|
||||
@ -184,15 +184,15 @@ void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.sp;
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
|
||||
const struct fpsimd_context* fpregs) {
|
||||
out->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||
|
||||
@ -210,15 +210,15 @@ void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
|
||||
#elif defined(__mips__)
|
||||
|
||||
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP];
|
||||
}
|
||||
|
||||
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||
uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
|
||||
return uc->uc_mcontext.pc;
|
||||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext_t *uc) {
|
||||
#if _MIPS_SIM == _ABI64
|
||||
out->context_flags = MD_CONTEXT_MIPS64_FULL;
|
||||
#elif _MIPS_SIM == _ABIO32
|
||||
|
@ -39,23 +39,23 @@
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Wraps platform-dependent implementations of accessors to ucontext structs.
|
||||
// Wraps platform-dependent implementations of accessors to ucontext_t structs.
|
||||
struct UContextReader {
|
||||
static uintptr_t GetStackPointer(const struct ucontext* uc);
|
||||
static uintptr_t GetStackPointer(const ucontext_t* uc);
|
||||
|
||||
static uintptr_t GetInstructionPointer(const struct ucontext* uc);
|
||||
static uintptr_t GetInstructionPointer(const ucontext_t* uc);
|
||||
|
||||
// Juggle a arch-specific ucontext into a minidump format
|
||||
// Juggle a arch-specific ucontext_t into a minidump format
|
||||
// out: the minidump structure
|
||||
// info: the collection of register structures.
|
||||
#if defined(__i386__) || defined(__x86_64)
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
|
||||
const struct _libc_fpstate* fp);
|
||||
#elif defined(__aarch64__)
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc,
|
||||
const struct fpsimd_context* fpregs);
|
||||
#else
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext *uc);
|
||||
static void FillCPUContext(RawContextCPU *out, const ucontext_t *uc);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -424,7 +424,7 @@ int ExceptionHandler::ThreadEntry(void *arg) {
|
||||
|
||||
// This function runs in a compromised context: see the top of the file.
|
||||
// Runs on the crashing thread.
|
||||
bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) {
|
||||
if (filter_ && !filter_(callback_context_))
|
||||
return false;
|
||||
|
||||
@ -439,9 +439,9 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
// Fill in all the holes in the struct to make Valgrind happy.
|
||||
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));
|
||||
memcpy(&g_crash_context_.context, uc, sizeof(ucontext_t));
|
||||
#if defined(__aarch64__)
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
ucontext_t* uc_ptr = (ucontext_t*)uc;
|
||||
struct fpsimd_context* fp_ptr =
|
||||
(struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
|
||||
if (fp_ptr->head.magic == FPSIMD_MAGIC) {
|
||||
@ -450,9 +450,9 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
||||
}
|
||||
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
// FP state is not part of user ABI on ARM Linux.
|
||||
// In case of MIPS Linux FP state is already part of struct ucontext
|
||||
// In case of MIPS Linux FP state is already part of ucontext_t
|
||||
// and 'float_state' is not a member of CrashContext.
|
||||
struct ucontext* uc_ptr = (struct ucontext*)uc;
|
||||
ucontext_t* uc_ptr = (ucontext_t*)uc;
|
||||
if (uc_ptr->uc_mcontext.fpregs) {
|
||||
memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs,
|
||||
sizeof(g_crash_context_.float_state));
|
||||
@ -476,7 +476,7 @@ bool ExceptionHandler::SimulateSignalDelivery(int sig) {
|
||||
// ExceptionHandler::HandleSignal().
|
||||
siginfo.si_code = SI_USER;
|
||||
siginfo.si_pid = getpid();
|
||||
struct ucontext context;
|
||||
ucontext_t context;
|
||||
getcontext(&context);
|
||||
return HandleSignal(sig, &siginfo, &context);
|
||||
}
|
||||
@ -586,12 +586,20 @@ void ExceptionHandler::WaitForContinueSignal() {
|
||||
// Runs on the cloned process.
|
||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
size_t context_size) {
|
||||
const bool may_skip_dump =
|
||||
minidump_descriptor_.skip_dump_if_principal_mapping_not_referenced();
|
||||
const uintptr_t principal_mapping_address =
|
||||
minidump_descriptor_.address_within_principal_mapping();
|
||||
const bool sanitize_stacks = minidump_descriptor_.sanitize_stacks();
|
||||
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||
return google_breakpad::WriteMicrodump(
|
||||
crashing_process,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
may_skip_dump,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks,
|
||||
*minidump_descriptor_.microdump_extra_info());
|
||||
}
|
||||
if (minidump_descriptor_.IsFD()) {
|
||||
@ -601,7 +609,10 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
app_memory_list_,
|
||||
may_skip_dump,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
|
||||
minidump_descriptor_.size_limit(),
|
||||
@ -609,7 +620,10 @@ bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||
context,
|
||||
context_size,
|
||||
mapping_list_,
|
||||
app_memory_list_);
|
||||
app_memory_list_,
|
||||
may_skip_dump,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -191,11 +191,11 @@ class ExceptionHandler {
|
||||
struct CrashContext {
|
||||
siginfo_t siginfo;
|
||||
pid_t tid; // the crashing thread.
|
||||
struct ucontext context;
|
||||
ucontext_t context;
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
||||
// In case of MIPS Linux FP state is already part of struct
|
||||
// ucontext so 'float_state' is not required.
|
||||
// In case of MIPS Linux FP state is already part of ucontext_t so
|
||||
// 'float_state' is not required.
|
||||
fpstate_t float_state;
|
||||
#endif
|
||||
};
|
||||
|
@ -38,9 +38,13 @@ struct MicrodumpExtraInfo {
|
||||
const char* build_fingerprint;
|
||||
const char* product_info;
|
||||
const char* gpu_fingerprint;
|
||||
const char* process_type;
|
||||
|
||||
MicrodumpExtraInfo()
|
||||
: build_fingerprint(NULL), product_info(NULL), gpu_fingerprint(NULL) {}
|
||||
: build_fingerprint(NULL),
|
||||
product_info(NULL),
|
||||
gpu_fingerprint(NULL),
|
||||
process_type(NULL) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -45,6 +45,11 @@ MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
||||
directory_(descriptor.directory_),
|
||||
c_path_(NULL),
|
||||
size_limit_(descriptor.size_limit_),
|
||||
address_within_principal_mapping_(
|
||||
descriptor.address_within_principal_mapping_),
|
||||
skip_dump_if_principal_mapping_not_referenced_(
|
||||
descriptor.skip_dump_if_principal_mapping_not_referenced_),
|
||||
sanitize_stacks_(descriptor.sanitize_stacks_),
|
||||
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
|
||||
@ -66,6 +71,11 @@ MinidumpDescriptor& MinidumpDescriptor::operator=(
|
||||
UpdatePath();
|
||||
}
|
||||
size_limit_ = descriptor.size_limit_;
|
||||
address_within_principal_mapping_ =
|
||||
descriptor.address_within_principal_mapping_;
|
||||
skip_dump_if_principal_mapping_not_referenced_ =
|
||||
descriptor.skip_dump_if_principal_mapping_not_referenced_;
|
||||
sanitize_stacks_ = descriptor.sanitize_stacks_;
|
||||
microdump_extra_info_ = descriptor.microdump_extra_info_;
|
||||
return *this;
|
||||
}
|
||||
|
@ -53,14 +53,19 @@ class MinidumpDescriptor {
|
||||
MinidumpDescriptor()
|
||||
: mode_(kUninitialized),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false) {}
|
||||
|
||||
explicit MinidumpDescriptor(const string& directory)
|
||||
: mode_(kWriteMinidumpToFile),
|
||||
fd_(-1),
|
||||
directory_(directory),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false),
|
||||
sanitize_stacks_(false) {
|
||||
assert(!directory.empty());
|
||||
}
|
||||
|
||||
@ -68,14 +73,20 @@ class MinidumpDescriptor {
|
||||
: mode_(kWriteMinidumpToFd),
|
||||
fd_(fd),
|
||||
c_path_(NULL),
|
||||
size_limit_(-1) {
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false),
|
||||
sanitize_stacks_(false) {
|
||||
assert(fd != -1);
|
||||
}
|
||||
|
||||
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
|
||||
: mode_(kWriteMicrodumpToConsole),
|
||||
fd_(-1),
|
||||
size_limit_(-1) {}
|
||||
size_limit_(-1),
|
||||
address_within_principal_mapping_(0),
|
||||
skip_dump_if_principal_mapping_not_referenced_(false),
|
||||
sanitize_stacks_(false) {}
|
||||
|
||||
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
|
||||
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
|
||||
@ -101,6 +112,28 @@ class MinidumpDescriptor {
|
||||
off_t size_limit() const { return size_limit_; }
|
||||
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
||||
|
||||
uintptr_t address_within_principal_mapping() const {
|
||||
return address_within_principal_mapping_;
|
||||
}
|
||||
void set_address_within_principal_mapping(
|
||||
uintptr_t address_within_principal_mapping) {
|
||||
address_within_principal_mapping_ = address_within_principal_mapping;
|
||||
}
|
||||
|
||||
bool skip_dump_if_principal_mapping_not_referenced() {
|
||||
return skip_dump_if_principal_mapping_not_referenced_;
|
||||
}
|
||||
void set_skip_dump_if_principal_mapping_not_referenced(
|
||||
bool skip_dump_if_principal_mapping_not_referenced) {
|
||||
skip_dump_if_principal_mapping_not_referenced_ =
|
||||
skip_dump_if_principal_mapping_not_referenced;
|
||||
}
|
||||
|
||||
bool sanitize_stacks() const { return sanitize_stacks_; }
|
||||
void set_sanitize_stacks(bool sanitize_stacks) {
|
||||
sanitize_stacks_ = sanitize_stacks;
|
||||
}
|
||||
|
||||
MicrodumpExtraInfo* microdump_extra_info() {
|
||||
assert(IsMicrodumpOnConsole());
|
||||
return µdump_extra_info_;
|
||||
@ -132,6 +165,23 @@ class MinidumpDescriptor {
|
||||
|
||||
off_t size_limit_;
|
||||
|
||||
// This member points somewhere into the main module for this
|
||||
// process (the module that is considerered interesting for the
|
||||
// purposes of debugging crashes).
|
||||
uintptr_t address_within_principal_mapping_;
|
||||
|
||||
// If set, threads that do not reference the address range
|
||||
// associated with |address_within_principal_mapping_| will not have their
|
||||
// stacks logged.
|
||||
bool skip_dump_if_principal_mapping_not_referenced_;
|
||||
|
||||
// If set, stacks are sanitized to remove PII. This involves
|
||||
// overwriting any pointer-aligned words that are not either
|
||||
// pointers into a process mapping or small integers (+/-4096). This
|
||||
// leaves enough information to unwind stacks, and preserve some
|
||||
// register values, but elides strings and other program data.
|
||||
bool sanitize_stacks_;
|
||||
|
||||
// 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
|
||||
|
@ -132,6 +132,9 @@ class MicrodumpWriter {
|
||||
public:
|
||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
bool skip_dump_if_principal_mapping_not_referenced,
|
||||
uintptr_t address_within_principal_mapping,
|
||||
bool sanitize_stack,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
LinuxDumper* dumper)
|
||||
: ucontext_(context ? &context->context : NULL),
|
||||
@ -140,8 +143,16 @@ class MicrodumpWriter {
|
||||
#endif
|
||||
dumper_(dumper),
|
||||
mapping_list_(mappings),
|
||||
skip_dump_if_principal_mapping_not_referenced_(
|
||||
skip_dump_if_principal_mapping_not_referenced),
|
||||
address_within_principal_mapping_(address_within_principal_mapping),
|
||||
sanitize_stack_(sanitize_stack),
|
||||
microdump_extra_info_(microdump_extra_info),
|
||||
log_line_(NULL) {
|
||||
log_line_(NULL),
|
||||
stack_copy_(NULL),
|
||||
stack_len_(0),
|
||||
stack_lower_bound_(0),
|
||||
stack_pointer_(0) {
|
||||
log_line_ = reinterpret_cast<char*>(Alloc(kLineBufferSize));
|
||||
if (log_line_)
|
||||
log_line_[0] = '\0'; // Clear out the log line buffer.
|
||||
@ -159,24 +170,31 @@ class MicrodumpWriter {
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
bool success;
|
||||
void Dump() {
|
||||
CaptureResult stack_capture_result = CaptureCrashingThreadStack(-1);
|
||||
if (stack_capture_result == CAPTURE_UNINTERESTING) {
|
||||
LogLine("Microdump skipped (uninteresting)");
|
||||
return;
|
||||
}
|
||||
|
||||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||
DumpProductInformation();
|
||||
DumpOSInformation();
|
||||
DumpProcessType();
|
||||
DumpGPUInformation();
|
||||
#if !defined(__LP64__)
|
||||
DumpFreeSpace();
|
||||
#endif
|
||||
success = DumpCrashingThread();
|
||||
if (success)
|
||||
success = DumpMappings();
|
||||
if (stack_capture_result == CAPTURE_OK)
|
||||
DumpThreadStack();
|
||||
DumpCPUState();
|
||||
DumpMappings();
|
||||
LogLine("-----END BREAKPAD MICRODUMP-----");
|
||||
dumper_->ThreadsResume();
|
||||
return success;
|
||||
}
|
||||
|
||||
private:
|
||||
enum CaptureResult { CAPTURE_OK, CAPTURE_FAILED, CAPTURE_UNINTERESTING };
|
||||
|
||||
// Writes one line to the system log.
|
||||
void LogLine(const char* msg) {
|
||||
#if defined(__ANDROID__)
|
||||
@ -220,7 +238,44 @@ class MicrodumpWriter {
|
||||
// Writes out the current line buffer on the system log.
|
||||
void LogCommitLine() {
|
||||
LogLine(log_line_);
|
||||
my_strlcpy(log_line_, "", kLineBufferSize);
|
||||
log_line_[0] = 0;
|
||||
}
|
||||
|
||||
CaptureResult CaptureCrashingThreadStack(int max_stack_len) {
|
||||
stack_pointer_ = UContextReader::GetStackPointer(ucontext_);
|
||||
|
||||
if (!dumper_->GetStackInfo(reinterpret_cast<const void**>(&stack_lower_bound_),
|
||||
&stack_len_, stack_pointer_)) {
|
||||
return CAPTURE_FAILED;
|
||||
}
|
||||
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len_ > static_cast<size_t>(max_stack_len)) {
|
||||
stack_len_ = max_stack_len;
|
||||
}
|
||||
|
||||
stack_copy_ = reinterpret_cast<uint8_t*>(Alloc(stack_len_));
|
||||
dumper_->CopyFromProcess(stack_copy_, dumper_->crash_thread(),
|
||||
reinterpret_cast<const void*>(stack_lower_bound_),
|
||||
stack_len_);
|
||||
|
||||
if (!skip_dump_if_principal_mapping_not_referenced_) return CAPTURE_OK;
|
||||
|
||||
const MappingInfo* principal_mapping =
|
||||
dumper_->FindMappingNoBias(address_within_principal_mapping_);
|
||||
if (!principal_mapping) return CAPTURE_UNINTERESTING;
|
||||
|
||||
uintptr_t low_addr = principal_mapping->system_mapping_info.start_addr;
|
||||
uintptr_t high_addr = principal_mapping->system_mapping_info.end_addr;
|
||||
uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
|
||||
if (low_addr <= pc && pc <= high_addr) return CAPTURE_OK;
|
||||
|
||||
if (dumper_->StackHasPointerToMapping(stack_copy_, stack_len_,
|
||||
stack_pointer_ - stack_lower_bound_,
|
||||
*principal_mapping)) {
|
||||
return CAPTURE_OK;
|
||||
}
|
||||
return CAPTURE_UNINTERESTING;
|
||||
}
|
||||
|
||||
void DumpProductInformation() {
|
||||
@ -233,6 +288,16 @@ class MicrodumpWriter {
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpProcessType() {
|
||||
LogAppend("P ");
|
||||
if (microdump_extra_info_.process_type) {
|
||||
LogAppend(microdump_extra_info_.process_type);
|
||||
} else {
|
||||
LogAppend("UNKNOWN");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpOSInformation() {
|
||||
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
|
||||
|
||||
@ -304,87 +369,42 @@ class MicrodumpWriter {
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
bool DumpThreadStack(uint32_t thread_id,
|
||||
uintptr_t stack_pointer,
|
||||
int max_stack_len,
|
||||
uint8_t** stack_copy) {
|
||||
*stack_copy = NULL;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||
// 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;
|
||||
void DumpThreadStack() {
|
||||
if (sanitize_stack_) {
|
||||
dumper_->SanitizeStackCopy(stack_copy_, stack_len_, stack_pointer_,
|
||||
stack_pointer_ - stack_lower_bound_);
|
||||
}
|
||||
|
||||
LogAppend("S 0 ");
|
||||
LogAppend(stack_pointer);
|
||||
LogAppend(stack_pointer_);
|
||||
LogAppend(" ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack));
|
||||
LogAppend(stack_lower_bound_);
|
||||
LogAppend(" ");
|
||||
LogAppend(stack_len);
|
||||
LogAppend(stack_len_);
|
||||
LogCommitLine();
|
||||
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||
stack_len = max_stack_len;
|
||||
}
|
||||
|
||||
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
|
||||
|
||||
// Dump the content of the stack, splicing it into chunks which size is
|
||||
// compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
|
||||
const size_t STACK_DUMP_CHUNK_SIZE = 384;
|
||||
for (size_t stack_off = 0; stack_off < stack_len;
|
||||
for (size_t stack_off = 0; stack_off < stack_len_;
|
||||
stack_off += STACK_DUMP_CHUNK_SIZE) {
|
||||
LogAppend("S ");
|
||||
LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
|
||||
LogAppend(stack_lower_bound_ + stack_off);
|
||||
LogAppend(" ");
|
||||
LogAppend(*stack_copy + stack_off,
|
||||
std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
|
||||
LogAppend(stack_copy_ + stack_off,
|
||||
std::min(STACK_DUMP_CHUNK_SIZE, stack_len_ - stack_off));
|
||||
LogCommitLine();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write information about the crashing thread.
|
||||
bool DumpCrashingThread() {
|
||||
const unsigned num_threads = dumper_->threads().size();
|
||||
|
||||
for (unsigned i = 0; i < num_threads; ++i) {
|
||||
MDRawThread thread;
|
||||
my_memset(&thread, 0, sizeof(thread));
|
||||
thread.thread_id = dumper_->threads()[i];
|
||||
|
||||
// Dump only the crashing thread.
|
||||
if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
|
||||
continue;
|
||||
|
||||
assert(ucontext_);
|
||||
assert(!dumper_->IsPostMortem());
|
||||
|
||||
uint8_t* stack_copy;
|
||||
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||
if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
|
||||
return false;
|
||||
|
||||
RawContextCPU cpu;
|
||||
my_memset(&cpu, 0, sizeof(RawContextCPU));
|
||||
void DumpCPUState() {
|
||||
RawContextCPU cpu;
|
||||
my_memset(&cpu, 0, sizeof(RawContextCPU));
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
|
||||
#else
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||
#endif
|
||||
DumpCPUState(&cpu);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpCPUState(RawContextCPU* cpu) {
|
||||
LogAppend("C ");
|
||||
LogAppend(cpu, sizeof(*cpu));
|
||||
LogAppend(&cpu, sizeof(cpu));
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
@ -536,7 +556,7 @@ class MicrodumpWriter {
|
||||
#endif
|
||||
|
||||
// Write information about the mappings in effect.
|
||||
bool DumpMappings() {
|
||||
void DumpMappings() {
|
||||
// First write all the mappings from the dumper
|
||||
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
|
||||
const MappingInfo& mapping = *dumper_->mappings()[i];
|
||||
@ -555,19 +575,35 @@ class MicrodumpWriter {
|
||||
++iter) {
|
||||
DumpModule(iter->first, false, 0, iter->second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
|
||||
|
||||
const struct ucontext* const ucontext_;
|
||||
const ucontext_t* const ucontext_;
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
const google_breakpad::fpstate_t* const float_state_;
|
||||
#endif
|
||||
LinuxDumper* dumper_;
|
||||
const MappingList& mapping_list_;
|
||||
bool skip_dump_if_principal_mapping_not_referenced_;
|
||||
uintptr_t address_within_principal_mapping_;
|
||||
bool sanitize_stack_;
|
||||
const MicrodumpExtraInfo microdump_extra_info_;
|
||||
char* log_line_;
|
||||
|
||||
// The local copy of crashed process stack memory, beginning at
|
||||
// |stack_lower_bound_|.
|
||||
uint8_t* stack_copy_;
|
||||
|
||||
// The length of crashed process stack copy.
|
||||
size_t stack_len_;
|
||||
|
||||
// The address of the page containing the stack pointer in the
|
||||
// crashed process. |stack_lower_bound_| <= |stack_pointer_|
|
||||
uintptr_t stack_lower_bound_;
|
||||
|
||||
// The stack pointer in the crashed process.
|
||||
uintptr_t stack_pointer_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@ -577,6 +613,9 @@ bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
bool skip_dump_if_principal_mapping_not_referenced,
|
||||
uintptr_t address_within_principal_mapping,
|
||||
bool sanitize_stack,
|
||||
const MicrodumpExtraInfo& microdump_extra_info) {
|
||||
LinuxPtraceDumper dumper(crashing_process);
|
||||
const ExceptionHandler::CrashContext* context = NULL;
|
||||
@ -589,10 +628,14 @@ bool WriteMicrodump(pid_t crashing_process,
|
||||
dumper.set_crash_signal(context->siginfo.si_signo);
|
||||
dumper.set_crash_thread(context->tid);
|
||||
}
|
||||
MicrodumpWriter writer(context, mappings, microdump_extra_info, &dumper);
|
||||
MicrodumpWriter writer(context, mappings,
|
||||
skip_dump_if_principal_mapping_not_referenced,
|
||||
address_within_principal_mapping, sanitize_stack,
|
||||
microdump_extra_info, &dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
writer.Dump();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -58,6 +58,9 @@ bool WriteMicrodump(pid_t crashing_process,
|
||||
const void* blob,
|
||||
size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
bool skip_dump_if_main_module_not_referenced,
|
||||
uintptr_t address_within_main_module,
|
||||
bool sanitize_stack,
|
||||
const MicrodumpExtraInfo& microdump_extra_info);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <ucontext.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
@ -47,6 +48,11 @@
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
extern "C" {
|
||||
extern char __executable_start;
|
||||
extern char __etext;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test MicrodumpWriterTest;
|
||||
@ -62,10 +68,19 @@ MicrodumpExtraInfo MakeMicrodumpExtraInfo(
|
||||
return info;
|
||||
}
|
||||
|
||||
void CrashAndGetMicrodump(
|
||||
const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
scoped_array<char>* buf) {
|
||||
bool ContainsMicrodump(const std::string& buf) {
|
||||
return std::string::npos != buf.find("-----BEGIN BREAKPAD MICRODUMP-----") &&
|
||||
std::string::npos != buf.find("-----END BREAKPAD MICRODUMP-----");
|
||||
}
|
||||
|
||||
const char kIdentifiableString[] = "_IDENTIFIABLE_";
|
||||
|
||||
void CrashAndGetMicrodump(const MappingList& mappings,
|
||||
const MicrodumpExtraInfo& microdump_extra_info,
|
||||
std::string* microdump,
|
||||
bool skip_dump_if_principal_mapping_not_referenced = false,
|
||||
uintptr_t address_within_principal_mapping = 0,
|
||||
bool sanitize_stack = false) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
@ -74,6 +89,14 @@ void CrashAndGetMicrodump(
|
||||
int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
|
||||
ASSERT_NE(-1, err_fd);
|
||||
|
||||
char identifiable_string[sizeof(kIdentifiableString)];
|
||||
|
||||
// This string should not appear in the resulting microdump if it
|
||||
// has been sanitized.
|
||||
strcpy(identifiable_string, kIdentifiableString);
|
||||
// Force the strcpy to not be optimized away.
|
||||
IGNORE_RET(write(STDOUT_FILENO, identifiable_string, 0));
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
@ -86,7 +109,10 @@ void CrashAndGetMicrodump(
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
|
||||
// Pretend the current context is the child context (which is
|
||||
// approximately right) so that we have a valid stack pointer, and
|
||||
// can fetch child stack data via ptrace.
|
||||
getcontext(&context.context);
|
||||
// Set a non-zero tid to avoid tripping asserts.
|
||||
context.tid = child;
|
||||
|
||||
@ -96,6 +122,8 @@ void CrashAndGetMicrodump(
|
||||
ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO));
|
||||
|
||||
ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings,
|
||||
skip_dump_if_principal_mapping_not_referenced,
|
||||
address_within_principal_mapping, sanitize_stack,
|
||||
microdump_extra_info));
|
||||
|
||||
// Revert stderr back to the console.
|
||||
@ -105,17 +133,38 @@ void CrashAndGetMicrodump(
|
||||
// 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);
|
||||
|
||||
microdump->clear();
|
||||
char buf[1024];
|
||||
|
||||
while (true) {
|
||||
int bytes_read = IGNORE_EINTR(read(err_fd, buf, 1024));
|
||||
if (bytes_read <= 0) break;
|
||||
microdump->append(buf, buf + bytes_read);
|
||||
}
|
||||
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 ExtractMicrodumpStackContents(const string& microdump_content,
|
||||
string* result) {
|
||||
std::istringstream iss(microdump_content);
|
||||
result->clear();
|
||||
for (string line; std::getline(iss, line);) {
|
||||
if (line.find("S ") == 0) {
|
||||
std::istringstream stack_data(line);
|
||||
std::string key;
|
||||
std::string addr;
|
||||
std::string data;
|
||||
stack_data >> key >> addr >> data;
|
||||
EXPECT_TRUE((data.size() & 1u) == 0u);
|
||||
result->reserve(result->size() + data.size() / 2);
|
||||
for (size_t i = 0; i < data.size(); i += 2) {
|
||||
std::string byte = data.substr(i, 2);
|
||||
result->push_back(static_cast<char>(strtoul(byte.c_str(), NULL, 16)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
@ -159,6 +208,13 @@ void CheckMicrodumpContents(const string& microdump_content,
|
||||
ASSERT_TRUE(did_find_gpu_info);
|
||||
}
|
||||
|
||||
bool MicrodumpStackContains(const string& microdump_content,
|
||||
const string& expected_content) {
|
||||
string result;
|
||||
ExtractMicrodumpStackContents(microdump_content, &result);
|
||||
return result.find(kIdentifiableString) != string::npos;
|
||||
}
|
||||
|
||||
void CheckMicrodumpContents(const string& microdump_content,
|
||||
const string& expected_fingerprint,
|
||||
const string& expected_product_info,
|
||||
@ -191,23 +247,101 @@ TEST(MicrodumpWriterTest, BasicWithMappings) {
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
|
||||
scoped_array<char> buf;
|
||||
std::string buf;
|
||||
CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
|
||||
#ifdef __LP64__
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 0000000000001000 000000000000002A 0000000000001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
ASSERT_NE(std::string::npos,
|
||||
buf.find("M 0000000000001000 000000000000002A 0000000000001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
#else
|
||||
ASSERT_NE(static_cast<char*>(0), strstr(
|
||||
buf.get(), "M 00001000 0000002A 00001000 "
|
||||
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
|
||||
ASSERT_NE(std::string::npos,
|
||||
buf.find("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"));
|
||||
ASSERT_NE(std::string::npos, buf.find("V UNKNOWN:0.0.0.0"));
|
||||
}
|
||||
|
||||
// Ensure that no output occurs if the interest region is set, but
|
||||
// doesn't overlap anything on the stack.
|
||||
TEST(MicrodumpWriterTest, NoOutputIfUninteresting) {
|
||||
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));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true, 0);
|
||||
ASSERT_FALSE(ContainsMicrodump(buf));
|
||||
}
|
||||
|
||||
// Ensure that stack content does not contain an identifiable string if the
|
||||
// stack is sanitized.
|
||||
TEST(MicrodumpWriterTest, StringRemovedBySanitization) {
|
||||
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));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, true);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
ASSERT_FALSE(MicrodumpStackContains(buf, kIdentifiableString));
|
||||
}
|
||||
|
||||
// Ensure that stack content does contain an identifiable string if the
|
||||
// stack is not sanitized.
|
||||
TEST(MicrodumpWriterTest, StringPresentIfNotSanitized) {
|
||||
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));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, false);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
ASSERT_TRUE(MicrodumpStackContains(buf, kIdentifiableString));
|
||||
}
|
||||
|
||||
// Ensure that output occurs if the interest region is set, and
|
||||
// does overlap something on the stack.
|
||||
TEST(MicrodumpWriterTest, OutputIfInteresting) {
|
||||
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));
|
||||
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true,
|
||||
reinterpret_cast<uintptr_t>(CrashAndGetMicrodump));
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
}
|
||||
|
||||
// Ensure that the product info and build fingerprint metadata show up in the
|
||||
@ -220,38 +354,40 @@ TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) {
|
||||
"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;
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kMicrodumpExtraInfo);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
CheckMicrodumpContents(buf, kMicrodumpExtraInfo);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoProductInfo) {
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
const char kGPUFingerprint[] = "bazqux";
|
||||
scoped_array<char> buf;
|
||||
std::string 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);
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
CheckMicrodumpContents(buf, kBuildFingerprint, "UNKNOWN:0.0.0.0",
|
||||
kGPUFingerprint);
|
||||
}
|
||||
|
||||
TEST(MicrodumpWriterTest, NoGPUInfo) {
|
||||
const char kProductInfo[] = "bazqux";
|
||||
const char kBuildFingerprint[] = "foobar";
|
||||
scoped_array<char> buf;
|
||||
std::string buf;
|
||||
MappingList no_mappings;
|
||||
|
||||
const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo(
|
||||
MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL));
|
||||
|
||||
CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf);
|
||||
CheckMicrodumpContents(string(buf.get()), kBuildFingerprint,
|
||||
kProductInfo, "UNKNOWN");
|
||||
ASSERT_TRUE(ContainsMicrodump(buf));
|
||||
CheckMicrodumpContents(buf, kBuildFingerprint, kProductInfo, "UNKNOWN");
|
||||
}
|
||||
} // namespace
|
||||
|
@ -41,7 +41,7 @@ using namespace google_breakpad;
|
||||
|
||||
TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) {
|
||||
const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root");
|
||||
const MappingInfo mapping = { 0, 0, 0, false, "/usr/lib/libc.so" };
|
||||
const MappingInfo mapping = {0, 0, {0, 0}, 0, false, "/usr/lib/libc.so"};
|
||||
|
||||
char path[PATH_MAX];
|
||||
dumper.GetMappingAbsolutePath(mapping, path);
|
||||
|
@ -84,6 +84,179 @@ inline static bool IsMappedFileOpenUnsafe(
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
namespace {
|
||||
|
||||
bool MappingContainsAddress(const MappingInfo& mapping, uintptr_t address) {
|
||||
return mapping.system_mapping_info.start_addr <= address &&
|
||||
address < mapping.system_mapping_info.end_addr;
|
||||
}
|
||||
|
||||
#if defined(__CHROMEOS__)
|
||||
|
||||
// Recover memory mappings before writing dump on ChromeOS
|
||||
//
|
||||
// On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from
|
||||
// addresses. ChromeOS' hugepage implementation replaces some segments with
|
||||
// anonymous private pages, which is a restriction of current implementation
|
||||
// in Linux kernel at the time of writing. Thus, breakpad can no longer
|
||||
// symbolize addresses from those text segments replaced with hugepages.
|
||||
//
|
||||
// This postprocess tries to recover the mappings. Because hugepages are always
|
||||
// inserted in between some .text sections, it tries to infer the names and
|
||||
// offsets of the segments, by looking at segments immediately precede and
|
||||
// succeed them.
|
||||
//
|
||||
// For example, a text segment before hugepage optimization
|
||||
// 02001000-03002000 r-xp /opt/google/chrome/chrome
|
||||
//
|
||||
// can be broken into
|
||||
// 02001000-02200000 r-xp /opt/google/chrome/chrome
|
||||
// 02200000-03000000 r-xp
|
||||
// 03000000-03002000 r-xp /opt/google/chrome/chrome
|
||||
//
|
||||
// For more details, see:
|
||||
// crbug.com/628040 ChromeOS' use of hugepages confuses crash symbolization
|
||||
|
||||
// Copied from CrOS' hugepage implementation, which is unlikely to change.
|
||||
// The hugepage size is 2M.
|
||||
const unsigned int kHpageShift = 21;
|
||||
const size_t kHpageSize = (1 << kHpageShift);
|
||||
const size_t kHpageMask = (~(kHpageSize - 1));
|
||||
|
||||
// Find and merge anonymous r-xp segments with surrounding named segments.
|
||||
// There are two cases:
|
||||
|
||||
// Case 1: curr, next
|
||||
// curr is anonymous
|
||||
// curr is r-xp
|
||||
// curr.size >= 2M
|
||||
// curr.size is a multiple of 2M.
|
||||
// next is backed by some file.
|
||||
// curr and next are contiguous.
|
||||
// offset(next) == sizeof(curr)
|
||||
void TryRecoverMappings(MappingInfo *curr, MappingInfo *next) {
|
||||
// Merged segments are marked with size = 0.
|
||||
if (curr->size == 0 || next->size == 0)
|
||||
return;
|
||||
|
||||
if (curr->size >= kHpageSize &&
|
||||
curr->exec &&
|
||||
(curr->size & kHpageMask) == curr->size &&
|
||||
(curr->start_addr & kHpageMask) == curr->start_addr &&
|
||||
curr->name[0] == '\0' &&
|
||||
next->name[0] != '\0' &&
|
||||
curr->start_addr + curr->size == next->start_addr &&
|
||||
curr->size == next->offset) {
|
||||
|
||||
// matched
|
||||
my_strlcpy(curr->name, next->name, NAME_MAX);
|
||||
if (next->exec) {
|
||||
// (curr, next)
|
||||
curr->size += next->size;
|
||||
next->size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 2: prev, curr, next
|
||||
// curr is anonymous
|
||||
// curr is r-xp
|
||||
// curr.size >= 2M
|
||||
// curr.size is a multiple of 2M.
|
||||
// next and prev are backed by the same file.
|
||||
// prev, curr and next are contiguous.
|
||||
// offset(next) == offset(prev) + sizeof(prev) + sizeof(curr)
|
||||
void TryRecoverMappings(MappingInfo *prev, MappingInfo *curr,
|
||||
MappingInfo *next) {
|
||||
// Merged segments are marked with size = 0.
|
||||
if (prev->size == 0 || curr->size == 0 || next->size == 0)
|
||||
return;
|
||||
|
||||
if (curr->size >= kHpageSize &&
|
||||
curr->exec &&
|
||||
(curr->size & kHpageMask) == curr->size &&
|
||||
(curr->start_addr & kHpageMask) == curr->start_addr &&
|
||||
curr->name[0] == '\0' &&
|
||||
next->name[0] != '\0' &&
|
||||
curr->start_addr + curr->size == next->start_addr &&
|
||||
prev->start_addr + prev->size == curr->start_addr &&
|
||||
my_strncmp(prev->name, next->name, NAME_MAX) == 0 &&
|
||||
next->offset == prev->offset + prev->size + curr->size) {
|
||||
|
||||
// matched
|
||||
my_strlcpy(curr->name, prev->name, NAME_MAX);
|
||||
if (prev->exec) {
|
||||
curr->offset = prev->offset;
|
||||
curr->start_addr = prev->start_addr;
|
||||
if (next->exec) {
|
||||
// (prev, curr, next)
|
||||
curr->size += prev->size + next->size;
|
||||
prev->size = 0;
|
||||
next->size = 0;
|
||||
} else {
|
||||
// (prev, curr), next
|
||||
curr->size += prev->size;
|
||||
prev->size = 0;
|
||||
}
|
||||
} else {
|
||||
curr->offset = prev->offset + prev->size;
|
||||
if (next->exec) {
|
||||
// prev, (curr, next)
|
||||
curr->size += next->size;
|
||||
next->size = 0;
|
||||
} else {
|
||||
// prev, curr, next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mappings_ is sorted excepted for the first entry.
|
||||
// This function tries to merge segemnts into the first entry,
|
||||
// then check for other sorted entries.
|
||||
// See LinuxDumper::EnumerateMappings().
|
||||
void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
|
||||
// Find the candidate "next" to first segment, which is the only one that
|
||||
// could be out-of-order.
|
||||
size_t l = 1;
|
||||
size_t r = mappings.size();
|
||||
size_t next = mappings.size();
|
||||
while (l < r) {
|
||||
int m = (l + r) / 2;
|
||||
if (mappings[m]->start_addr > mappings[0]->start_addr)
|
||||
r = next = m;
|
||||
else
|
||||
l = m + 1;
|
||||
}
|
||||
|
||||
// Try to merge segments into the first.
|
||||
if (next < mappings.size()) {
|
||||
TryRecoverMappings(mappings[0], mappings[next]);
|
||||
if (next - 1 > 0)
|
||||
TryRecoverMappings(mappings[next - 1], mappings[0], mappings[next]);
|
||||
}
|
||||
|
||||
// Iterate through normal, sorted cases.
|
||||
// Normal case 1.
|
||||
for (size_t i = 1; i < mappings.size() - 1; i++)
|
||||
TryRecoverMappings(mappings[i], mappings[i + 1]);
|
||||
|
||||
// Normal case 2.
|
||||
for (size_t i = 1; i < mappings.size() - 2; i++)
|
||||
TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
|
||||
|
||||
// Collect merged (size == 0) segments.
|
||||
size_t f, e;
|
||||
for (f = e = 0; e < mappings.size(); e++)
|
||||
if (mappings[e]->size > 0)
|
||||
mappings[f++] = mappings[e];
|
||||
mappings.resize(f);
|
||||
}
|
||||
|
||||
#endif // __CHROMEOS__
|
||||
|
||||
} // namespace
|
||||
|
||||
// All interesting auvx entry types are below AT_SYSINFO_EHDR
|
||||
#define AT_MAX AT_SYSINFO_EHDR
|
||||
|
||||
@ -113,6 +286,11 @@ bool LinuxDumper::LateInit() {
|
||||
#if defined(__ANDROID__)
|
||||
LatePostprocessMappings();
|
||||
#endif
|
||||
|
||||
#if defined(__CHROMEOS__)
|
||||
CrOSPostProcessMappings(mappings_);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -358,6 +536,8 @@ bool LinuxDumper::EnumerateMappings() {
|
||||
}
|
||||
MappingInfo* const module = new(allocator_) MappingInfo;
|
||||
my_memset(module, 0, sizeof(MappingInfo));
|
||||
module->system_mapping_info.start_addr = start_addr;
|
||||
module->system_mapping_info.end_addr = end_addr;
|
||||
module->start_addr = start_addr;
|
||||
module->size = end_addr - start_addr;
|
||||
module->offset = offset;
|
||||
@ -531,6 +711,126 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxDumper::SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
|
||||
uintptr_t stack_pointer,
|
||||
uintptr_t sp_offset) {
|
||||
// We optimize the search for containing mappings in three ways:
|
||||
// 1) We expect that pointers into the stack mapping will be common, so
|
||||
// we cache that address range.
|
||||
// 2) The last referenced mapping is a reasonable predictor for the next
|
||||
// referenced mapping, so we test that first.
|
||||
// 3) We precompute a bitfield based upon bits 32:32-n of the start and
|
||||
// stop addresses, and use that to short circuit any values that can
|
||||
// not be pointers. (n=11)
|
||||
const uintptr_t defaced =
|
||||
#if defined(__LP64__)
|
||||
0x0defaced0defaced;
|
||||
#else
|
||||
0x0defaced;
|
||||
#endif
|
||||
// the bitfield length is 2^test_bits long.
|
||||
const unsigned int test_bits = 11;
|
||||
// byte length of the corresponding array.
|
||||
const unsigned int array_size = 1 << (test_bits - 3);
|
||||
const unsigned int array_mask = array_size - 1;
|
||||
// The amount to right shift pointers by. This captures the top bits
|
||||
// on 32 bit architectures. On 64 bit architectures this would be
|
||||
// uninformative so we take the same range of bits.
|
||||
const unsigned int shift = 32 - 11;
|
||||
const MappingInfo* last_hit_mapping = nullptr;
|
||||
const MappingInfo* hit_mapping = nullptr;
|
||||
const MappingInfo* stack_mapping = FindMappingNoBias(stack_pointer);
|
||||
// The magnitude below which integers are considered to be to be
|
||||
// 'small', and not constitute a PII risk. These are included to
|
||||
// avoid eliding useful register values.
|
||||
const ssize_t small_int_magnitude = 4096;
|
||||
|
||||
char could_hit_mapping[array_size];
|
||||
my_memset(could_hit_mapping, 0, array_size);
|
||||
|
||||
// Initialize the bitfield such that if the (pointer >> shift)'th
|
||||
// bit, modulo the bitfield size, is not set then there does not
|
||||
// exist a mapping in mappings_ that would contain that pointer.
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
if (!mappings_[i]->exec) continue;
|
||||
// For each mapping, work out the (unmodulo'ed) range of bits to
|
||||
// set.
|
||||
uintptr_t start = mappings_[i]->start_addr;
|
||||
uintptr_t end = start + mappings_[i]->size;
|
||||
start >>= shift;
|
||||
end >>= shift;
|
||||
for (size_t bit = start; bit <= end; ++bit) {
|
||||
// Set each bit in the range, applying the modulus.
|
||||
could_hit_mapping[(bit >> 3) & array_mask] |= 1 << (bit & 7);
|
||||
}
|
||||
}
|
||||
|
||||
// Zero memory that is below the current stack pointer.
|
||||
const uintptr_t offset =
|
||||
(sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
|
||||
if (offset) {
|
||||
my_memset(stack_copy, 0, offset);
|
||||
}
|
||||
|
||||
// Apply sanitization to each complete pointer-aligned word in the
|
||||
// stack.
|
||||
uint8_t* sp;
|
||||
for (sp = stack_copy + offset;
|
||||
sp <= stack_copy + stack_len - sizeof(uintptr_t);
|
||||
sp += sizeof(uintptr_t)) {
|
||||
uintptr_t addr;
|
||||
my_memcpy(&addr, sp, sizeof(uintptr_t));
|
||||
if (static_cast<intptr_t>(addr) <= small_int_magnitude &&
|
||||
static_cast<intptr_t>(addr) >= -small_int_magnitude) {
|
||||
continue;
|
||||
}
|
||||
if (stack_mapping && MappingContainsAddress(*stack_mapping, addr)) {
|
||||
continue;
|
||||
}
|
||||
if (last_hit_mapping && MappingContainsAddress(*last_hit_mapping, addr)) {
|
||||
continue;
|
||||
}
|
||||
uintptr_t test = addr >> shift;
|
||||
if (could_hit_mapping[(test >> 3) & array_mask] & (1 << (test & 7)) &&
|
||||
(hit_mapping = FindMappingNoBias(addr)) != nullptr &&
|
||||
hit_mapping->exec) {
|
||||
last_hit_mapping = hit_mapping;
|
||||
continue;
|
||||
}
|
||||
my_memcpy(sp, &defaced, sizeof(uintptr_t));
|
||||
}
|
||||
// Zero any partial word at the top of the stack, if alignment is
|
||||
// such that that is required.
|
||||
if (sp < stack_copy + stack_len) {
|
||||
my_memset(sp, 0, stack_copy + stack_len - sp);
|
||||
}
|
||||
}
|
||||
|
||||
bool LinuxDumper::StackHasPointerToMapping(const uint8_t* stack_copy,
|
||||
size_t stack_len,
|
||||
uintptr_t sp_offset,
|
||||
const MappingInfo& mapping) {
|
||||
// Loop over all stack words that would have been on the stack in
|
||||
// the target process (i.e. are word aligned, and at addresses >=
|
||||
// the stack pointer). Regardless of the alignment of |stack_copy|,
|
||||
// the memory starting at |stack_copy| + |offset| represents an
|
||||
// aligned word in the target process.
|
||||
const uintptr_t low_addr = mapping.system_mapping_info.start_addr;
|
||||
const uintptr_t high_addr = mapping.system_mapping_info.end_addr;
|
||||
const uintptr_t offset =
|
||||
(sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1);
|
||||
|
||||
for (const uint8_t* sp = stack_copy + offset;
|
||||
sp <= stack_copy + stack_len - sizeof(uintptr_t);
|
||||
sp += sizeof(uintptr_t)) {
|
||||
uintptr_t addr;
|
||||
my_memcpy(&addr, sp, sizeof(uintptr_t));
|
||||
if (low_addr <= addr && addr <= high_addr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the mapping which the given memory address falls in.
|
||||
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||
const uintptr_t addr = (uintptr_t) address;
|
||||
@ -544,6 +844,19 @@ const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Find the mapping which the given memory address falls in. Uses the
|
||||
// unadjusted mapping address range from the kernel, rather than the
|
||||
// biased range.
|
||||
const MappingInfo* LinuxDumper::FindMappingNoBias(uintptr_t address) const {
|
||||
for (size_t i = 0; i < mappings_.size(); ++i) {
|
||||
if (address >= mappings_[i]->system_mapping_info.start_addr &&
|
||||
address < mappings_[i]->system_mapping_info.end_addr) {
|
||||
return mappings_[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
||||
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
|
||||
|
||||
|
@ -103,6 +103,11 @@ class LinuxDumper {
|
||||
const wasteful_vector<pid_t> &threads() { return threads_; }
|
||||
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
|
||||
const MappingInfo* FindMapping(const void* address) const;
|
||||
// Find the mapping which the given memory address falls in. Unlike
|
||||
// FindMapping, this method uses the unadjusted mapping address
|
||||
// ranges from the kernel, rather than the ranges that have had the
|
||||
// load bias applied.
|
||||
const MappingInfo* FindMappingNoBias(uintptr_t address) const;
|
||||
const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
|
||||
|
||||
// Find a block of memory to take as the stack given the top of stack pointer.
|
||||
@ -111,6 +116,32 @@ class LinuxDumper {
|
||||
// stack_top: the current top of the stack
|
||||
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
|
||||
|
||||
// Sanitize a copy of the stack by overwriting words that are not
|
||||
// pointers with a sentinel (0x0defaced).
|
||||
// stack_copy: a copy of the stack to sanitize. |stack_copy| might
|
||||
// not be word aligned, but it represents word aligned
|
||||
// data copied from another location.
|
||||
// stack_len: the length of the allocation pointed to by |stack_copy|.
|
||||
// stack_pointer: the address of the stack pointer (used to locate
|
||||
// the stack mapping, as an optimization).
|
||||
// sp_offset: the offset relative to stack_copy that reflects the
|
||||
// current value of the stack pointer.
|
||||
void SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len,
|
||||
uintptr_t stack_pointer, uintptr_t sp_offset);
|
||||
|
||||
// Test whether |stack_copy| contains a pointer-aligned word that
|
||||
// could be an address within a given mapping.
|
||||
// stack_copy: a copy of the stack to check. |stack_copy| might
|
||||
// not be word aligned, but it represents word aligned
|
||||
// data copied from another location.
|
||||
// stack_len: the length of the allocation pointed to by |stack_copy|.
|
||||
// sp_offset: the offset relative to stack_copy that reflects the
|
||||
// current value of the stack pointer.
|
||||
// mapping: the mapping against which to test stack words.
|
||||
bool StackHasPointerToMapping(const uint8_t* stack_copy, size_t stack_len,
|
||||
uintptr_t sp_offset,
|
||||
const MappingInfo& mapping);
|
||||
|
||||
PageAllocator* allocator() { return &allocator_; }
|
||||
|
||||
// Copy content of |length| bytes from a given process |child|,
|
||||
|
@ -57,14 +57,15 @@
|
||||
|
||||
void *thread_function(void *data) {
|
||||
int pipefd = *static_cast<int *>(data);
|
||||
volatile pid_t thread_id = syscall(__NR_gettid);
|
||||
volatile pid_t* thread_id = new pid_t;
|
||||
*thread_id = syscall(__NR_gettid);
|
||||
// Signal parent that a thread has started.
|
||||
uint8_t byte = 1;
|
||||
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
|
||||
perror("ERROR: parent notification failed");
|
||||
return NULL;
|
||||
}
|
||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
|
||||
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = thread_id;
|
||||
while (true)
|
||||
asm volatile ("" : : "r" (thread_id_ptr));
|
||||
return NULL;
|
||||
|
@ -66,6 +66,63 @@ using namespace google_breakpad;
|
||||
|
||||
namespace {
|
||||
|
||||
pid_t SetupChildProcess(int number_of_threads) {
|
||||
char kNumberOfThreadsArgument[2];
|
||||
sprintf(kNumberOfThreadsArgument, "%d", number_of_threads);
|
||||
|
||||
int fds[2];
|
||||
EXPECT_NE(-1, pipe(fds));
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// In child process.
|
||||
close(fds[0]);
|
||||
|
||||
string helper_path(GetHelperBinary());
|
||||
if (helper_path.empty()) {
|
||||
fprintf(stderr, "Couldn't find helper binary\n");
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
// Pass the pipe fd and the number of threads as arguments.
|
||||
char pipe_fd_string[8];
|
||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
||||
execl(helper_path.c_str(),
|
||||
"linux_dumper_unittest_helper",
|
||||
pipe_fd_string,
|
||||
kNumberOfThreadsArgument,
|
||||
NULL);
|
||||
// Kill if we get here.
|
||||
printf("Errno from exec: %d", errno);
|
||||
std::string err_str = "Exec of " + helper_path + " failed";
|
||||
perror(err_str.c_str());
|
||||
_exit(1);
|
||||
}
|
||||
close(fds[1]);
|
||||
|
||||
// Wait for all child threads to indicate that they have started
|
||||
for (int threads = 0; threads < number_of_threads; threads++) {
|
||||
struct pollfd pfd;
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = fds[0];
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
|
||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
||||
EXPECT_EQ(1, r);
|
||||
EXPECT_TRUE(pfd.revents & POLLIN);
|
||||
uint8_t junk;
|
||||
EXPECT_EQ(read(fds[0], &junk, sizeof(junk)),
|
||||
static_cast<ssize_t>(sizeof(junk)));
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
// There is a race here because we may stop a child thread before
|
||||
// it is actually running the busy loop. Empirically this sleep
|
||||
// is sufficient to avoid the race.
|
||||
usleep(100000);
|
||||
return child_pid;
|
||||
}
|
||||
|
||||
typedef wasteful_vector<uint8_t> id_vector;
|
||||
typedef testing::Test LinuxPtraceDumperTest;
|
||||
|
||||
@ -370,58 +427,9 @@ TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
|
||||
|
||||
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
static const int kNumberOfThreadsInHelperProgram = 5;
|
||||
char kNumberOfThreadsArgument[2];
|
||||
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
|
||||
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// In child process.
|
||||
close(fds[0]);
|
||||
|
||||
string helper_path(GetHelperBinary());
|
||||
if (helper_path.empty()) {
|
||||
FAIL() << "Couldn't find helper binary";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Pass the pipe fd and the number of threads as arguments.
|
||||
char pipe_fd_string[8];
|
||||
sprintf(pipe_fd_string, "%d", fds[1]);
|
||||
execl(helper_path.c_str(),
|
||||
"linux_dumper_unittest_helper",
|
||||
pipe_fd_string,
|
||||
kNumberOfThreadsArgument,
|
||||
NULL);
|
||||
// Kill if we get here.
|
||||
printf("Errno from exec: %d", errno);
|
||||
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
|
||||
exit(0);
|
||||
}
|
||||
close(fds[1]);
|
||||
|
||||
// Wait for all child threads to indicate that they have started
|
||||
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
|
||||
struct pollfd pfd;
|
||||
memset(&pfd, 0, sizeof(pfd));
|
||||
pfd.fd = fds[0];
|
||||
pfd.events = POLLIN | POLLERR;
|
||||
|
||||
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
|
||||
ASSERT_EQ(1, r);
|
||||
ASSERT_TRUE(pfd.revents & POLLIN);
|
||||
uint8_t junk;
|
||||
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
|
||||
static_cast<ssize_t>(sizeof(junk)));
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
// There is a race here because we may stop a child thread before
|
||||
// it is actually running the busy loop. Empirically this sleep
|
||||
// is sufficient to avoid the race.
|
||||
usleep(100000);
|
||||
pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
|
||||
ASSERT_NE(child_pid, -1);
|
||||
|
||||
// Children are ready now.
|
||||
LinuxPtraceDumper dumper(child_pid);
|
||||
@ -468,3 +476,99 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
ASSERT_TRUE(WIFSIGNALED(status));
|
||||
ASSERT_EQ(SIGKILL, WTERMSIG(status));
|
||||
}
|
||||
|
||||
TEST_F(LinuxPtraceDumperTest, SanitizeStackCopy) {
|
||||
static const int kNumberOfThreadsInHelperProgram = 1;
|
||||
|
||||
pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram);
|
||||
ASSERT_NE(child_pid, -1);
|
||||
|
||||
LinuxPtraceDumper dumper(child_pid);
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
EXPECT_TRUE(dumper.ThreadsSuspend());
|
||||
|
||||
ThreadInfo thread_info;
|
||||
EXPECT_TRUE(dumper.GetThreadInfoByIndex(0, &thread_info));
|
||||
|
||||
const uintptr_t defaced =
|
||||
#if defined(__LP64__)
|
||||
0x0defaced0defaced;
|
||||
#else
|
||||
0x0defaced;
|
||||
#endif
|
||||
|
||||
uintptr_t simulated_stack[2];
|
||||
|
||||
// Pointers into the stack shouldn't be sanitized.
|
||||
memset(simulated_stack, 0xff, sizeof(simulated_stack));
|
||||
simulated_stack[1] = thread_info.stack_pointer;
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
sizeof(uintptr_t));
|
||||
ASSERT_NE(simulated_stack[1], defaced);
|
||||
|
||||
// Memory prior to the stack pointer should be cleared.
|
||||
ASSERT_EQ(simulated_stack[0], 0u);
|
||||
|
||||
// Small integers should not be sanitized.
|
||||
for (int i = -4096; i <= 4096; ++i) {
|
||||
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||
simulated_stack[0] = static_cast<uintptr_t>(i);
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_NE(simulated_stack[0], defaced);
|
||||
}
|
||||
|
||||
// The instruction pointer definitely should point into an executable mapping.
|
||||
const MappingInfo* mapping_info = dumper.FindMappingNoBias(
|
||||
reinterpret_cast<uintptr_t>(thread_info.GetInstructionPointer()));
|
||||
ASSERT_NE(mapping_info, nullptr);
|
||||
ASSERT_TRUE(mapping_info->exec);
|
||||
|
||||
// Pointers to code shouldn't be sanitized.
|
||||
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||
simulated_stack[1] = thread_info.GetInstructionPointer();
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_NE(simulated_stack[0], defaced);
|
||||
|
||||
// String fragments should be sanitized.
|
||||
memcpy(simulated_stack, "abcdefghijklmnop", sizeof(simulated_stack));
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_EQ(simulated_stack[0], defaced);
|
||||
ASSERT_EQ(simulated_stack[1], defaced);
|
||||
|
||||
// Heap pointers should be sanititzed.
|
||||
#if defined(__ARM_EABI__)
|
||||
uintptr_t heap_addr = thread_info.regs.uregs[3];
|
||||
#elif defined(__aarch64__)
|
||||
uintptr_t heap_addr = thread_info.regs.regs[3];
|
||||
#elif defined(__i386)
|
||||
uintptr_t heap_addr = thread_info.regs.ecx;
|
||||
#elif defined(__x86_64)
|
||||
uintptr_t heap_addr = thread_info.regs.rcx;
|
||||
#elif defined(__mips__)
|
||||
uintptr_t heap_addr = thread_info.mcontext.gregs[1];
|
||||
#else
|
||||
#error This test has not been ported to this platform.
|
||||
#endif
|
||||
memset(simulated_stack, 0, sizeof(simulated_stack));
|
||||
simulated_stack[0] = heap_addr;
|
||||
dumper.SanitizeStackCopy(reinterpret_cast<uint8_t*>(&simulated_stack),
|
||||
sizeof(simulated_stack), thread_info.stack_pointer,
|
||||
0u);
|
||||
ASSERT_EQ(simulated_stack[0], defaced);
|
||||
|
||||
EXPECT_TRUE(dumper.ThreadsResume());
|
||||
kill(child_pid, SIGKILL);
|
||||
|
||||
// Reap child.
|
||||
int status;
|
||||
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
|
||||
ASSERT_TRUE(WIFSIGNALED(status));
|
||||
ASSERT_EQ(SIGKILL, WTERMSIG(status));
|
||||
}
|
||||
|
@ -129,6 +129,9 @@ class MinidumpWriter {
|
||||
const ExceptionHandler::CrashContext* context,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks,
|
||||
LinuxDumper* dumper)
|
||||
: fd_(minidump_fd),
|
||||
path_(minidump_path),
|
||||
@ -140,7 +143,12 @@ class MinidumpWriter {
|
||||
minidump_size_limit_(-1),
|
||||
memory_blocks_(dumper_->allocator()),
|
||||
mapping_list_(mappings),
|
||||
app_memory_list_(appmem) {
|
||||
app_memory_list_(appmem),
|
||||
skip_stacks_if_mapping_unreferenced_(
|
||||
skip_stacks_if_mapping_unreferenced),
|
||||
principal_mapping_address_(principal_mapping_address),
|
||||
principal_mapping_(nullptr),
|
||||
sanitize_stacks_(sanitize_stacks) {
|
||||
// Assert there should be either a valid fd or a valid path, not both.
|
||||
assert(fd_ != -1 || minidump_path);
|
||||
assert(fd_ == -1 || !minidump_path);
|
||||
@ -150,12 +158,22 @@ class MinidumpWriter {
|
||||
if (!dumper_->Init())
|
||||
return false;
|
||||
|
||||
if (!dumper_->ThreadsSuspend() || !dumper_->LateInit())
|
||||
return false;
|
||||
|
||||
if (skip_stacks_if_mapping_unreferenced_) {
|
||||
principal_mapping_ =
|
||||
dumper_->FindMappingNoBias(principal_mapping_address_);
|
||||
if (!CrashingThreadReferencesPrincipalMapping())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fd_ != -1)
|
||||
minidump_writer_.SetFile(fd_);
|
||||
else if (!minidump_writer_.Open(path_))
|
||||
return false;
|
||||
|
||||
return dumper_->ThreadsSuspend() && dumper_->LateInit();
|
||||
return true;
|
||||
}
|
||||
|
||||
~MinidumpWriter() {
|
||||
@ -166,6 +184,38 @@ class MinidumpWriter {
|
||||
dumper_->ThreadsResume();
|
||||
}
|
||||
|
||||
bool CrashingThreadReferencesPrincipalMapping() {
|
||||
if (!ucontext_ || !principal_mapping_)
|
||||
return false;
|
||||
|
||||
const uintptr_t low_addr =
|
||||
principal_mapping_->system_mapping_info.start_addr;
|
||||
const uintptr_t high_addr =
|
||||
principal_mapping_->system_mapping_info.end_addr;
|
||||
|
||||
const uintptr_t stack_pointer = UContextReader::GetStackPointer(ucontext_);
|
||||
const uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_);
|
||||
|
||||
if (pc >= low_addr && pc < high_addr)
|
||||
return true;
|
||||
|
||||
uint8_t* stack_copy;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer))
|
||||
return false;
|
||||
|
||||
stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(stack_copy, GetCrashThread(), stack, stack_len);
|
||||
|
||||
uintptr_t stack_pointer_offset =
|
||||
stack_pointer - reinterpret_cast<uintptr_t>(stack);
|
||||
|
||||
return dumper_->StackHasPointerToMapping(
|
||||
stack_copy, stack_len, stack_pointer_offset, *principal_mapping_);
|
||||
}
|
||||
|
||||
bool Dump() {
|
||||
// A minidump file contains a number of tagged streams. This is the number
|
||||
// of stream which we write.
|
||||
@ -266,12 +316,16 @@ class MinidumpWriter {
|
||||
}
|
||||
|
||||
bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer,
|
||||
int max_stack_len, uint8_t** stack_copy) {
|
||||
uintptr_t pc, int max_stack_len, uint8_t** stack_copy) {
|
||||
*stack_copy = NULL;
|
||||
const void* stack;
|
||||
size_t stack_len;
|
||||
|
||||
thread->stack.start_of_memory_range = stack_pointer;
|
||||
thread->stack.memory.data_size = 0;
|
||||
thread->stack.memory.rva = minidump_writer_.position();
|
||||
|
||||
if (dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||
UntypedMDRVA memory(&minidump_writer_);
|
||||
if (max_stack_len >= 0 &&
|
||||
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||
stack_len = max_stack_len;
|
||||
@ -284,20 +338,38 @@ class MinidumpWriter {
|
||||
}
|
||||
stack = reinterpret_cast<const void*>(int_stack);
|
||||
}
|
||||
if (!memory.Allocate(stack_len))
|
||||
return false;
|
||||
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||
dumper_->CopyFromProcess(*stack_copy, thread->thread_id, stack,
|
||||
stack_len);
|
||||
|
||||
uintptr_t stack_pointer_offset =
|
||||
stack_pointer - reinterpret_cast<uintptr_t>(stack);
|
||||
if (skip_stacks_if_mapping_unreferenced_) {
|
||||
if (!principal_mapping_) {
|
||||
return true;
|
||||
}
|
||||
uintptr_t low_addr = principal_mapping_->system_mapping_info.start_addr;
|
||||
uintptr_t high_addr = principal_mapping_->system_mapping_info.end_addr;
|
||||
if ((pc < low_addr || pc > high_addr) &&
|
||||
!dumper_->StackHasPointerToMapping(*stack_copy, stack_len,
|
||||
stack_pointer_offset,
|
||||
*principal_mapping_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (sanitize_stacks_) {
|
||||
dumper_->SanitizeStackCopy(*stack_copy, stack_len, stack_pointer,
|
||||
stack_pointer_offset);
|
||||
}
|
||||
|
||||
UntypedMDRVA memory(&minidump_writer_);
|
||||
if (!memory.Allocate(stack_len))
|
||||
return false;
|
||||
memory.Copy(*stack_copy, stack_len);
|
||||
thread->stack.start_of_memory_range =
|
||||
reinterpret_cast<uintptr_t>(stack);
|
||||
thread->stack.start_of_memory_range = reinterpret_cast<uintptr_t>(stack);
|
||||
thread->stack.memory = memory.location();
|
||||
memory_blocks_.push_back(thread->stack);
|
||||
} else {
|
||||
thread->stack.start_of_memory_range = stack_pointer;
|
||||
thread->stack.memory.data_size = 0;
|
||||
thread->stack.memory.rva = minidump_writer_.position();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -344,7 +416,9 @@ class MinidumpWriter {
|
||||
!dumper_->IsPostMortem()) {
|
||||
uint8_t* stack_copy;
|
||||
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||
if (!FillThreadStack(&thread, stack_ptr, -1, &stack_copy))
|
||||
if (!FillThreadStack(&thread, stack_ptr,
|
||||
UContextReader::GetInstructionPointer(ucontext_),
|
||||
-1, &stack_copy))
|
||||
return false;
|
||||
|
||||
// Copy 256 bytes around crashing instruction pointer to minidump.
|
||||
@ -410,8 +484,9 @@ class MinidumpWriter {
|
||||
int max_stack_len = -1; // default to no maximum for this thread
|
||||
if (minidump_size_limit_ >= 0 && i >= kLimitBaseThreadCount)
|
||||
max_stack_len = extra_thread_stack_len;
|
||||
if (!FillThreadStack(&thread, info.stack_pointer, max_stack_len,
|
||||
&stack_copy))
|
||||
if (!FillThreadStack(&thread, info.stack_pointer,
|
||||
info.GetInstructionPointer(), max_stack_len,
|
||||
&stack_copy))
|
||||
return false;
|
||||
|
||||
TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
|
||||
@ -1248,7 +1323,7 @@ class MinidumpWriter {
|
||||
const int fd_; // File descriptor where the minidum should be written.
|
||||
const char* path_; // Path to the file where the minidum should be written.
|
||||
|
||||
const struct ucontext* const ucontext_; // also from the signal handler
|
||||
const ucontext_t* const ucontext_; // also from the signal handler
|
||||
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||
const google_breakpad::fpstate_t* const float_state_; // ditto
|
||||
#endif
|
||||
@ -1265,6 +1340,13 @@ class MinidumpWriter {
|
||||
// Additional memory regions to be included in the dump,
|
||||
// provided by the caller.
|
||||
const AppMemoryList& app_memory_list_;
|
||||
// If set, skip recording any threads that do not reference the
|
||||
// mapping containing principal_mapping_address_.
|
||||
bool skip_stacks_if_mapping_unreferenced_;
|
||||
uintptr_t principal_mapping_address_;
|
||||
const MappingInfo* principal_mapping_;
|
||||
// If true, apply stack sanitization to stored stack data.
|
||||
bool sanitize_stacks_;
|
||||
};
|
||||
|
||||
|
||||
@ -1274,7 +1356,10 @@ bool WriteMinidumpImpl(const char* minidump_path,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
LinuxPtraceDumper dumper(crashing_process);
|
||||
const ExceptionHandler::CrashContext* context = NULL;
|
||||
if (blob) {
|
||||
@ -1287,7 +1372,8 @@ bool WriteMinidumpImpl(const char* minidump_path,
|
||||
dumper.set_crash_thread(context->tid);
|
||||
}
|
||||
MinidumpWriter writer(minidump_path, minidump_fd, context, mappings,
|
||||
appmem, &dumper);
|
||||
appmem, skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address, sanitize_stacks, &dumper);
|
||||
// Set desired limit for file size of minidump (-1 means no limit).
|
||||
writer.set_minidump_size_limit(minidump_size_limit);
|
||||
if (!writer.Init())
|
||||
@ -1300,17 +1386,29 @@ bool WriteMinidumpImpl(const char* minidump_path,
|
||||
namespace google_breakpad {
|
||||
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size) {
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(minidump_path, -1, -1,
|
||||
crashing_process, blob, blob_size,
|
||||
MappingList(), AppMemoryList());
|
||||
MappingList(), AppMemoryList(),
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size) {
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(NULL, minidump_fd, -1,
|
||||
crashing_process, blob, blob_size,
|
||||
MappingList(), AppMemoryList());
|
||||
MappingList(), AppMemoryList(),
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
@ -1320,7 +1418,7 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
dumper.set_crash_signal(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED);
|
||||
dumper.set_crash_thread(process_blamed_thread);
|
||||
MinidumpWriter writer(minidump_path, -1, NULL, MappingList(),
|
||||
AppMemoryList(), &dumper);
|
||||
AppMemoryList(), false, 0, false, &dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
@ -1329,46 +1427,71 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(minidump_path, -1, -1, crashing_process,
|
||||
blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(NULL, minidump_fd, -1, crashing_process,
|
||||
blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(minidump_path, -1, minidump_size_limit,
|
||||
crashing_process, blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem) {
|
||||
const AppMemoryList& appmem,
|
||||
bool skip_stacks_if_mapping_unreferenced,
|
||||
uintptr_t principal_mapping_address,
|
||||
bool sanitize_stacks) {
|
||||
return WriteMinidumpImpl(NULL, minidump_fd, minidump_size_limit,
|
||||
crashing_process, blob, blob_size,
|
||||
mappings, appmem);
|
||||
mappings, appmem,
|
||||
skip_stacks_if_mapping_unreferenced,
|
||||
principal_mapping_address,
|
||||
sanitize_stacks);
|
||||
}
|
||||
|
||||
bool WriteMinidump(const char* filename,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appmem,
|
||||
LinuxDumper* dumper) {
|
||||
MinidumpWriter writer(filename, -1, NULL, mappings, appmem, dumper);
|
||||
MinidumpWriter writer(filename, -1, NULL, mappings, appmem,
|
||||
false, 0, false, dumper);
|
||||
if (!writer.Init())
|
||||
return false;
|
||||
return writer.Dump();
|
||||
|
@ -78,10 +78,16 @@ typedef std::list<AppMemory> AppMemoryList;
|
||||
//
|
||||
// Returns true iff successful.
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
// Same as above but takes an open file descriptor instead of a path.
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size);
|
||||
const void* blob, size_t blob_size,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
|
||||
// Alternate form of WriteMinidump() that works with processes that
|
||||
// are not expected to have crashed. If |process_blamed_thread| is
|
||||
@ -96,23 +102,35 @@ bool WriteMinidump(const char* minidump_path, pid_t process,
|
||||
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
|
||||
// These overloads also allow passing a file size limit for the minidump.
|
||||
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
|
||||
pid_t crashing_process,
|
||||
const void* blob, size_t blob_size,
|
||||
const MappingList& mappings,
|
||||
const AppMemoryList& appdata);
|
||||
const AppMemoryList& appdata,
|
||||
bool skip_stacks_if_mapping_unreferenced = false,
|
||||
uintptr_t principal_mapping_address = 0,
|
||||
bool sanitize_stacks = false);
|
||||
|
||||
bool WriteMinidump(const char* filename,
|
||||
const MappingList& mappings,
|
||||
|
@ -169,6 +169,7 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
||||
info.start_addr = kMemoryAddress;
|
||||
info.size = memory_size;
|
||||
info.offset = 0;
|
||||
info.exec = false;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
@ -178,7 +179,7 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
||||
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
|
||||
mappings.push_back(mapping);
|
||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
||||
mappings, memory_list));
|
||||
mappings, memory_list, false, 0, false));
|
||||
|
||||
// Read the minidump. Load the module list, and ensure that
|
||||
// the mmap'ed |memory| is listed with the given module name
|
||||
@ -214,6 +215,146 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
// Test that minidumping is skipped while writing minidumps if principal mapping
|
||||
// is not referenced.
|
||||
TEST(MinidumpWriterTest, MinidumpSkippedIfRequested) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
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));
|
||||
ASSERT_EQ(0, getcontext(&context.context));
|
||||
context.tid = child;
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||
|
||||
// pass an invalid principal mapping address, which will force
|
||||
// WriteMinidump to not write a minidump.
|
||||
ASSERT_FALSE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
||||
true, static_cast<uintptr_t>(0x0102030405060708ull),
|
||||
false));
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
// Test that minidumping is skipped while writing minidumps if principal mapping
|
||||
// is not referenced.
|
||||
TEST(MinidumpWriterTest, MinidumpStacksSkippedIfRequested) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
|
||||
// Create a thread that does not return, and only references libc (not the
|
||||
// current executable). This thread should not be captured in the minidump.
|
||||
pthread_t thread;
|
||||
pthread_attr_t thread_attributes;
|
||||
pthread_attr_init(&thread_attributes);
|
||||
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
|
||||
sigset_t sigset;
|
||||
sigemptyset(&sigset);
|
||||
pthread_create(&thread, &thread_attributes,
|
||||
reinterpret_cast<void* (*)(void*)>(&sigsuspend), &sigset);
|
||||
|
||||
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));
|
||||
ASSERT_EQ(0, getcontext(&context.context));
|
||||
context.tid = child;
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||
|
||||
// Pass an invalid principal mapping address, which will force
|
||||
// WriteMinidump to not dump any thread stacks.
|
||||
ASSERT_TRUE(WriteMinidump(
|
||||
templ.c_str(), child, &context, sizeof(context), true,
|
||||
reinterpret_cast<uintptr_t>(google_breakpad::WriteFile), false));
|
||||
|
||||
// Read the minidump. And ensure that thread memory was dumped only for the
|
||||
// main thread.
|
||||
Minidump minidump(templ);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
|
||||
MinidumpThreadList *threads = minidump.GetThreadList();
|
||||
int threads_with_stacks = 0;
|
||||
for (unsigned int i = 0; i < threads->thread_count(); ++i) {
|
||||
MinidumpThread *thread = threads->GetThreadAtIndex(i);
|
||||
if (thread->GetMemory()) {
|
||||
++threads_with_stacks;
|
||||
}
|
||||
}
|
||||
ASSERT_EQ(1, threads_with_stacks);
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
// Test that stacks can be sanitized while writing minidumps.
|
||||
TEST(MinidumpWriterTest, StacksAreSanitizedIfRequested) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
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));
|
||||
ASSERT_EQ(0, getcontext(&context.context));
|
||||
context.tid = child;
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||
// pass an invalid principal mapping address, which will force
|
||||
// WriteMinidump to not dump any thread stacks.
|
||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
|
||||
false, 0, true));
|
||||
|
||||
// Read the minidump. And ensure that thread memory contains a defaced value.
|
||||
Minidump minidump(templ);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
|
||||
const uintptr_t defaced =
|
||||
#if defined(__LP64__)
|
||||
0x0defaced0defaced;
|
||||
#else
|
||||
0x0defaced;
|
||||
#endif
|
||||
MinidumpThreadList *threads = minidump.GetThreadList();
|
||||
for (unsigned int i = 0; i < threads->thread_count(); ++i) {
|
||||
MinidumpThread *thread = threads->GetThreadAtIndex(i);
|
||||
MinidumpMemoryRegion *mem = thread->GetMemory();
|
||||
ASSERT_TRUE(mem != nullptr);
|
||||
uint32_t sz = mem->GetSize();
|
||||
const uint8_t *data = mem->GetMemory();
|
||||
ASSERT_TRUE(memmem(data, sz, &defaced, sizeof(defaced)) != nullptr);
|
||||
}
|
||||
close(fds[1]);
|
||||
}
|
||||
|
||||
// Test that a binary with a longer-than-usual build id note
|
||||
// makes its way all the way through to the minidump unscathed.
|
||||
// The linux_client_unittest is linked with an explicit --build-id
|
||||
@ -323,6 +464,7 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
|
||||
info.start_addr = kMemoryAddress - memory_size;
|
||||
info.size = memory_size * 3;
|
||||
info.offset = 0;
|
||||
info.exec = false;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
|
@ -126,7 +126,7 @@ extern "C" {
|
||||
mach_msg_header_t* reply);
|
||||
|
||||
// This symbol must be visible to dlsym() - see
|
||||
// http://code.google.com/p/google-breakpad/issues/detail?id=345 for details.
|
||||
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=345 for details.
|
||||
kern_return_t catch_exception_raise(mach_port_t target_port,
|
||||
mach_port_t failed_thread,
|
||||
mach_port_t task,
|
||||
@ -356,6 +356,11 @@ bool ExceptionHandler::WriteMinidumpWithException(
|
||||
bool report_current_thread) {
|
||||
bool result = false;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
// _exit() should never be called on iOS.
|
||||
exit_after_write = false;
|
||||
#endif
|
||||
|
||||
if (directCallback_) {
|
||||
if (directCallback_(callback_context_,
|
||||
exception_type,
|
||||
@ -459,7 +464,7 @@ kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread,
|
||||
|
||||
kern_return_t result;
|
||||
// TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES
|
||||
// set. https://code.google.com/p/google-breakpad/issues/detail?id=551
|
||||
// set. https://bugs.chromium.org/p/google-breakpad/issues/detail?id=551
|
||||
switch (target_behavior) {
|
||||
case EXCEPTION_DEFAULT:
|
||||
result = exception_raise(target_port, failed_thread, task, exception,
|
||||
|
@ -68,7 +68,7 @@
|
||||
F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; };
|
||||
F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; };
|
||||
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; };
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.mm */; };
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.cc */; };
|
||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; };
|
||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; };
|
||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; };
|
||||
@ -151,7 +151,7 @@
|
||||
F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = "<group>"; };
|
||||
F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = "<group>"; };
|
||||
F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; };
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; };
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.cc; path = ../../../common/mac/dump_syms.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; };
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; };
|
||||
@ -240,7 +240,7 @@
|
||||
F9721F760E8B0DC700D7E813 /* bytereader.cc */,
|
||||
F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */,
|
||||
F9721F780E8B0DC700D7E813 /* functioninfo.cc */,
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.mm */,
|
||||
F9721F390E8B0D0D00D7E813 /* dump_syms.cc */,
|
||||
F9721F380E8B0CFC00D7E813 /* dump_syms.h */,
|
||||
F917C4F70E03265A00F86017 /* breakpad_exc_server.c */,
|
||||
F917C4F80E03265A00F86017 /* breakpad_exc_server.h */,
|
||||
@ -597,7 +597,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */,
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.mm in Sources */,
|
||||
F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */,
|
||||
F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */,
|
||||
F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */,
|
||||
F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */,
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/handler/exception_handler.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
@ -93,7 +94,7 @@ static bool MDCallback(const char *dump_dir, const char *file_name,
|
||||
path.append(".dmp");
|
||||
|
||||
int fd = *reinterpret_cast<int*>(context);
|
||||
(void)write(fd, path.c_str(), path.length() + 1);
|
||||
IGNORE_RET(write(fd, path.c_str(), path.length() + 1));
|
||||
close(fd);
|
||||
exit(0);
|
||||
// not reached
|
||||
@ -293,7 +294,7 @@ TEST_F(ExceptionHandlerTest, DumpChildProcess) {
|
||||
|
||||
// Unblock child process
|
||||
uint8_t data = 1;
|
||||
(void)write(fds[1], &data, 1);
|
||||
IGNORE_RET(write(fds[1], &data, 1));
|
||||
|
||||
// Child process should have exited with a zero status.
|
||||
int ret;
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/mac/handler/minidump_generator.h"
|
||||
#include "client/mac/tests/spawn_child_process.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/mac/MachIPC.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
@ -190,7 +191,7 @@ TEST_F(MinidumpGeneratorTest, OutOfProcess) {
|
||||
|
||||
// Unblock child process
|
||||
uint8_t data = 1;
|
||||
(void)write(fds[1], &data, 1);
|
||||
IGNORE_RET(write(fds[1], &data, 1));
|
||||
|
||||
// Child process should have exited with a zero status.
|
||||
int ret;
|
||||
|
@ -41,18 +41,17 @@
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static const int kWaitForHandlerThreadMs = 60000;
|
||||
static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
|
||||
|
||||
// As documented on MSDN, on failure SuspendThread returns (DWORD) -1
|
||||
static const DWORD kFailedToSuspendThread = static_cast<DWORD>(-1);
|
||||
|
||||
// This is passed as the context to the MinidumpWriteDump callback.
|
||||
typedef struct {
|
||||
AppMemoryList::const_iterator iter;
|
||||
AppMemoryList::const_iterator end;
|
||||
} MinidumpCallbackContext;
|
||||
|
||||
// This define is new to Windows 10.
|
||||
#ifndef DBG_PRINTEXCEPTION_WIDE_C
|
||||
#define DBG_PRINTEXCEPTION_WIDE_C ((DWORD)0x4001000A)
|
||||
#endif
|
||||
|
||||
vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
|
||||
LONG ExceptionHandler::handler_stack_index_ = 0;
|
||||
CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
|
||||
@ -217,6 +216,7 @@ void ExceptionHandler::Initialize(
|
||||
// Don't attempt to create the thread if we could not create the semaphores.
|
||||
if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) {
|
||||
DWORD thread_id;
|
||||
const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
|
||||
handler_thread_ = CreateThread(NULL, // lpThreadAttributes
|
||||
kExceptionHandlerThreadInitialStackSize,
|
||||
ExceptionHandlerThreadMain,
|
||||
@ -353,6 +353,7 @@ ExceptionHandler::~ExceptionHandler() {
|
||||
// inside DllMain.
|
||||
is_shutdown_ = true;
|
||||
ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
|
||||
const int kWaitForHandlerThreadMs = 60000;
|
||||
WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs);
|
||||
#else
|
||||
TerminateThread(handler_thread_, 1);
|
||||
@ -480,7 +481,9 @@ LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) {
|
||||
DWORD code = exinfo->ExceptionRecord->ExceptionCode;
|
||||
LONG action;
|
||||
bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) ||
|
||||
(code == EXCEPTION_SINGLE_STEP);
|
||||
(code == EXCEPTION_SINGLE_STEP) ||
|
||||
(code == DBG_PRINTEXCEPTION_C) ||
|
||||
(code == DBG_PRINTEXCEPTION_WIDE_C);
|
||||
|
||||
if (code == EXCEPTION_INVALID_HANDLE &&
|
||||
current_handler->consume_invalid_handle_exceptions_) {
|
||||
@ -764,9 +767,10 @@ bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) {
|
||||
// static
|
||||
bool ExceptionHandler::WriteMinidump(const wstring &dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
void* callback_context,
|
||||
MINIDUMP_TYPE dump_type) {
|
||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context,
|
||||
HANDLER_NONE);
|
||||
HANDLER_NONE, dump_type, (HANDLE)NULL, NULL);
|
||||
return handler.WriteMinidump();
|
||||
}
|
||||
|
||||
@ -775,10 +779,13 @@ bool ExceptionHandler::WriteMinidumpForChild(HANDLE child,
|
||||
DWORD child_blamed_thread,
|
||||
const wstring& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context) {
|
||||
void* callback_context,
|
||||
MINIDUMP_TYPE dump_type) {
|
||||
EXCEPTION_RECORD ex;
|
||||
CONTEXT ctx;
|
||||
EXCEPTION_POINTERS exinfo = { NULL, NULL };
|
||||
// As documented on MSDN, on failure SuspendThread returns (DWORD) -1
|
||||
const DWORD kFailedToSuspendThread = static_cast<DWORD>(-1);
|
||||
DWORD last_suspend_count = kFailedToSuspendThread;
|
||||
HANDLE child_thread_handle = OpenThread(THREAD_GET_CONTEXT |
|
||||
THREAD_QUERY_INFORMATION |
|
||||
@ -806,7 +813,7 @@ bool ExceptionHandler::WriteMinidumpForChild(HANDLE child,
|
||||
}
|
||||
|
||||
ExceptionHandler handler(dump_path, NULL, callback, callback_context,
|
||||
HANDLER_NONE);
|
||||
HANDLER_NONE, dump_type, (HANDLE)NULL, NULL);
|
||||
bool success = handler.WriteMinidumpWithExceptionForProcess(
|
||||
child_blamed_thread,
|
||||
exinfo.ExceptionRecord ? &exinfo : NULL,
|
||||
|
@ -238,7 +238,8 @@ class ExceptionHandler {
|
||||
// Convenience form of WriteMinidump which does not require an
|
||||
// ExceptionHandler instance.
|
||||
static bool WriteMinidump(const wstring &dump_path,
|
||||
MinidumpCallback callback, void* callback_context);
|
||||
MinidumpCallback callback, void* callback_context,
|
||||
MINIDUMP_TYPE dump_type = MiniDumpNormal);
|
||||
|
||||
// Write a minidump of |child| immediately. This can be used to
|
||||
// capture the execution state of |child| independently of a crash.
|
||||
@ -249,7 +250,8 @@ class ExceptionHandler {
|
||||
DWORD child_blamed_thread,
|
||||
const wstring& dump_path,
|
||||
MinidumpCallback callback,
|
||||
void* callback_context);
|
||||
void* callback_context,
|
||||
MINIDUMP_TYPE dump_type = MiniDumpNormal);
|
||||
|
||||
// Get the thread ID of the thread requesting the dump (either the exception
|
||||
// thread or any other thread that called WriteMinidump directly). This
|
||||
|
@ -28,9 +28,7 @@
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/windows/crash_generation/crash_generation_server.h"
|
||||
#include "client/windows/common/ipc_protocol.h"
|
||||
|
||||
|
@ -31,8 +31,8 @@
|
||||
#include <objbase.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/windows/unittests/dump_analysis.h" // NOLINT
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
DumpAnalysis::~DumpAnalysis() {
|
||||
if (dump_file_view_ != NULL) {
|
||||
|
@ -31,11 +31,10 @@
|
||||
#include <objbase.h>
|
||||
#include <dbghelp.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/windows/crash_generation/minidump_generator.h"
|
||||
#include "client/windows/unittests/dump_analysis.h" // NOLINT
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Minidump with stacks, PEB, TEB, and unloaded module list.
|
||||
|
@ -38,11 +38,12 @@
|
||||
'type': 'static_library',
|
||||
'include_dirs': [
|
||||
'<(DEPTH)/testing/include',
|
||||
'<(DEPTH)/testing/gtest',
|
||||
'<(DEPTH)/testing/gtest/include',
|
||||
'<(DEPTH)/testing/googletest/include',
|
||||
'<(DEPTH)/testing/googletest',
|
||||
'<(DEPTH)/testing',
|
||||
],
|
||||
'sources': [
|
||||
'<(DEPTH)/testing/gtest/src/gtest-all.cc',
|
||||
'<(DEPTH)/testing/googletest/src/gtest-all.cc',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
@ -61,18 +62,24 @@
|
||||
'type': 'static_library',
|
||||
'include_dirs': [
|
||||
'<(DEPTH)/testing/include',
|
||||
'<(DEPTH)/testing/',
|
||||
'<(DEPTH)/testing/gtest',
|
||||
'<(DEPTH)/testing/gtest/include',
|
||||
'<(DEPTH)/testing/googletest/include',
|
||||
'<(DEPTH)/testing/googletest',
|
||||
'<(DEPTH)/testing/googlemock/include',
|
||||
'<(DEPTH)/testing/googlemock',
|
||||
'<(DEPTH)/testing',
|
||||
],
|
||||
'sources': [
|
||||
'<(DEPTH)/testing/src/gmock-all.cc',
|
||||
'<(DEPTH)/testing/src/gmock_main.cc',
|
||||
'<(DEPTH)/testing/googlemock/src/gmock-all.cc',
|
||||
'<(DEPTH)/testing/googletest/src/gtest_main.cc',
|
||||
],
|
||||
'direct_dependent_settings': {
|
||||
'include_dirs': [
|
||||
'<(DEPTH)/testing/include',
|
||||
'<(DEPTH)/testing/gtest/include',
|
||||
'<(DEPTH)/testing/googletest/include',
|
||||
'<(DEPTH)/testing/googletest',
|
||||
'<(DEPTH)/testing/googlemock/include',
|
||||
'<(DEPTH)/testing/googlemock',
|
||||
'<(DEPTH)/testing',
|
||||
],
|
||||
'defines': ['_VARIADIC_MAX=10'],
|
||||
},
|
||||
|
@ -34,12 +34,20 @@
|
||||
// glibc) and therefore avoid doing otherwise awkward #ifdefs in the code.
|
||||
// The following quirks are currently handled by this file:
|
||||
// - i386: Use the Android NDK but alias user_fxsr_struct > user_fpxregs_struct.
|
||||
// - aarch64: Add missing user_regs_struct and user_fpsimd_struct structs.
|
||||
// - aarch64:
|
||||
// - NDK r10: Add missing user_regs_struct and user_fpsimd_struct structs.
|
||||
// - NDK r11+: Add missing <stdint.h> include
|
||||
// - Other platforms: Just use the Android NDK unchanged.
|
||||
|
||||
// TODO(primiano): remove these changes after Chromium has stably rolled to
|
||||
// an NDK with the appropriate fixes.
|
||||
|
||||
#if defined(ANDROID_NDK_MAJOR_VERSION) && ANDROID_NDK_MAJOR_VERSION > 10
|
||||
#ifdef __aarch64__
|
||||
#include <stdint.h>
|
||||
#endif // __aarch64__
|
||||
#endif // defined(ANDROID_NDK_MAJOR_VERSION) && ANDROID_NDK_MAJOR_VERSION > 10
|
||||
|
||||
#include_next <sys/user.h>
|
||||
|
||||
#ifdef __i386__
|
||||
@ -52,6 +60,7 @@ typedef struct user_fxsr_struct user_fpxregs_struct;
|
||||
#endif // __cplusplus
|
||||
#endif // __i386__
|
||||
|
||||
#if !defined(ANDROID_NDK_MAJOR_VERSION) || ANDROID_NDK_MAJOR_VERSION == 10
|
||||
#ifdef __aarch64__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -71,5 +80,6 @@ struct user_fpsimd_struct {
|
||||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
#endif // __aarch64__
|
||||
#endif // defined(ANDROID_NDK_VERSION) && ANDROID_NDK_MAJOR_VERSION == 10
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H
|
||||
|
@ -46,7 +46,9 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
|
||||
unsigned return_address_register,
|
||||
uint8_t version,
|
||||
const string &augmentation,
|
||||
bool dwarf64) {
|
||||
bool dwarf64,
|
||||
uint8_t address_size,
|
||||
uint8_t segment_size) {
|
||||
assert(!entry_length_);
|
||||
entry_length_ = new PendingLength();
|
||||
in_fde_ = false;
|
||||
@ -63,6 +65,10 @@ CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
|
||||
}
|
||||
D8(version);
|
||||
AppendCString(augmentation);
|
||||
if (version >= 4) {
|
||||
D8(address_size);
|
||||
D8(segment_size);
|
||||
}
|
||||
ULEB128(code_alignment_factor);
|
||||
LEB128(data_alignment_factor);
|
||||
if (version == 1)
|
||||
|
@ -138,7 +138,9 @@ class CFISection: public Section {
|
||||
unsigned return_address_register,
|
||||
uint8_t version = 3,
|
||||
const string &augmentation = "",
|
||||
bool dwarf64 = false);
|
||||
bool dwarf64 = false,
|
||||
uint8_t address_size = 8,
|
||||
uint8_t segment_size = 0);
|
||||
|
||||
// Append a Frame Description Entry header to this section with the
|
||||
// given values. If dwarf64 is true, use the 64-bit DWARF initial
|
||||
|
@ -232,6 +232,8 @@ enum DwarfAttribute {
|
||||
DW_AT_call_column = 0x57,
|
||||
DW_AT_call_file = 0x58,
|
||||
DW_AT_call_line = 0x59,
|
||||
// DWARF 4
|
||||
DW_AT_linkage_name = 0x6e,
|
||||
// SGI/MIPS extensions.
|
||||
DW_AT_MIPS_fde = 0x2001,
|
||||
DW_AT_MIPS_loop_begin = 0x2002,
|
||||
@ -499,7 +501,7 @@ enum DwarfOpcode {
|
||||
DW_OP_call_frame_cfa =0x9c,
|
||||
DW_OP_bit_piece =0x9d,
|
||||
DW_OP_lo_user =0xe0,
|
||||
DW_OP_hi_user =0xff,
|
||||
DW_OP_hi_user =0xff,
|
||||
// GNU extensions
|
||||
DW_OP_GNU_push_tls_address =0xe0,
|
||||
// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
|
||||
@ -542,6 +544,8 @@ enum DwarfLanguage
|
||||
DW_LANG_ObjC_plus_plus =0x0011,
|
||||
DW_LANG_UPC =0x0012,
|
||||
DW_LANG_D =0x0013,
|
||||
DW_LANG_Rust =0x001c,
|
||||
DW_LANG_Swift =0x001e,
|
||||
// Implementation-defined language code range.
|
||||
DW_LANG_lo_user = 0x8000,
|
||||
DW_LANG_hi_user = 0xffff,
|
||||
@ -668,7 +672,7 @@ enum DwarfPointerEncoding
|
||||
// encoding (except DW_EH_PE_aligned), and indicates that the
|
||||
// encoded value represents the address at which the true address
|
||||
// is stored, not the true address itself.
|
||||
DW_EH_PE_indirect = 0x80
|
||||
DW_EH_PE_indirect = 0x80
|
||||
};
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
@ -2253,11 +2253,11 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
cursor++;
|
||||
|
||||
// If we don't recognize the version, we can't parse any more fields of the
|
||||
// CIE. For DWARF CFI, we handle versions 1 through 3 (there was never a
|
||||
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 3 as well;
|
||||
// CIE. For DWARF CFI, we handle versions 1 through 4 (there was never a
|
||||
// version 2 of CFI data). For .eh_frame, we handle versions 1 and 4 as well;
|
||||
// the difference between those versions seems to be the same as for
|
||||
// .debug_frame.
|
||||
if (cie->version < 1 || cie->version > 3) {
|
||||
if (cie->version < 1 || cie->version > 4) {
|
||||
reporter_->UnrecognizedVersion(cie->offset, cie->version);
|
||||
return false;
|
||||
}
|
||||
@ -2287,16 +2287,36 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
}
|
||||
}
|
||||
|
||||
if (cie->version >= 4) {
|
||||
uint8_t address_size = *cursor++;
|
||||
if (address_size != 8) {
|
||||
// TODO(scottmg): Only supporting x64 for now.
|
||||
reporter_->UnexpectedAddressSize(cie->offset, address_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t segment_size = *cursor++;
|
||||
if (segment_size != 0) {
|
||||
// TODO(scottmg): Only supporting x64 for now.
|
||||
// I would have perhaps expected 4 here, but LLVM emits a 0, near
|
||||
// http://llvm.org/docs/doxygen/html/MCDwarf_8cpp_source.html#l00606. As
|
||||
// we are not using the value, only succeed for now if it's the expected
|
||||
// 0.
|
||||
reporter_->UnexpectedSegmentSize(cie->offset, segment_size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the code alignment factor.
|
||||
cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len);
|
||||
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
||||
cursor += len;
|
||||
|
||||
|
||||
// Parse the data alignment factor.
|
||||
cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len);
|
||||
if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie);
|
||||
cursor += len;
|
||||
|
||||
|
||||
// Parse the return address register. This is a ubyte in version 1, and
|
||||
// a ULEB128 in version 3.
|
||||
if (cie->version == 1) {
|
||||
@ -2407,7 +2427,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool CallFrameInfo::ReadFDEFields(FDE *fde) {
|
||||
const uint8_t *cursor = fde->fields;
|
||||
size_t size;
|
||||
@ -2648,6 +2668,22 @@ void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) {
|
||||
filename_.c_str(), offset, section_.c_str(), cie_offset);
|
||||
}
|
||||
|
||||
void CallFrameInfo::Reporter::UnexpectedAddressSize(uint64 offset,
|
||||
uint8_t address_size) {
|
||||
fprintf(stderr,
|
||||
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||
" CIE specifies unexpected address size: %d\n",
|
||||
filename_.c_str(), offset, section_.c_str(), address_size);
|
||||
}
|
||||
|
||||
void CallFrameInfo::Reporter::UnexpectedSegmentSize(uint64 offset,
|
||||
uint8_t segment_size) {
|
||||
fprintf(stderr,
|
||||
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||
" CIE specifies unexpected segment size: %d\n",
|
||||
filename_.c_str(), offset, section_.c_str(), segment_size);
|
||||
}
|
||||
|
||||
void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) {
|
||||
fprintf(stderr,
|
||||
"%s: CFI frame description entry at offset 0x%llx in '%s':"
|
||||
|
@ -1227,6 +1227,14 @@ class CallFrameInfo::Reporter {
|
||||
// there is not a CIE.
|
||||
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with an address size we don't know how
|
||||
// to handle.
|
||||
virtual void UnexpectedAddressSize(uint64 offset, uint8_t address_size);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with an segment descriptor size we
|
||||
// don't know how to handle.
|
||||
virtual void UnexpectedSegmentSize(uint64 offset, uint8_t segment_size);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with version number VERSION,
|
||||
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
||||
// a version number we don't recognize.
|
||||
|
@ -126,6 +126,8 @@ class MockCallFrameErrorReporter: public CallFrameInfo::Reporter {
|
||||
MOCK_METHOD1(EarlyEHTerminator, void(uint64));
|
||||
MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64));
|
||||
MOCK_METHOD2(BadCIEId, void(uint64, uint64));
|
||||
MOCK_METHOD2(UnexpectedAddressSize, void(uint64, uint8_t));
|
||||
MOCK_METHOD2(UnexpectedSegmentSize, void(uint64, uint8_t));
|
||||
MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version));
|
||||
MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &));
|
||||
MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8));
|
||||
@ -605,6 +607,91 @@ TEST_F(CFI, CIEVersion3ReturnColumn) {
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
||||
TEST_F(CFI, CIEVersion4AdditionalFields) {
|
||||
CFISection section(kBigEndian, 4);
|
||||
Label cie;
|
||||
section
|
||||
.Mark(&cie)
|
||||
// CIE version 4 with expected address and segment size.
|
||||
.CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 4, "", true, 8, 0)
|
||||
.FinishEntry()
|
||||
// FDE, citing that CIE.
|
||||
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
|
||||
.FinishEntry();
|
||||
|
||||
PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section);
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 4, "", 0x89))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(handler, End()).WillOnce(Return(true));
|
||||
}
|
||||
|
||||
string contents;
|
||||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
||||
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedAddressSize) {
|
||||
CFISection section(kBigEndian, 4);
|
||||
Label cie;
|
||||
|
||||
section
|
||||
.Mark(&cie)
|
||||
// Unexpected address size.
|
||||
.CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 4, "", true, 3, 0)
|
||||
.FinishEntry()
|
||||
// FDE, citing that CIE.
|
||||
.FDEHeader(cie, 0x86763f2b, 0x2a66dc23)
|
||||
.FinishEntry();
|
||||
|
||||
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedAddress", section);
|
||||
|
||||
EXPECT_CALL(reporter, UnexpectedAddressSize(_, 3))
|
||||
.WillOnce(Return());
|
||||
|
||||
string contents;
|
||||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
||||
TEST_F(CFI, CIEVersion4AdditionalFieldsUnexpectedSegmentSize) {
|
||||
CFISection section(kBigEndian, 4);
|
||||
Label cie;
|
||||
|
||||
section
|
||||
.Mark(&cie)
|
||||
.CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 4, "", true, 8, 7)
|
||||
.FinishEntry()
|
||||
.FDEHeader(cie, 0x7bf0fda0, 0xcbcd28d8)
|
||||
.FinishEntry();
|
||||
|
||||
PERHAPS_WRITE_DEBUG_FRAME_FILE("AdditionalFieldsUnexpectedSegment", section);
|
||||
|
||||
EXPECT_CALL(reporter, UnexpectedSegmentSize(_, 7))
|
||||
.WillOnce(Return());
|
||||
|
||||
string contents;
|
||||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
||||
struct CFIInsnFixture: public CFIFixture {
|
||||
CFIInsnFixture() : CFIFixture() {
|
||||
data_factor = 0xb6f;
|
||||
|
@ -47,7 +47,8 @@
|
||||
|
||||
#include "third_party/musl/include/elf.h"
|
||||
#include "elf_reader.h"
|
||||
//#include "using_std_string.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
// EM_AARCH64 is not defined by elf.h of GRTE v3 on x86.
|
||||
// TODO(dougkwan): Remove this when v17 is retired.
|
||||
#if !defined(EM_AARCH64)
|
||||
@ -74,7 +75,6 @@
|
||||
//DEFINE_bool(elfreader_process_dynsyms, true,
|
||||
// "Activate PLT function processing");
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
namespace {
|
||||
|
@ -19,8 +19,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/types.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::pair;
|
||||
|
||||
|
@ -195,7 +195,7 @@ void DwarfCFIToModule::Record(Module::Address address, int reg,
|
||||
|
||||
// Place the name in our global set of strings, and then use the string
|
||||
// from the set. Even though the assignment looks like a copy, all the
|
||||
// major std::string implementations use reference counting internally,
|
||||
// major string implementations use reference counting internally,
|
||||
// so the effect is to have all our data structures share copies of rules
|
||||
// whenever possible. Since register names are drawn from a
|
||||
// vector<string>, register names are already shared.
|
||||
|
@ -181,7 +181,7 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
|
||||
|
||||
// The names of the return address and canonical frame address. Putting
|
||||
// these here instead of using string literals allows us to share their
|
||||
// texts in reference-counted std::string implementations (all the
|
||||
// texts in reference-counted string implementations (all the
|
||||
// popular ones). Many, many rules cite these strings.
|
||||
string cfa_name_, ra_name_;
|
||||
|
||||
@ -189,7 +189,7 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
|
||||
// our data structures, insert it into this set, and then use the string
|
||||
// from the set.
|
||||
//
|
||||
// Because std::string uses reference counting internally, simply using
|
||||
// Because string uses reference counting internally, simply using
|
||||
// strings from this set, even if passed by value, assigned, or held
|
||||
// directly in structures and containers (map<string, ...>, for example),
|
||||
// causes those strings to share a single instance of each distinct piece
|
||||
|
@ -39,9 +39,6 @@
|
||||
#include "common/dwarf_cu_to_module.h"
|
||||
|
||||
#include <assert.h>
|
||||
#if !defined(__ANDROID__)
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@ -264,7 +261,7 @@ class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
|
||||
uint64 offset_;
|
||||
|
||||
// Place the name in the global set of strings. Even though this looks
|
||||
// like a copy, all the major std::string implementations use reference
|
||||
// like a copy, all the major string implementations use reference
|
||||
// counting internally, so the effect is to have all the data structures
|
||||
// share copies of strings whenever possible.
|
||||
// FIXME: Should this return something like a string_ref to avoid the
|
||||
@ -350,20 +347,22 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||
case dwarf2reader::DW_AT_name:
|
||||
name_attribute_ = AddStringToPool(data);
|
||||
break;
|
||||
case dwarf2reader::DW_AT_MIPS_linkage_name: {
|
||||
char* demangled = 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));
|
||||
case dwarf2reader::DW_AT_MIPS_linkage_name:
|
||||
case dwarf2reader::DW_AT_linkage_name: {
|
||||
string demangled;
|
||||
Language::DemangleResult result =
|
||||
cu_context_->language->DemangleName(data, &demangled);
|
||||
switch (result) {
|
||||
case Language::kDemangleSuccess:
|
||||
demangled_name_ = AddStringToPool(demangled);
|
||||
break;
|
||||
|
||||
case Language::kDemangleFailure:
|
||||
cu_context_->reporter->DemangleError(data);
|
||||
// fallthrough
|
||||
case Language::kDontDemangle:
|
||||
demangled_name_.clear();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -676,11 +675,10 @@ void DwarfCUToModule::WarningReporter::UnnamedFunction(uint64 offset) {
|
||||
filename_.c_str(), offset);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::DemangleError(
|
||||
const string &input, int error) {
|
||||
void DwarfCUToModule::WarningReporter::DemangleError(const string &input) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: failed to demangle %s with error %d\n",
|
||||
filename_.c_str(), input.c_str(), error);
|
||||
fprintf(stderr, "%s: warning: failed to demangle %s\n",
|
||||
filename_.c_str(), input.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnhandledInterCUReference(
|
||||
@ -761,6 +759,7 @@ dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
|
||||
case dwarf2reader::DW_TAG_class_type:
|
||||
case dwarf2reader::DW_TAG_structure_type:
|
||||
case dwarf2reader::DW_TAG_union_type:
|
||||
case dwarf2reader::DW_TAG_module:
|
||||
return new NamedScopeHandler(cu_context_.get(), child_context_.get(),
|
||||
offset);
|
||||
default:
|
||||
@ -774,6 +773,14 @@ void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
|
||||
cu_context_->language = Language::Java;
|
||||
break;
|
||||
|
||||
case dwarf2reader::DW_LANG_Swift:
|
||||
cu_context_->language = Language::Swift;
|
||||
break;
|
||||
|
||||
case dwarf2reader::DW_LANG_Rust:
|
||||
cu_context_->language = Language::Rust;
|
||||
break;
|
||||
|
||||
// DWARF has no generic language code for assembly language; this is
|
||||
// what the GNU toolchain uses.
|
||||
case dwarf2reader::DW_LANG_Mips_Assembler:
|
||||
|
@ -202,7 +202,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
||||
virtual void UnnamedFunction(uint64 offset);
|
||||
|
||||
// __cxa_demangle() failed to demangle INPUT.
|
||||
virtual void DemangleError(const string &input, int error);
|
||||
virtual void DemangleError(const string &input);
|
||||
|
||||
// The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
|
||||
// FilePrivate did not retain the inter-CU specification data.
|
||||
|
@ -83,7 +83,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_METHOD1(DemangleError, void(const string &input));
|
||||
MOCK_METHOD2(UnhandledInterCUReference, void(uint64 offset, uint64 target));
|
||||
};
|
||||
|
||||
@ -1205,6 +1205,7 @@ TEST_F(Specifications, Function) {
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MangledName) {
|
||||
// Language defaults to C++, so no need to set it here.
|
||||
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
|
||||
|
||||
StartCU();
|
||||
@ -1221,6 +1222,53 @@ TEST_F(Specifications, MangledName) {
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MangledNameSwift) {
|
||||
// Swift mangled names should pass through untouched.
|
||||
SetLanguage(dwarf2reader::DW_LANG_Swift);
|
||||
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
|
||||
StartCU();
|
||||
const string kName = "_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si";
|
||||
DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
|
||||
dwarf2reader::DW_TAG_subprogram, "declaration-name",
|
||||
kName);
|
||||
DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
|
||||
0xcd3c51b946fb1eeeLL, "",
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
root_handler_.Finish();
|
||||
|
||||
TestFunctionCount(1);
|
||||
TestFunction(0, kName,
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MangledNameRust) {
|
||||
SetLanguage(dwarf2reader::DW_LANG_Rust);
|
||||
PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661);
|
||||
|
||||
StartCU();
|
||||
const string kName = "_ZN14rustc_demangle8demangle17h373defa94bffacdeE";
|
||||
DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL,
|
||||
dwarf2reader::DW_TAG_subprogram, "declaration-name",
|
||||
kName);
|
||||
DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram,
|
||||
0xcd3c51b946fb1eeeLL, "",
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
root_handler_.Finish();
|
||||
|
||||
TestFunctionCount(1);
|
||||
TestFunction(0,
|
||||
#ifndef HAVE_RUST_DEMANGLE
|
||||
// Rust mangled names should pass through untouched if not
|
||||
// using rust-demangle.
|
||||
kName,
|
||||
#else
|
||||
// If rust-demangle is available this should be properly
|
||||
// demangled.
|
||||
"rustc_demangle::demangle",
|
||||
#endif
|
||||
0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL);
|
||||
}
|
||||
|
||||
TEST_F(Specifications, MemberFunction) {
|
||||
PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691);
|
||||
|
||||
|
@ -34,18 +34,70 @@
|
||||
|
||||
#include "common/language.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_RUST_DEMANGLE)
|
||||
#include <rust_demangle.h>
|
||||
#endif
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace {
|
||||
|
||||
string MakeQualifiedNameWithSeparator(const string& parent_name,
|
||||
const char* separator,
|
||||
const string& name) {
|
||||
if (parent_name.empty()) {
|
||||
return name;
|
||||
}
|
||||
|
||||
return parent_name + separator + name;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// C++ language-specific operations.
|
||||
class CPPLanguage: public Language {
|
||||
public:
|
||||
CPPLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "::" + name;
|
||||
return MakeQualifiedNameWithSeparator(parent_name, "::", name);
|
||||
}
|
||||
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
string* demangled) const {
|
||||
#if defined(__ANDROID__)
|
||||
// Android NDK doesn't provide abi::__cxa_demangle.
|
||||
demangled->clear();
|
||||
return kDontDemangle;
|
||||
#else
|
||||
int status;
|
||||
char* demangled_c =
|
||||
abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
|
||||
|
||||
DemangleResult result;
|
||||
if (status == 0) {
|
||||
result = kDemangleSuccess;
|
||||
demangled->assign(demangled_c);
|
||||
} else {
|
||||
result = kDemangleFailure;
|
||||
demangled->clear();
|
||||
}
|
||||
|
||||
if (demangled_c) {
|
||||
free(reinterpret_cast<void*>(demangled_c));
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
@ -54,19 +106,79 @@ CPPLanguage CPPLanguageSingleton;
|
||||
// Java language-specific operations.
|
||||
class JavaLanguage: public Language {
|
||||
public:
|
||||
JavaLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "." + name;
|
||||
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
|
||||
}
|
||||
};
|
||||
|
||||
JavaLanguage JavaLanguageSingleton;
|
||||
|
||||
// Swift language-specific operations.
|
||||
class SwiftLanguage: public Language {
|
||||
public:
|
||||
SwiftLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
|
||||
}
|
||||
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
string* demangled) const {
|
||||
// There is no programmatic interface to a Swift demangler. Pass through the
|
||||
// mangled form because it encodes more information than the qualified name
|
||||
// that would have been built by MakeQualifiedName(). The output can be
|
||||
// post-processed by xcrun swift-demangle to transform mangled Swift names
|
||||
// into something more readable.
|
||||
demangled->assign(mangled);
|
||||
return kDemangleSuccess;
|
||||
}
|
||||
};
|
||||
|
||||
SwiftLanguage SwiftLanguageSingleton;
|
||||
|
||||
// Rust language-specific operations.
|
||||
class RustLanguage: public Language {
|
||||
public:
|
||||
RustLanguage() {}
|
||||
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
return MakeQualifiedNameWithSeparator(parent_name, ".", name);
|
||||
}
|
||||
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
std::string* demangled) const {
|
||||
// Rust names use GCC C++ name mangling, but demangling them with
|
||||
// abi_demangle doesn't produce stellar results due to them having
|
||||
// another layer of encoding.
|
||||
// If callers provide rustc-demangle, use that.
|
||||
#if defined(HAVE_RUST_DEMANGLE)
|
||||
char* rust_demangled = rust_demangle(mangled.c_str());
|
||||
if (rust_demangled == nullptr) {
|
||||
return kDemangleFailure;
|
||||
}
|
||||
demangled->assign(rust_demangled);
|
||||
free_rust_demangled_name(rust_demangled);
|
||||
#else
|
||||
// Otherwise, pass through the mangled name so callers can demangle
|
||||
// after the fact.
|
||||
demangled->assign(mangled);
|
||||
#endif
|
||||
return kDemangleSuccess;
|
||||
}
|
||||
};
|
||||
|
||||
RustLanguage RustLanguageSingleton;
|
||||
|
||||
// Assembler language-specific operations.
|
||||
class AssemblerLanguage: public Language {
|
||||
public:
|
||||
AssemblerLanguage() {}
|
||||
|
||||
bool HasFunctions() const { return false; }
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
@ -78,6 +190,8 @@ AssemblerLanguage AssemblerLanguageSingleton;
|
||||
|
||||
const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
|
||||
const Language * const Language::Java = &JavaLanguageSingleton;
|
||||
const Language * const Language::Swift = &SwiftLanguageSingleton;
|
||||
const Language * const Language::Rust = &RustLanguageSingleton;
|
||||
const Language * const Language::Assembler = &AssemblerLanguageSingleton;
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -77,9 +77,26 @@ class Language {
|
||||
virtual string MakeQualifiedName (const string &parent_name,
|
||||
const string &name) const = 0;
|
||||
|
||||
enum DemangleResult {
|
||||
// Demangling was not performed because it’s not appropriate to attempt.
|
||||
kDontDemangle = -1,
|
||||
|
||||
kDemangleSuccess,
|
||||
kDemangleFailure,
|
||||
};
|
||||
|
||||
// Wraps abi::__cxa_demangle() or similar for languages where appropriate.
|
||||
virtual DemangleResult DemangleName(const string& mangled,
|
||||
string* demangled) const {
|
||||
demangled->clear();
|
||||
return kDontDemangle;
|
||||
}
|
||||
|
||||
// Instances for specific languages.
|
||||
static const Language * const CPlusPlus,
|
||||
* const Java,
|
||||
* const Swift,
|
||||
* const Rust,
|
||||
* const Assembler;
|
||||
};
|
||||
|
||||
|
@ -926,8 +926,10 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
|
||||
// number appended to the end of the file identifier; this isn't
|
||||
// really used or necessary on other platforms, but be consistent.
|
||||
string id = FileID::ConvertIdentifierToUUIDString(identifier) + "0";
|
||||
// This is just the raw Build ID in hex.
|
||||
string code_id = FileID::ConvertIdentifierToString(identifier);
|
||||
|
||||
module.reset(new Module(name, os, architecture, id));
|
||||
module.reset(new Module(name, os, architecture, id, code_id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -40,6 +40,8 @@
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/elf_gnu_compat.h"
|
||||
#include "common/linux/elfutils.h"
|
||||
#include "common/linux/dump_symbols.h"
|
||||
#include "common/linux/synth_elf.h"
|
||||
#include "common/module.h"
|
||||
@ -54,6 +56,7 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file,
|
||||
Module** module);
|
||||
|
||||
using google_breakpad::synth_elf::ELF;
|
||||
using google_breakpad::synth_elf::Notes;
|
||||
using google_breakpad::synth_elf::StringTable;
|
||||
using google_breakpad::synth_elf::SymbolTable;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
@ -61,7 +64,9 @@ using google_breakpad::test_assembler::Section;
|
||||
using std::stringstream;
|
||||
using std::vector;
|
||||
using ::testing::Test;
|
||||
using ::testing::Types;
|
||||
|
||||
template<typename ElfClass>
|
||||
class DumpSymbols : public Test {
|
||||
public:
|
||||
void GetElfContents(ELF& elf) {
|
||||
@ -78,7 +83,11 @@ class DumpSymbols : public Test {
|
||||
uint8_t* elfdata;
|
||||
};
|
||||
|
||||
TEST_F(DumpSymbols, Invalid) {
|
||||
typedef Types<ElfClass32, ElfClass64> ElfClasses;
|
||||
|
||||
TYPED_TEST_CASE(DumpSymbols, ElfClasses);
|
||||
|
||||
TYPED_TEST(DumpSymbols, Invalid) {
|
||||
Elf32_Ehdr header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
Module* module;
|
||||
@ -90,8 +99,8 @@ TEST_F(DumpSymbols, Invalid) {
|
||||
&module));
|
||||
}
|
||||
|
||||
TEST_F(DumpSymbols, SimplePublic32) {
|
||||
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
|
||||
TYPED_TEST(DumpSymbols, SimplePublic) {
|
||||
ELF elf(TypeParam::kMachine, TypeParam::kClass, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
@ -99,8 +108,11 @@ TEST_F(DumpSymbols, SimplePublic32) {
|
||||
|
||||
// Add a public symbol.
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 4, table);
|
||||
syms.AddSymbol("superfunc", (uint32_t)0x1000, (uint32_t)0x10,
|
||||
SymbolTable syms(kLittleEndian, TypeParam::kAddrSize, table);
|
||||
syms.AddSymbol("superfunc",
|
||||
(typename TypeParam::Addr)0x1000,
|
||||
(typename TypeParam::Addr)0x10,
|
||||
// ELF32_ST_INFO works for 32-or 64-bit.
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
@ -109,14 +121,14 @@ TEST_F(DumpSymbols, SimplePublic32) {
|
||||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(Elf32_Sym)); // entsize
|
||||
sizeof(typename TypeParam::Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
GetElfContents(elf);
|
||||
this->GetElfContents(elf);
|
||||
|
||||
Module* module;
|
||||
DumpOptions options(ALL_SYMBOL_DATA, true);
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
|
||||
"foo",
|
||||
vector<string>(),
|
||||
options,
|
||||
@ -124,24 +136,40 @@ TEST_F(DumpSymbols, SimplePublic32) {
|
||||
|
||||
stringstream s;
|
||||
module->Write(s, ALL_SYMBOL_DATA);
|
||||
EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
const string expected =
|
||||
string("MODULE Linux ") + TypeParam::kMachineName
|
||||
+ " 000000000000000000000000000000000 foo\n"
|
||||
"INFO CODE_ID 00000000000000000000000000000000\n"
|
||||
"PUBLIC 1000 0 superfunc\n";
|
||||
EXPECT_EQ(expected, s.str());
|
||||
delete module;
|
||||
}
|
||||
|
||||
TEST_F(DumpSymbols, SimplePublic64) {
|
||||
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
TYPED_TEST(DumpSymbols, SimpleBuildID) {
|
||||
ELF elf(TypeParam::kMachine, TypeParam::kClass, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf.AddSection(".text", text, SHT_PROGBITS);
|
||||
|
||||
// Add a Build ID
|
||||
const uint8_t kExpectedIdentifierBytes[] =
|
||||
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13};
|
||||
Notes notes(kLittleEndian);
|
||||
notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
|
||||
sizeof(kExpectedIdentifierBytes));
|
||||
elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
|
||||
|
||||
// Add a public symbol.
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 8, table);
|
||||
syms.AddSymbol("superfunc", (uint64_t)0x1000, (uint64_t)0x10,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SymbolTable syms(kLittleEndian, TypeParam::kAddrSize, table);
|
||||
syms.AddSymbol("superfunc",
|
||||
(typename TypeParam::Addr)0x1000,
|
||||
(typename TypeParam::Addr)0x10,
|
||||
// ELF32_ST_INFO works for 32-or 64-bit.
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
elf.AddSection(".dynsym", syms,
|
||||
@ -149,14 +177,14 @@ TEST_F(DumpSymbols, SimplePublic64) {
|
||||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(Elf64_Sym)); // entsize
|
||||
sizeof(typename TypeParam::Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
GetElfContents(elf);
|
||||
this->GetElfContents(elf);
|
||||
|
||||
Module* module;
|
||||
DumpOptions options(ALL_SYMBOL_DATA, true);
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
|
||||
"foo",
|
||||
vector<string>(),
|
||||
options,
|
||||
@ -164,9 +192,13 @@ TEST_F(DumpSymbols, SimplePublic64) {
|
||||
|
||||
stringstream s;
|
||||
module->Write(s, ALL_SYMBOL_DATA);
|
||||
EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
const string expected =
|
||||
string("MODULE Linux ") + TypeParam::kMachineName
|
||||
+ " 030201000504070608090A0B0C0D0E0F0 foo\n"
|
||||
"INFO CODE_ID 000102030405060708090A0B0C0D0E0F10111213\n"
|
||||
"PUBLIC 1000 0 superfunc\n";
|
||||
EXPECT_EQ(expected, s.str());
|
||||
delete module;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -49,9 +49,13 @@ struct ElfClass32 {
|
||||
typedef Elf32_Shdr Shdr;
|
||||
typedef Elf32_Half Half;
|
||||
typedef Elf32_Off Off;
|
||||
typedef Elf32_Sym Sym;
|
||||
typedef Elf32_Word Word;
|
||||
|
||||
static const int kClass = ELFCLASS32;
|
||||
static const uint16_t kMachine = EM_386;
|
||||
static const size_t kAddrSize = sizeof(Elf32_Addr);
|
||||
static constexpr const char* kMachineName = "x86";
|
||||
};
|
||||
|
||||
struct ElfClass64 {
|
||||
@ -62,9 +66,13 @@ struct ElfClass64 {
|
||||
typedef Elf64_Shdr Shdr;
|
||||
typedef Elf64_Half Half;
|
||||
typedef Elf64_Off Off;
|
||||
typedef Elf64_Sym Sym;
|
||||
typedef Elf64_Word Word;
|
||||
|
||||
static const int kClass = ELFCLASS64;
|
||||
static const uint16_t kMachine = EM_X86_64;
|
||||
static const size_t kAddrSize = sizeof(Elf64_Addr);
|
||||
static constexpr const char* kMachineName = "x86_64";
|
||||
};
|
||||
|
||||
bool IsValidElf(const void* elf_header);
|
||||
|
@ -45,10 +45,9 @@
|
||||
#include "common/linux/elfutils.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/memory_mapped_file.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Used in a few places for backwards-compatibility.
|
||||
@ -164,8 +163,18 @@ bool FileID::ElfFileIdentifier(wasteful_vector<uint8_t>& identifier) {
|
||||
return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||
}
|
||||
|
||||
// This function is not ever called in an unsafe context, so it's OK
|
||||
// These three functions are not ever called in an unsafe context, so it's OK
|
||||
// to allocate memory and use libc.
|
||||
static string bytes_to_hex_string(const uint8_t* bytes, size_t count) {
|
||||
string result;
|
||||
for (unsigned int idx = 0; idx < count; ++idx) {
|
||||
char buf[3];
|
||||
snprintf(buf, sizeof(buf), "%02X", bytes[idx]);
|
||||
result.append(buf);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// static
|
||||
string FileID::ConvertIdentifierToUUIDString(
|
||||
const wasteful_vector<uint8_t>& identifier) {
|
||||
@ -181,13 +190,13 @@ string FileID::ConvertIdentifierToUUIDString(
|
||||
uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
|
||||
*data3 = htons(*data3);
|
||||
|
||||
string result;
|
||||
for (unsigned int idx = 0; idx < kMDGUIDSize; ++idx) {
|
||||
char buf[3];
|
||||
snprintf(buf, sizeof(buf), "%02X", identifier_swapped[idx]);
|
||||
result.append(buf);
|
||||
}
|
||||
return result;
|
||||
return bytes_to_hex_string(identifier_swapped, kMDGUIDSize);
|
||||
}
|
||||
|
||||
// static
|
||||
string FileID::ConvertIdentifierToString(
|
||||
const wasteful_vector<uint8_t>& identifier) {
|
||||
return bytes_to_hex_string(&identifier[0], identifier.size());
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -38,6 +38,7 @@
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
#include "common/memory.h"
|
||||
#include "common/using_std_string.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
@ -70,12 +71,16 @@ class FileID {
|
||||
// Convert the |identifier| data to a string. The string will
|
||||
// be formatted as a UUID in all uppercase without dashes.
|
||||
// (e.g., 22F065BBFC9C49F780FE26A7CEBD7BCE).
|
||||
static std::string ConvertIdentifierToUUIDString(
|
||||
static string ConvertIdentifierToUUIDString(
|
||||
const wasteful_vector<uint8_t>& identifier);
|
||||
|
||||
// Convert the entire |identifier| data to a hex string.
|
||||
static string ConvertIdentifierToString(
|
||||
const wasteful_vector<uint8_t>& identifier);
|
||||
|
||||
private:
|
||||
// Storage for the path specified
|
||||
std::string path_;
|
||||
string path_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -319,3 +319,20 @@ TYPED_TEST(FileIDTest, UniqueHashes) {
|
||||
|
||||
EXPECT_NE(identifier_string_1, identifier_string_2);
|
||||
}
|
||||
|
||||
TYPED_TEST(FileIDTest, ConvertIdentifierToString) {
|
||||
const uint8_t kIdentifierBytes[] =
|
||||
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
|
||||
const char* kExpected =
|
||||
"000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";
|
||||
|
||||
id_vector identifier(this->make_vector());
|
||||
identifier.insert(identifier.end(),
|
||||
kIdentifierBytes,
|
||||
kIdentifierBytes + sizeof(kIdentifierBytes));
|
||||
ASSERT_EQ(kExpected,
|
||||
FileID::ConvertIdentifierToString(identifier));
|
||||
}
|
||||
|
@ -87,18 +87,7 @@ bool MemoryMappedFile::Map(const char* path, size_t offset) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#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) {
|
||||
// Not page aligned.
|
||||
sys_close(fd);
|
||||
return false;
|
||||
}
|
||||
void* data = sys_mmap2(
|
||||
NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset >> 12);
|
||||
#endif
|
||||
sys_close(fd);
|
||||
if (data == MAP_FAILED) {
|
||||
return false;
|
||||
|
@ -202,7 +202,7 @@ bool CrashGenerator::CreateChildCrash(
|
||||
// On Android the signal sometimes doesn't seem to get sent even though
|
||||
// tkill returns '0'. Retry a couple of times if the signal doesn't get
|
||||
// through on the first go:
|
||||
// https://code.google.com/p/google-breakpad/issues/detail?id=579
|
||||
// https://bugs.chromium.org/p/google-breakpad/issues/detail?id=579
|
||||
#if defined(__ANDROID__)
|
||||
const int kRetries = 60;
|
||||
const unsigned int kSleepTimeInSeconds = 1;
|
||||
|
@ -117,7 +117,7 @@
|
||||
// developer level errors. This implementation simply macros to NSLog/NSAssert.
|
||||
// It is not intended to be a general logging/reporting system.
|
||||
//
|
||||
// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert
|
||||
// Please see https://github.com/google/google-toolbox-for-mac/wiki/DevLogNAssert
|
||||
// for a little more background on the usage of these macros.
|
||||
//
|
||||
// _GTMDevLog log some error/problem in debug builds
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
// Author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dump_syms.mm: Create a symbol file for use with minidumps
|
||||
// dump_syms.cc: Create a symbol file for use with minidumps
|
||||
|
||||
#include "common/mac/dump_syms.h"
|
||||
|
||||
|
@ -1114,7 +1114,7 @@ TEST_F(LoadCommand, SegmentBE32) {
|
||||
Return(true)));
|
||||
EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
|
||||
|
||||
EXPECT_EQ(false, actual_segment.bits_64);
|
||||
EXPECT_FALSE(actual_segment.bits_64);
|
||||
EXPECT_EQ("froon", actual_segment.name);
|
||||
EXPECT_EQ(0x1891139cU, actual_segment.vmaddr);
|
||||
EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
|
||||
@ -1151,7 +1151,7 @@ TEST_F(LoadCommand, SegmentLE32) {
|
||||
Return(true)));
|
||||
EXPECT_TRUE(reader.WalkLoadCommands(&load_command_handler));
|
||||
|
||||
EXPECT_EQ(false, actual_segment.bits_64);
|
||||
EXPECT_FALSE(actual_segment.bits_64);
|
||||
EXPECT_EQ("sixteenprecisely", actual_segment.name);
|
||||
EXPECT_EQ(0x4b877866U, actual_segment.vmaddr);
|
||||
EXPECT_EQ(0xcb76584fU, actual_segment.vmsize);
|
||||
|
@ -138,8 +138,8 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
|
||||
byteReverse(ctx->in, 14);
|
||||
|
||||
/* Append length in bits and transform */
|
||||
((u32 *) ctx->in)[14] = ctx->bits[0];
|
||||
((u32 *) ctx->in)[15] = ctx->bits[1];
|
||||
memcpy(&ctx->in[14], &ctx->bits[0], sizeof(u32));
|
||||
memcpy(&ctx->in[15], &ctx->bits[1], sizeof(u32));
|
||||
|
||||
MD5Transform(ctx->buf, (u32 *) ctx->in);
|
||||
byteReverse((unsigned char *) ctx->buf, 4);
|
||||
|
@ -44,7 +44,6 @@
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define sys_mmap mmap
|
||||
#define sys_mmap2 mmap
|
||||
#define sys_munmap munmap
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#else
|
||||
@ -117,14 +116,8 @@ class PageAllocator {
|
||||
|
||||
private:
|
||||
uint8_t *GetNPages(size_t num_pages) {
|
||||
#if defined(__x86_64__) || defined(__aarch64__) || defined(__aarch64__) || \
|
||||
((defined(__mips__) && _MIPS_SIM == _ABI64))
|
||||
void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
#else
|
||||
void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
#endif
|
||||
if (a == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
|
@ -49,11 +49,13 @@ using std::hex;
|
||||
|
||||
|
||||
Module::Module(const string &name, const string &os,
|
||||
const string &architecture, const string &id) :
|
||||
const string &architecture, const string &id,
|
||||
const string &code_id /* = "" */) :
|
||||
name_(name),
|
||||
os_(os),
|
||||
architecture_(architecture),
|
||||
id_(id),
|
||||
code_id_(code_id),
|
||||
load_address_(0) { }
|
||||
|
||||
Module::~Module() {
|
||||
@ -235,6 +237,10 @@ bool Module::Write(std::ostream &stream, SymbolData symbol_data) {
|
||||
if (!stream.good())
|
||||
return ReportError();
|
||||
|
||||
if (!code_id_.empty()) {
|
||||
stream << "INFO CODE_ID " << code_id_ << endl;
|
||||
}
|
||||
|
||||
if (symbol_data != ONLY_CFI) {
|
||||
AssignSourceIds();
|
||||
|
||||
|
@ -179,7 +179,7 @@ class Module {
|
||||
// Create a new module with the given name, operating system,
|
||||
// architecture, and ID string.
|
||||
Module(const string &name, const string &os, const string &architecture,
|
||||
const string &id);
|
||||
const string &id, const string &code_id = "");
|
||||
~Module();
|
||||
|
||||
// Set the module's load address to LOAD_ADDRESS; addresses given
|
||||
@ -281,6 +281,7 @@ class Module {
|
||||
string os() const { return os_; }
|
||||
string architecture() const { return architecture_; }
|
||||
string identifier() const { return id_; }
|
||||
string code_identifier() const { return code_id_; }
|
||||
|
||||
private:
|
||||
// Report an error that has occurred writing the symbol file, using
|
||||
@ -293,7 +294,7 @@ class Module {
|
||||
static bool WriteRuleMap(const RuleMap &rule_map, std::ostream &stream);
|
||||
|
||||
// Module header entries.
|
||||
string name_, os_, architecture_, id_;
|
||||
string name_, os_, architecture_, id_, code_id_;
|
||||
|
||||
// The module's nominal load address. Addresses for functions and
|
||||
// lines are absolute, assuming the module is loaded at this
|
||||
|
@ -64,6 +64,7 @@ static Module::Function *generate_duplicate_function(const string &name) {
|
||||
#define MODULE_OS "os-name"
|
||||
#define MODULE_ARCH "architecture"
|
||||
#define MODULE_ID "id-string"
|
||||
#define MODULE_CODE_ID "code-id-string"
|
||||
|
||||
TEST(Write, Header) {
|
||||
stringstream s;
|
||||
@ -74,6 +75,16 @@ TEST(Write, Header) {
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, HeaderCodeId) {
|
||||
stringstream s;
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, MODULE_CODE_ID);
|
||||
m.Write(s, ALL_SYMBOL_DATA);
|
||||
string contents = s.str();
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"INFO CODE_ID code-id-string\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, OneLineFunc) {
|
||||
stringstream s;
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
@ -57,9 +57,9 @@ void UTF8ToUTF16(const char *in, vector<uint16_t> *out) {
|
||||
|
||||
int UTF8ToUTF16Char(const char *in, int in_length, uint16_t out[2]) {
|
||||
const UTF8 *source_ptr = reinterpret_cast<const UTF8 *>(in);
|
||||
const UTF8 *source_end_ptr = source_ptr + sizeof(char);
|
||||
const UTF8 *source_end_ptr = source_ptr + 1;
|
||||
uint16_t *target_ptr = out;
|
||||
uint16_t *target_end_ptr = target_ptr + 2 * sizeof(uint16_t);
|
||||
uint16_t *target_end_ptr = target_ptr + 2;
|
||||
out[0] = out[1] = 0;
|
||||
|
||||
// Process one character at a time
|
||||
@ -103,7 +103,7 @@ void UTF32ToUTF16Char(wchar_t in, uint16_t out[2]) {
|
||||
const UTF32 *source_ptr = reinterpret_cast<const UTF32 *>(&in);
|
||||
const UTF32 *source_end_ptr = source_ptr + 1;
|
||||
uint16_t *target_ptr = out;
|
||||
uint16_t *target_end_ptr = target_ptr + 2 * sizeof(uint16_t);
|
||||
uint16_t *target_end_ptr = target_ptr + 2;
|
||||
out[0] = out[1] = 0;
|
||||
ConversionResult result = ConvertUTF32toUTF16(&source_ptr, source_end_ptr,
|
||||
&target_ptr, target_end_ptr,
|
||||
|
@ -449,6 +449,27 @@ void BuildEndpointIndexMap(ImageMap* image_map) {
|
||||
}
|
||||
}
|
||||
|
||||
void BuildSubsequentRVAMap(const OmapData &omap_data,
|
||||
std::map<DWORD, DWORD> *subsequent) {
|
||||
assert(subsequent->empty());
|
||||
const OmapFromTable &orig2tran =
|
||||
reinterpret_cast<const OmapFromTable &>(omap_data.omap_from);
|
||||
|
||||
if (orig2tran.empty())
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < orig2tran.size() - 1; ++i) {
|
||||
// Expect that orig2tran is sorted.
|
||||
if (orig2tran[i].rva_original >= orig2tran[i + 1].rva_original) {
|
||||
fprintf(stderr, "OMAP 'from' table unexpectedly unsorted\n");
|
||||
subsequent->clear();
|
||||
return;
|
||||
}
|
||||
subsequent->insert(std::make_pair(orig2tran[i].rva_original,
|
||||
orig2tran[i + 1].rva_original));
|
||||
}
|
||||
}
|
||||
|
||||
// Clips the given mapped range.
|
||||
void ClipMappedRangeOriginal(const AddressRange& clip_range,
|
||||
MappedRange* mapped_range) {
|
||||
@ -576,6 +597,7 @@ void BuildImageMap(const OmapData& omap_data, ImageMap* image_map) {
|
||||
|
||||
BuildMapping(omap_data, &image_map->mapping);
|
||||
BuildEndpointIndexMap(image_map);
|
||||
BuildSubsequentRVAMap(omap_data, &image_map->subsequent_rva_block);
|
||||
}
|
||||
|
||||
void MapAddressRange(const ImageMap& image_map,
|
||||
@ -691,4 +713,4 @@ void MapAddressRange(const ImageMap& image_map,
|
||||
return;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
} // namespace google_breakpad
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <windows.h>
|
||||
#include <dia2.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace google_breakpad {
|
||||
@ -130,6 +131,8 @@ struct ImageMap {
|
||||
// an interval in |mapping| that contains the endpoint. Useful for doing
|
||||
// interval intersection queries.
|
||||
EndpointIndexMap endpoint_index_map;
|
||||
|
||||
std::map<DWORD, DWORD> subsequent_rva_block;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
@ -30,8 +30,7 @@
|
||||
|
||||
#include "common/windows/omap.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
@ -122,6 +122,16 @@ class AutoImage {
|
||||
PLOADED_IMAGE img_;
|
||||
};
|
||||
|
||||
bool SymbolsMatch(IDiaSymbol* a, IDiaSymbol* b) {
|
||||
DWORD a_section, a_offset, b_section, b_offset;
|
||||
if (FAILED(a->get_addressSection(&a_section)) ||
|
||||
FAILED(a->get_addressOffset(&a_offset)) ||
|
||||
FAILED(b->get_addressSection(&b_section)) ||
|
||||
FAILED(b->get_addressOffset(&b_offset)))
|
||||
return false;
|
||||
return a_section == b_section && a_offset == b_offset;
|
||||
}
|
||||
|
||||
bool CreateDiaDataSourceInstance(CComPtr<IDiaDataSource> &data_source) {
|
||||
if (SUCCEEDED(data_source.CoCreateInstance(CLSID_DiaSource))) {
|
||||
return true;
|
||||
@ -856,6 +866,39 @@ bool PDBSourceLineWriter::PrintCodePublicSymbol(IDiaSymbol *symbol) {
|
||||
stack_param_size > 0 ? stack_param_size : 0,
|
||||
name.m_str);
|
||||
}
|
||||
|
||||
// Now walk the function in the original untranslated space, asking DIA
|
||||
// what function is at that location, stepping through OMAP blocks. If
|
||||
// we're still in the same function, emit another entry, because the
|
||||
// symbol could have been split into multiple pieces. If we've gotten to
|
||||
// another symbol in the original address space, then we're done for
|
||||
// this symbol. See https://crbug.com/678874.
|
||||
for (;;) {
|
||||
// This steps to the next block in the original image. Simply doing
|
||||
// rva++ would also be correct, but would emit tons of unnecessary
|
||||
// entries.
|
||||
rva = image_map_.subsequent_rva_block[rva];
|
||||
if (rva == 0)
|
||||
break;
|
||||
|
||||
CComPtr<IDiaSymbol> next_sym = NULL;
|
||||
LONG displacement;
|
||||
if (FAILED(session_->findSymbolByRVAEx(rva, SymTagPublicSymbol, &next_sym,
|
||||
&displacement))) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!SymbolsMatch(symbol, next_sym))
|
||||
break;
|
||||
|
||||
AddressRangeVector next_ranges;
|
||||
MapAddressRange(image_map_, AddressRange(rva, 1), &next_ranges);
|
||||
for (size_t i = 0; i < next_ranges.size(); ++i) {
|
||||
fprintf(output_, "PUBLIC %x %x %ws\n", next_ranges[i].rva,
|
||||
stack_param_size > 0 ? stack_param_size : 0, name.m_str);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
*
|
||||
* Author: Mark Mentovai
|
||||
* Split into its own file: Neal Sidhwaney */
|
||||
|
||||
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_H__
|
||||
#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_H__
|
||||
@ -65,8 +65,10 @@ typedef enum {
|
||||
/* EXC_SYSCALL */
|
||||
MD_EXCEPTION_MAC_MACH_SYSCALL = 8,
|
||||
/* EXC_MACH_SYSCALL */
|
||||
MD_EXCEPTION_MAC_RPC_ALERT = 9
|
||||
MD_EXCEPTION_MAC_RPC_ALERT = 9,
|
||||
/* EXC_RPC_ALERT */
|
||||
MD_EXCEPTION_MAC_SIMULATED = 0x43507378
|
||||
/* Fake exception code used by Crashpad's SimulateCrash ('CPsx'). */
|
||||
} MDExceptionMac;
|
||||
|
||||
/* For (MDException).exception_flags. Breakpad minidump extension for Mac OS X
|
||||
|
@ -94,15 +94,23 @@ typedef enum {
|
||||
/* EXCEPTION_PRIV_INSTRUCTION */
|
||||
MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW = 0xc00000fd,
|
||||
/* EXCEPTION_STACK_OVERFLOW */
|
||||
MD_EXCEPTION_CODE_WIN_BAD_FUNCTION_TABLE = 0xc00000ff,
|
||||
/* EXCEPTION_BAD_FUNCTION_TABLE */
|
||||
MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194,
|
||||
/* EXCEPTION_POSSIBLE_DEADLOCK */
|
||||
MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN = 0xc0000409,
|
||||
/* STATUS_STACK_BUFFER_OVERRUN */
|
||||
MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION = 0xc0000374,
|
||||
/* STATUS_HEAP_CORRUPTION */
|
||||
MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363
|
||||
MD_EXCEPTION_OUT_OF_MEMORY = 0xe0000008,
|
||||
/* Exception thrown by Chromium allocators to indicate OOM.
|
||||
See base/process/memory.h in Chromium for rationale. */
|
||||
MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363,
|
||||
/* Per http://support.microsoft.com/kb/185294,
|
||||
generated by Visual C++ compiler */
|
||||
MD_EXCEPTION_CODE_WIN_SIMULATED = 0x0517a7ed
|
||||
/* Fake exception code used by Crashpad's
|
||||
CrashpadClient::DumpWithoutCrash. */
|
||||
} MDExceptionCodeWin;
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user