mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-19 09:53:47 +00:00
Merge master bac601e785fc into doc
This commit is contained in:
commit
7ac57464ca
59
.gitattributes
vendored
Normal file
59
.gitattributes
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
# Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
*.S text eol=lf
|
||||
*.asm text eol=lf
|
||||
*.c text eol=lf
|
||||
*.cc text eol=lf
|
||||
*.cmx text eol=lf
|
||||
*.css text eol=lf
|
||||
*.defs text eol=lf
|
||||
*.doxy text eol=lf
|
||||
*.gn text eol=lf
|
||||
*.gni text eol=lf
|
||||
*.go text eol=lf
|
||||
*.gyp text eol=lf
|
||||
*.gypi text eol=lf
|
||||
*.h text eol=lf
|
||||
*.m text eol=lf
|
||||
*.md text eol=lf
|
||||
*.mm text eol=lf
|
||||
*.pem text eol=lf
|
||||
*.plist text eol=lf
|
||||
*.proctype text eol=lf
|
||||
*.py text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.sym text eol=lf
|
||||
*.txt text eol=lf
|
||||
*.yaml text eol=lf
|
||||
.clang-format text eol=lf
|
||||
.gitattributes text eol=lf
|
||||
.gitignore text eol=lf
|
||||
.vpython text eol=lf
|
||||
/AUTHORS text eol=lf
|
||||
/CONTRIBUTORS text eol=lf
|
||||
/LICENSE text eol=lf
|
||||
/codereview.settings text eol=lf
|
||||
APPLE_LICENSE text eol=lf
|
||||
COPYING.LIB text eol=lf
|
||||
DEPS text eol=lf
|
||||
README text eol=lf
|
||||
README.crashpad text eol=lf
|
||||
|
||||
*.dat binary
|
||||
*.dll binary
|
||||
*.ico binary
|
||||
*.obj binary
|
||||
*.png binary
|
||||
*.so binary
|
16
.gitignore
vendored
16
.gitignore
vendored
@ -1,3 +1,17 @@
|
||||
# Copyright 2014 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
*.Makefile
|
||||
*.ninja
|
||||
*.pyc
|
||||
@ -10,6 +24,7 @@
|
||||
.gdbinit
|
||||
/Makefile
|
||||
/out
|
||||
/third_party/edo/edo
|
||||
/third_party/fuchsia/.cipd
|
||||
/third_party/fuchsia/clang
|
||||
/third_party/fuchsia/qemu
|
||||
@ -19,6 +34,7 @@
|
||||
/third_party/linux/.cipd
|
||||
/third_party/linux/clang
|
||||
/third_party/linux/sysroot
|
||||
/third_party/lss/lss
|
||||
/third_party/gyp/gyp
|
||||
/third_party/mini_chromium/mini_chromium
|
||||
/third_party/zlib/zlib
|
||||
|
16
.style.yapf
Normal file
16
.style.yapf
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
[style]
|
||||
based_on_style = google
|
32
.vpython
Normal file
32
.vpython
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright 2018 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This is a vpython "spec" file.
|
||||
#
|
||||
# It describes patterns for python wheel dependencies of the python scripts.
|
||||
#
|
||||
# Read more about `vpython` and how to modify this file here:
|
||||
# https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md
|
||||
|
||||
# This is needed for snapshot/win/end_to_end_test.py.
|
||||
wheel: <
|
||||
name: "infra/python/wheels/pypiwin32/${vpython_platform}"
|
||||
version: "version:219"
|
||||
match_tag: <
|
||||
platform: "win32"
|
||||
>
|
||||
match_tag: <
|
||||
platform: "win_amd64"
|
||||
>
|
||||
>
|
127
BUILD.gn
127
BUILD.gn
@ -14,29 +14,71 @@
|
||||
|
||||
import("build/crashpad_buildconfig.gni")
|
||||
import("build/test.gni")
|
||||
import("util/net/tls.gni")
|
||||
|
||||
config("crashpad_config") {
|
||||
include_dirs = [ "." ]
|
||||
}
|
||||
|
||||
# TODO(fuchsia:46805): Remove this once instances of UB have been cleaned up.
|
||||
config("disable_ubsan") {
|
||||
if (crashpad_is_in_fuchsia) {
|
||||
cflags = [ "-fno-sanitize=undefined" ]
|
||||
}
|
||||
visibility = [
|
||||
"snapshot:snapshot",
|
||||
"minidump:minidump_test",
|
||||
"third_party/gtest:gtest",
|
||||
"util:util",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||
test("crashpad_tests") {
|
||||
deps = [
|
||||
"client:client_test",
|
||||
"handler:handler_test",
|
||||
"minidump:minidump_test",
|
||||
"snapshot:snapshot_test",
|
||||
"test:gmock_main",
|
||||
"test:test_test",
|
||||
"util:util_test",
|
||||
]
|
||||
if (!crashpad_is_ios) {
|
||||
deps += [ "snapshot:snapshot_test" ]
|
||||
}
|
||||
if (!crashpad_is_ios && !crashpad_is_fuchsia) {
|
||||
deps += [ "handler:handler_test" ]
|
||||
}
|
||||
if (crashpad_is_in_fuchsia) {
|
||||
# TODO(fuchsia:46559): Fix the leaks and remove this.
|
||||
deps += [ "//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS" ]
|
||||
}
|
||||
if (crashpad_is_android) {
|
||||
use_raw_android_executable = true
|
||||
|
||||
copy("crashpad_test_data") {
|
||||
testonly = true
|
||||
sources = [
|
||||
"test/test_paths_test_data_root.txt",
|
||||
"util/net/testdata/ascii_http_body.txt",
|
||||
"util/net/testdata/binary_http_body.dat",
|
||||
]
|
||||
|
||||
outputs = [ "$root_out_dir/crashpad_test_data/{{source}}" ]
|
||||
}
|
||||
|
||||
deps += [ ":crashpad_test_data" ]
|
||||
|
||||
extra_dist_files = [
|
||||
"$root_out_dir/crashpad_handler",
|
||||
"$root_out_dir/crashpad_test_test_multiprocess_exec_test_child",
|
||||
"$root_out_dir/crashpad_test_data",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_in_fuchsia) {
|
||||
import("//build/package.gni")
|
||||
package("crashpad_test") {
|
||||
testonly = true
|
||||
deprecated_system_image = true
|
||||
|
||||
deps = [
|
||||
":crashpad_tests",
|
||||
@ -45,7 +87,6 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||
"snapshot:crashpad_snapshot_test_module_large",
|
||||
"snapshot:crashpad_snapshot_test_module_small",
|
||||
"test:crashpad_test_test_multiprocess_exec_test_child",
|
||||
"util:generate_test_server_key",
|
||||
"util:http_transport_test_server",
|
||||
]
|
||||
|
||||
@ -53,24 +94,23 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||
{
|
||||
name = "crashpad_tests"
|
||||
},
|
||||
]
|
||||
|
||||
meta = [
|
||||
{
|
||||
path = "test/fuchsia_crashpad_tests.cmx"
|
||||
dest = "crashpad_tests.cmx"
|
||||
},
|
||||
]
|
||||
|
||||
binaries = [
|
||||
{
|
||||
name = "crashpad_test_test_multiprocess_exec_test_child"
|
||||
dest = "crashpad_test_data/crashpad_test_test_multiprocess_exec_test_child"
|
||||
dest = "crashpad_test_test_multiprocess_exec_test_child"
|
||||
},
|
||||
{
|
||||
name = "http_transport_test_server"
|
||||
dest = "crashpad_test_data/http_transport_test_server"
|
||||
},
|
||||
|
||||
# These aren't actually tests, but that seems to be the only way to
|
||||
# convince package() to get them from the output directory.
|
||||
{
|
||||
name = "crashpad_util_test_cert.pem"
|
||||
dest = "crashpad_test_data/crashpad_util_test_cert.pem"
|
||||
},
|
||||
{
|
||||
name = "crashpad_util_test_key.pem"
|
||||
dest = "crashpad_test_data/crashpad_util_test_key.pem"
|
||||
dest = "http_transport_test_server"
|
||||
},
|
||||
]
|
||||
|
||||
@ -92,43 +132,39 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||
resources = [
|
||||
{
|
||||
path = "util/net/testdata/ascii_http_body.txt"
|
||||
dest = "crashpad_test_data/util/net/testdata/ascii_http_body.txt"
|
||||
dest = "util/net/testdata/ascii_http_body.txt"
|
||||
},
|
||||
{
|
||||
path = "util/net/testdata/binary_http_body.dat"
|
||||
dest = "crashpad_test_data/util/net/testdata/binary_http_body.dat"
|
||||
dest = "util/net/testdata/binary_http_body.dat"
|
||||
},
|
||||
{
|
||||
path = "test/test_paths_test_data_root.txt"
|
||||
dest = "crashpad_test_data/test/test_paths_test_data_root.txt"
|
||||
dest = "test/test_paths_test_data_root.txt"
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
package("crashpad_handler") {
|
||||
deprecated_system_image = true
|
||||
|
||||
deps = [
|
||||
"handler:crashpad_handler",
|
||||
]
|
||||
|
||||
binaries = [
|
||||
{
|
||||
name = "crashpad_handler"
|
||||
},
|
||||
]
|
||||
if (crashpad_use_boringssl_for_http_transport_socket) {
|
||||
resources += [
|
||||
{
|
||||
path = "util/net/testdata/crashpad_util_test_cert.pem"
|
||||
dest = "util/net/testdata/crashpad_util_test_cert.pem"
|
||||
},
|
||||
{
|
||||
path = "util/net/testdata/crashpad_util_test_key.pem"
|
||||
dest = "util/net/testdata/crashpad_util_test_key.pem"
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
package("crashpad_database_util") {
|
||||
deprecated_system_image = true
|
||||
|
||||
deps = [
|
||||
"tools:crashpad_database_util",
|
||||
]
|
||||
deps = [ "tools:crashpad_database_util" ]
|
||||
|
||||
binaries = [
|
||||
{
|
||||
name = "crashpad_database_util"
|
||||
shell = true
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -146,6 +182,9 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||
"handler:handler_test",
|
||||
"test:gtest_main",
|
||||
]
|
||||
if (crashpad_is_ios || crashpad_is_fuchsia) {
|
||||
deps -= [ "handler:handler_test" ]
|
||||
}
|
||||
}
|
||||
|
||||
test("crashpad_minidump_test") {
|
||||
@ -160,6 +199,9 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||
"snapshot:snapshot_test",
|
||||
"test:gtest_main",
|
||||
]
|
||||
if (crashpad_is_ios) {
|
||||
deps -= [ "snapshot:snapshot_test" ]
|
||||
}
|
||||
}
|
||||
|
||||
test("crashpad_test_test") {
|
||||
@ -176,3 +218,10 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_ios) {
|
||||
group("ios_xcuitests") {
|
||||
testonly = true
|
||||
deps = [ "test/ios:all_tests" ]
|
||||
}
|
||||
}
|
||||
|
@ -12,3 +12,4 @@
|
||||
Mark Mentovai <mark@chromium.org>
|
||||
Robert Sesek <rsesek@chromium.org>
|
||||
Scott Graham <scottmg@chromium.org>
|
||||
Joshua Peraza <jperaza@chromium.org>
|
||||
|
99
DEPS
99
DEPS
@ -15,22 +15,34 @@
|
||||
vars = {
|
||||
'chromium_git': 'https://chromium.googlesource.com',
|
||||
'pull_linux_clang': False,
|
||||
'pull_win_toolchain': False
|
||||
'pull_win_toolchain': False,
|
||||
# Controls whether crashpad/build/ios/setup-ios-gn.py is run as part of
|
||||
# gclient hooks. It is enabled by default for developer's convenience. It can
|
||||
# be disabled with custom_vars (done automatically on the bots).
|
||||
'run_setup_ios_gn': True,
|
||||
}
|
||||
|
||||
deps = {
|
||||
'buildtools':
|
||||
Var('chromium_git') + '/chromium/buildtools.git@' +
|
||||
'6fe4a3251488f7af86d64fc25cf442e817cf6133',
|
||||
Var('chromium_git') + '/chromium/src/buildtools.git@' +
|
||||
'4164a305626786b1912d467003acf4c4995bec7d',
|
||||
'crashpad/third_party/edo/edo': {
|
||||
'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' +
|
||||
'243fc89ae95b24717d41f3786f6a9abeeef87c92',
|
||||
'condition': 'checkout_ios',
|
||||
},
|
||||
'crashpad/third_party/gtest/gtest':
|
||||
Var('chromium_git') + '/external/github.com/google/googletest@' +
|
||||
'c091b0469ab4c04ee9411ef770f32360945f4c53',
|
||||
'e3f0319d89f4cbf32993de595d984183b1a9fc57',
|
||||
'crashpad/third_party/gyp/gyp':
|
||||
Var('chromium_git') + '/external/gyp@' +
|
||||
'5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f',
|
||||
'8bee09f4a57807136593ddc906b0b213c21f9014',
|
||||
'crashpad/third_party/lss/lss':
|
||||
Var('chromium_git') + '/linux-syscall-support.git@' +
|
||||
'7bde79cc274d06451bf65ae82c012a5d3e476b5a',
|
||||
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||
'793e94e2c652831af2d25bb5288b04e59048c62d',
|
||||
'8ca5ea356cdb97913d62d379d503567a80d90726',
|
||||
'crashpad/third_party/libfuzzer/src':
|
||||
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
|
||||
'fda403cf93ecb8792cb1d061564d89a6553ca020',
|
||||
@ -49,7 +61,7 @@ deps = {
|
||||
'condition': 'checkout_linux and pull_linux_clang',
|
||||
'dep_type': 'cipd'
|
||||
},
|
||||
'crashpad/third_party/linux/clang/mac-amd64': {
|
||||
'crashpad/third_party/fuchsia/clang/mac-amd64': {
|
||||
'packages': [
|
||||
{
|
||||
'package': 'fuchsia/clang/mac-amd64',
|
||||
@ -89,22 +101,24 @@ deps = {
|
||||
'condition': 'checkout_fuchsia and host_os == "linux"',
|
||||
'dep_type': 'cipd'
|
||||
},
|
||||
'crashpad/third_party/fuchsia/sdk/linux-amd64': {
|
||||
# The SDK is keyed to the host system because it contains build tools.
|
||||
# Currently, linux-amd64 is the only SDK published (see
|
||||
# https://chrome-infra-packages.appspot.com/#/?path=fuchsia/sdk).
|
||||
# As long as this is the case, use that SDK package
|
||||
# even on other build hosts.
|
||||
# The sysroot (containing headers and libraries) and other components are
|
||||
# related to the target and should be functional with an appropriate
|
||||
# toolchain that runs on the build host (fuchsia_clang, above).
|
||||
'crashpad/third_party/fuchsia/sdk/mac-amd64': {
|
||||
'packages': [
|
||||
{
|
||||
'package': 'fuchsia/sdk/linux-amd64',
|
||||
'package': 'fuchsia/sdk/gn/mac-amd64',
|
||||
'version': 'latest'
|
||||
},
|
||||
],
|
||||
'condition': 'checkout_fuchsia',
|
||||
'condition': 'checkout_fuchsia and host_os == "mac"',
|
||||
'dep_type': 'cipd'
|
||||
},
|
||||
'crashpad/third_party/fuchsia/sdk/linux-amd64': {
|
||||
'packages': [
|
||||
{
|
||||
'package': 'fuchsia/sdk/gn/linux-amd64',
|
||||
'version': 'latest'
|
||||
},
|
||||
],
|
||||
'condition': 'checkout_fuchsia and host_os == "linux"',
|
||||
'dep_type': 'cipd'
|
||||
},
|
||||
'crashpad/third_party/win/toolchain': {
|
||||
@ -163,45 +177,6 @@ hooks = [
|
||||
'buildtools/win/clang-format.exe.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'gn_mac',
|
||||
'pattern': '.',
|
||||
'condition': 'host_os == "mac"',
|
||||
'action': [
|
||||
'download_from_google_storage',
|
||||
'--no_resume',
|
||||
'--no_auth',
|
||||
'--bucket=chromium-gn',
|
||||
'--sha1_file',
|
||||
'buildtools/mac/gn.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'gn_linux',
|
||||
'pattern': '.',
|
||||
'condition': 'host_os == "linux"',
|
||||
'action': [
|
||||
'download_from_google_storage',
|
||||
'--no_resume',
|
||||
'--no_auth',
|
||||
'--bucket=chromium-gn',
|
||||
'--sha1_file',
|
||||
'buildtools/linux64/gn.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'gn_win',
|
||||
'pattern': '.',
|
||||
'condition': 'host_os == "win"',
|
||||
'action': [
|
||||
'download_from_google_storage',
|
||||
'--no_resume',
|
||||
'--no_auth',
|
||||
'--bucket=chromium-gn',
|
||||
'--sha1_file',
|
||||
'buildtools/win/gn.exe.sha1',
|
||||
],
|
||||
},
|
||||
{
|
||||
# If using a local clang ("pull_linux_clang" above), also pull down a
|
||||
# sysroot.
|
||||
@ -213,9 +188,13 @@ hooks = [
|
||||
],
|
||||
},
|
||||
{
|
||||
'name': 'gyp',
|
||||
'pattern': '\.gypi?$',
|
||||
'action': ['python', 'crashpad/build/gyp_crashpad.py'],
|
||||
'name': 'setup_gn_ios',
|
||||
'pattern': '.',
|
||||
'condition': 'run_setup_ios_gn and checkout_ios',
|
||||
'action': [
|
||||
'python',
|
||||
'crashpad/build/ios/setup_ios_gn.py'
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -36,7 +36,7 @@ https://chromium.googlesource.com/crashpad/crashpad.
|
||||
|
||||
* Bugs can be reported at the [Crashpad issue
|
||||
tracker](https://crashpad.chromium.org/bug/).
|
||||
* The [Crashpad Buildbots](https://build.chromium.org/p/client.crashpad)
|
||||
* The [Crashpad bots](https://ci.chromium.org/p/crashpad/g/main/console)
|
||||
perform automated builds and tests.
|
||||
* [crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev)
|
||||
is the Crashpad developers’ mailing list.
|
||||
|
@ -32,9 +32,7 @@ config("crashpad_is_in_fuchsia") {
|
||||
|
||||
group("default_exe_manifest_win") {
|
||||
if (crashpad_is_in_chromium) {
|
||||
deps = [
|
||||
"//build/win:default_exe_manifest",
|
||||
]
|
||||
deps = [ "//build/win:default_exe_manifest" ]
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,7 +43,26 @@ config("crashpad_fuzzer_flags") {
|
||||
"-fsanitize=fuzzer",
|
||||
]
|
||||
|
||||
ldflags = [
|
||||
"-fsanitize=address",
|
||||
]
|
||||
ldflags = [ "-fsanitize=address" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_ios) {
|
||||
group("ios_enable_arc") {
|
||||
if (crashpad_is_in_chromium) {
|
||||
public_configs = [ "//build/config/compiler:enable_arc" ]
|
||||
} else if (crashpad_is_standalone) {
|
||||
public_configs =
|
||||
[ "//third_party/mini_chromium/mini_chromium/build:ios_enable_arc" ]
|
||||
}
|
||||
}
|
||||
|
||||
group("ios_xctest") {
|
||||
if (crashpad_is_in_chromium) {
|
||||
public_configs = [ "//build/config/ios:xctest_config" ]
|
||||
} else if (crashpad_is_standalone) {
|
||||
public_configs = [
|
||||
"//third_party/mini_chromium/mini_chromium/build/ios:xctest_config",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ declare_args() {
|
||||
_default_configs = [
|
||||
"//third_party/mini_chromium/mini_chromium/build:default",
|
||||
"//third_party/mini_chromium/mini_chromium/build:Wexit_time_destructors",
|
||||
"//third_party/mini_chromium/mini_chromium/build:Wimplicit_fallthrough",
|
||||
]
|
||||
|
||||
if (crashpad_use_libfuzzer) {
|
||||
|
@ -35,6 +35,11 @@
|
||||
}],
|
||||
],
|
||||
}],
|
||||
['OS=="android"', {
|
||||
'ldflags': [
|
||||
'-static-libstdc++',
|
||||
],
|
||||
}],
|
||||
],
|
||||
},
|
||||
}
|
||||
|
@ -15,28 +15,28 @@
|
||||
declare_args() {
|
||||
# Determines various flavors of build configuration, and which concrete
|
||||
# targets to use for dependencies. Valid values are "standalone", "chromium",
|
||||
# and "fuchsia".
|
||||
# "fuchsia", "dart" or "external".
|
||||
crashpad_dependencies = "standalone"
|
||||
|
||||
if (defined(is_fuchsia_tree) && is_fuchsia_tree) {
|
||||
# Determines various flavors of build configuration, and which concrete
|
||||
# targets to use for dependencies. Valid values are "standalone",
|
||||
# "chromium", and "fuchsia". Defaulted to "fuchsia" because
|
||||
# "is_fuchsia_tree" is set.
|
||||
crashpad_dependencies = "fuchsia"
|
||||
}
|
||||
}
|
||||
|
||||
assert(
|
||||
crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" ||
|
||||
crashpad_dependencies == "standalone")
|
||||
crashpad_dependencies == "standalone" ||
|
||||
crashpad_dependencies == "external" || crashpad_dependencies == "dart")
|
||||
|
||||
crashpad_is_in_chromium = crashpad_dependencies == "chromium"
|
||||
crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia"
|
||||
crashpad_is_in_dart = crashpad_dependencies == "dart"
|
||||
crashpad_is_external = crashpad_dependencies == "external"
|
||||
crashpad_is_standalone = crashpad_dependencies == "standalone"
|
||||
|
||||
if (crashpad_is_in_chromium) {
|
||||
crashpad_is_mac = is_mac
|
||||
crashpad_is_ios = is_ios
|
||||
crashpad_is_win = is_win
|
||||
crashpad_is_linux = is_linux
|
||||
crashpad_is_android = is_android
|
||||
@ -46,11 +46,18 @@ if (crashpad_is_in_chromium) {
|
||||
|
||||
crashpad_is_clang = is_clang
|
||||
} else {
|
||||
# Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into
|
||||
# the same location in both cases.
|
||||
import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
|
||||
import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
|
||||
# External and Dart SDK builds assume crashpad and mini_chromium are peers.
|
||||
if (crashpad_is_external || crashpad_is_in_dart) {
|
||||
import("../../../mini_chromium/mini_chromium/build/compiler.gni")
|
||||
import("../../../mini_chromium/mini_chromium/build/platform.gni")
|
||||
} else {
|
||||
# Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into
|
||||
# the same location in both cases.
|
||||
import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
|
||||
import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
|
||||
}
|
||||
crashpad_is_mac = mini_chromium_is_mac
|
||||
crashpad_is_ios = mini_chromium_is_ios
|
||||
crashpad_is_win = mini_chromium_is_win
|
||||
crashpad_is_linux = mini_chromium_is_linux
|
||||
crashpad_is_android = mini_chromium_is_android
|
||||
@ -63,7 +70,12 @@ if (crashpad_is_in_chromium) {
|
||||
|
||||
template("crashpad_executable") {
|
||||
executable(target_name) {
|
||||
forward_variables_from(invoker, "*", [ "configs", "remove_configs" ])
|
||||
forward_variables_from(invoker,
|
||||
"*",
|
||||
[
|
||||
"configs",
|
||||
"remove_configs",
|
||||
])
|
||||
if (defined(invoker.remove_configs)) {
|
||||
configs -= invoker.remove_configs
|
||||
}
|
||||
@ -72,21 +84,23 @@ template("crashpad_executable") {
|
||||
configs += invoker.configs
|
||||
}
|
||||
|
||||
if (!defined(deps)) {
|
||||
deps = []
|
||||
}
|
||||
|
||||
if (crashpad_is_in_chromium) {
|
||||
deps += [ "//build/config:exe_and_shlib_deps" ]
|
||||
} else if (crashpad_is_in_fuchsia) {
|
||||
configs += [ "//build/config/fuchsia:fdio_config" ]
|
||||
if (crashpad_is_in_fuchsia) {
|
||||
fdio_config = [ "//build/config/fuchsia:fdio_config" ]
|
||||
if (configs + fdio_config - fdio_config == configs) {
|
||||
configs += fdio_config
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template("crashpad_loadable_module") {
|
||||
loadable_module(target_name) {
|
||||
forward_variables_from(invoker, "*", [ "configs", "remove_configs" ])
|
||||
forward_variables_from(invoker,
|
||||
"*",
|
||||
[
|
||||
"configs",
|
||||
"remove_configs",
|
||||
])
|
||||
if (defined(invoker.remove_configs)) {
|
||||
configs -= invoker.remove_configs
|
||||
}
|
||||
@ -95,13 +109,7 @@ template("crashpad_loadable_module") {
|
||||
configs += invoker.configs
|
||||
}
|
||||
|
||||
if (!defined(deps)) {
|
||||
deps = []
|
||||
}
|
||||
|
||||
if (crashpad_is_in_chromium) {
|
||||
deps += [ "//build/config:exe_and_shlib_deps" ]
|
||||
} else if (crashpad_is_in_fuchsia) {
|
||||
if (crashpad_is_in_fuchsia) {
|
||||
configs += [ "//build/config/fuchsia:fdio_config" ]
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
# Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
declare_args() {
|
||||
# Determines various flavors of build configuration, and which concrete
|
||||
# targets to use for dependencies. Valid values are "standalone", "chromium",
|
||||
# and "fuchsia".
|
||||
crashpad_dependencies = "standalone"
|
||||
}
|
||||
|
||||
assert(
|
||||
crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" ||
|
||||
crashpad_dependencies == "standalone")
|
||||
|
||||
crashpad_is_in_chromium = crashpad_dependencies == "chromium"
|
||||
crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia"
|
||||
crashpad_is_standalone = crashpad_dependencies == "standalone"
|
||||
|
||||
if (crashpad_is_in_chromium) {
|
||||
crashpad_is_posix = is_posix
|
||||
} else if (crashpad_is_in_fuchsia) {
|
||||
import("//third_party/mini_chromium/build/is_posix.gni")
|
||||
crashpad_is_posix = mini_chromium_is_posix
|
||||
} else if (crashpad_is_standalone) {
|
||||
import("../third_party/mini_chromium/mini_chromium/build/is_posix.gni")
|
||||
crashpad_is_posix = mini_chromium_is_posix
|
||||
}
|
||||
|
||||
if (crashpad_is_in_chromium) {
|
||||
import("//testing/test.gni")
|
||||
} else {
|
||||
template("test") {
|
||||
executable(target_name) {
|
||||
testonly = true
|
||||
forward_variables_from(invoker, "*")
|
||||
}
|
||||
}
|
||||
|
||||
set_defaults("test") {
|
||||
configs = default_executable_configs
|
||||
}
|
||||
}
|
@ -14,8 +14,11 @@
|
||||
|
||||
import("crashpad_buildconfig.gni")
|
||||
import("test.gni")
|
||||
if (crashpad_is_in_chromium) {
|
||||
import("//testing/libfuzzer/fuzzer_test.gni")
|
||||
}
|
||||
|
||||
template("fuzzer_test") {
|
||||
template("crashpad_fuzzer_test") {
|
||||
if (crashpad_is_standalone && crashpad_use_libfuzzer) {
|
||||
test(target_name) {
|
||||
forward_variables_from(invoker,
|
||||
@ -38,6 +41,15 @@ template("fuzzer_test") {
|
||||
}
|
||||
cflags += [ "-fsanitize=fuzzer" ]
|
||||
}
|
||||
if (defined(invoker.seed_corpus)) {
|
||||
not_needed(invoker, [ "seed_corpus" ])
|
||||
}
|
||||
} else if (crashpad_is_in_chromium && use_fuzzing_engine) {
|
||||
# Append "crashpad_" to the beginning of the fuzzer's name to make it easier
|
||||
# in Chromium to recognize where fuzzer came from.
|
||||
forward_variables_from(invoker, "*")
|
||||
fuzzer_test("crashpad_" + target_name) {
|
||||
}
|
||||
} else {
|
||||
not_needed(invoker, "*")
|
||||
group(target_name) {
|
||||
|
@ -19,83 +19,84 @@ import sys
|
||||
|
||||
|
||||
def ChooseDependencyPath(local_path, external_path):
|
||||
"""Chooses between a dependency located at local path and an external path.
|
||||
"""Chooses between a dependency located at local path and an external path.
|
||||
|
||||
The local path, used in standalone builds, is preferred. If it is not present
|
||||
but the external path is, the external path will be used. If neither path is
|
||||
present, the local path will be used, so that error messages uniformly refer
|
||||
to the local path.
|
||||
The local path, used in standalone builds, is preferred. If it is not
|
||||
present but the external path is, the external path will be used. If neither
|
||||
path is present, the local path will be used, so that error messages
|
||||
uniformly refer to the local path.
|
||||
|
||||
Args:
|
||||
local_path: The preferred local path to use for a standalone build.
|
||||
external_path: The external path to fall back to.
|
||||
Args:
|
||||
local_path: The preferred local path to use for a standalone build.
|
||||
external_path: The external path to fall back to.
|
||||
|
||||
Returns:
|
||||
A 2-tuple. The first element is None or 'external', depending on whether
|
||||
local_path or external_path was chosen. The second element is the chosen
|
||||
path.
|
||||
"""
|
||||
if os.path.exists(local_path) or not os.path.exists(external_path):
|
||||
return (None, local_path)
|
||||
return ('external', external_path)
|
||||
Returns:
|
||||
A 2-tuple. The first element is None or 'external', depending on whether
|
||||
local_path or external_path was chosen. The second element is the chosen
|
||||
path.
|
||||
"""
|
||||
if os.path.exists(local_path) or not os.path.exists(external_path):
|
||||
return (None, local_path)
|
||||
return ('external', external_path)
|
||||
|
||||
|
||||
script_dir = os.path.dirname(__file__)
|
||||
crashpad_dir = (os.path.dirname(script_dir) if script_dir not in ('', os.curdir)
|
||||
else os.pardir)
|
||||
crashpad_dir = (os.path.dirname(script_dir)
|
||||
if script_dir not in ('', os.curdir) else os.pardir)
|
||||
|
||||
sys.path.insert(0,
|
||||
ChooseDependencyPath(os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp',
|
||||
'pylib'),
|
||||
os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp',
|
||||
'pylib'))[1])
|
||||
sys.path.insert(
|
||||
0,
|
||||
ChooseDependencyPath(
|
||||
os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib'),
|
||||
os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp', 'pylib'))[1])
|
||||
|
||||
import gyp
|
||||
|
||||
|
||||
def main(args):
|
||||
if 'GYP_GENERATORS' not in os.environ:
|
||||
os.environ['GYP_GENERATORS'] = 'ninja'
|
||||
if 'GYP_GENERATORS' not in os.environ:
|
||||
os.environ['GYP_GENERATORS'] = 'ninja'
|
||||
|
||||
crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else os.curdir
|
||||
crashpad_dir_or_dot = crashpad_dir if crashpad_dir is not '' else os.curdir
|
||||
|
||||
(dependencies, mini_chromium_common_gypi) = (ChooseDependencyPath(
|
||||
os.path.join(crashpad_dir, 'third_party', 'mini_chromium',
|
||||
'mini_chromium', 'build', 'common.gypi'),
|
||||
os.path.join(crashpad_dir, os.pardir, os.pardir, 'mini_chromium',
|
||||
'mini_chromium', 'build', 'common.gypi')))
|
||||
if dependencies is not None:
|
||||
args.extend(['-D', 'crashpad_dependencies=%s' % dependencies])
|
||||
args.extend(['--include', mini_chromium_common_gypi])
|
||||
args.extend(['--depth', crashpad_dir_or_dot])
|
||||
args.append(os.path.join(crashpad_dir, 'crashpad.gyp'))
|
||||
(dependencies, mini_chromium_common_gypi) = (ChooseDependencyPath(
|
||||
os.path.join(crashpad_dir, 'third_party', 'mini_chromium',
|
||||
'mini_chromium', 'build', 'common.gypi'),
|
||||
os.path.join(crashpad_dir, os.pardir, os.pardir, 'mini_chromium',
|
||||
'mini_chromium', 'build', 'common.gypi')))
|
||||
if dependencies is not None:
|
||||
args.extend(['-D', 'crashpad_dependencies=%s' % dependencies])
|
||||
args.extend(['--include', mini_chromium_common_gypi])
|
||||
args.extend(['--depth', crashpad_dir_or_dot])
|
||||
args.append(os.path.join(crashpad_dir, 'crashpad.gyp'))
|
||||
|
||||
result = gyp.main(args)
|
||||
if result != 0:
|
||||
return result
|
||||
|
||||
if sys.platform == 'win32':
|
||||
# Check to make sure that no target_arch was specified. target_arch may be
|
||||
# set during a cross build, such as a cross build for Android.
|
||||
has_target_arch = False
|
||||
for arg_index in range(0, len(args)):
|
||||
arg = args[arg_index]
|
||||
if (arg.startswith('-Dtarget_arch=') or
|
||||
(arg == '-D' and arg_index + 1 < len(args) and
|
||||
args[arg_index + 1].startswith('target_arch='))):
|
||||
has_target_arch = True
|
||||
break
|
||||
|
||||
if not has_target_arch:
|
||||
# Also generate the x86 build.
|
||||
result = gyp.main(args + ['-D', 'target_arch=ia32', '-G', 'config=Debug'])
|
||||
if result != 0:
|
||||
result = gyp.main(args)
|
||||
if result != 0:
|
||||
return result
|
||||
result = gyp.main(
|
||||
args + ['-D', 'target_arch=ia32', '-G', 'config=Release'])
|
||||
|
||||
return result
|
||||
if sys.platform == 'win32':
|
||||
# Check to make sure that no target_arch was specified. target_arch may
|
||||
# be set during a cross build, such as a cross build for Android.
|
||||
has_target_arch = False
|
||||
for arg_index in range(0, len(args)):
|
||||
arg = args[arg_index]
|
||||
if (arg.startswith('-Dtarget_arch=') or
|
||||
(arg == '-D' and arg_index + 1 < len(args) and
|
||||
args[arg_index + 1].startswith('target_arch='))):
|
||||
has_target_arch = True
|
||||
break
|
||||
|
||||
if not has_target_arch:
|
||||
# Also generate the x86 build.
|
||||
result = gyp.main(args +
|
||||
['-D', 'target_arch=ia32', '-G', 'config=Debug'])
|
||||
if result != 0:
|
||||
return result
|
||||
result = gyp.main(
|
||||
args + ['-D', 'target_arch=ia32', '-G', 'config=Release'])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
@ -25,89 +24,46 @@ import sys
|
||||
|
||||
|
||||
def main(args):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Set up an Android cross build',
|
||||
epilog='Additional arguments will be passed to gyp_crashpad.py.')
|
||||
parser.add_argument('--ndk', required=True, help='Standalone NDK toolchain')
|
||||
parser.add_argument('--compiler',
|
||||
default='clang',
|
||||
choices=('clang', 'gcc'),
|
||||
help='The compiler to use, clang by default')
|
||||
(parsed, extra_command_line_args) = parser.parse_known_args(args)
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Set up an Android cross build',
|
||||
epilog='Additional arguments will be passed to gyp_crashpad.py.')
|
||||
parser.add_argument('--arch', required=True, help='Target architecture')
|
||||
parser.add_argument('--api-level', required=True, help='Target API level')
|
||||
parser.add_argument('--ndk', required=True, help='Standalone NDK toolchain')
|
||||
(parsed, extra_command_line_args) = parser.parse_known_args(args)
|
||||
|
||||
NDK_ERROR=(
|
||||
'NDK must be a valid standalone NDK toolchain.\n' +
|
||||
'See https://developer.android.com/ndk/guides/standalone_toolchain.html')
|
||||
arch_dirs = glob.glob(os.path.join(parsed.ndk, '*-linux-android*'))
|
||||
if len(arch_dirs) != 1:
|
||||
parser.error(NDK_ERROR)
|
||||
ndk_bin_dir = os.path.join(parsed.ndk, 'toolchains', 'llvm', 'prebuilt',
|
||||
'linux-x86_64', 'bin')
|
||||
if not os.path.exists(ndk_bin_dir):
|
||||
parser.error("missing toolchain")
|
||||
|
||||
arch_triplet = os.path.basename(arch_dirs[0])
|
||||
ARCH_TRIPLET_TO_ARCH = {
|
||||
'arm-linux-androideabi': 'arm',
|
||||
'aarch64-linux-android': 'arm64',
|
||||
'i686-linux-android': 'ia32',
|
||||
'x86_64-linux-android': 'x64',
|
||||
'mipsel-linux-android': 'mips',
|
||||
'mips64el-linux-android': 'mips64',
|
||||
}
|
||||
if arch_triplet not in ARCH_TRIPLET_TO_ARCH:
|
||||
parser.error(NDK_ERROR)
|
||||
arch = ARCH_TRIPLET_TO_ARCH[arch_triplet]
|
||||
ARCH_TO_ARCH_TRIPLET = {
|
||||
'arm': 'armv7a-linux-androideabi',
|
||||
'arm64': 'aarch64-linux-android',
|
||||
'ia32': 'i686-linux-android',
|
||||
'x64': 'x86_64-linux-android',
|
||||
}
|
||||
|
||||
ndk_bin_dir = os.path.join(parsed.ndk, 'bin')
|
||||
|
||||
clang_path = os.path.join(ndk_bin_dir, 'clang')
|
||||
extra_args = []
|
||||
|
||||
if parsed.compiler == 'clang':
|
||||
os.environ['CC_target'] = clang_path
|
||||
os.environ['CXX_target'] = os.path.join(ndk_bin_dir, 'clang++')
|
||||
elif parsed.compiler == 'gcc':
|
||||
os.environ['CC_target'] = os.path.join(ndk_bin_dir,
|
||||
'%s-gcc' % arch_triplet)
|
||||
clang_prefix = ARCH_TO_ARCH_TRIPLET[parsed.arch] + parsed.api_level
|
||||
os.environ['CC_target'] = os.path.join(ndk_bin_dir, clang_prefix + '-clang')
|
||||
os.environ['CXX_target'] = os.path.join(ndk_bin_dir,
|
||||
'%s-g++' % arch_triplet)
|
||||
clang_prefix + '-clang++')
|
||||
|
||||
# Unlike the Clang build, when using GCC with unified headers, __ANDROID_API__
|
||||
# isn’t set automatically and must be pushed in to the build. Fish the correct
|
||||
# value out of the Clang wrapper script. If deprecated headers are in use, the
|
||||
# Clang wrapper won’t mention __ANDROID_API__, but the standalone toolchain’s
|
||||
# <android/api-level.h> will #define it for both Clang and GCC.
|
||||
#
|
||||
# android_api_level is extracted in this manner even when compiling with Clang
|
||||
# so that it’s available for use in GYP conditions that need to test the API
|
||||
# level, but beware that it’ll only be available when unified headers are in
|
||||
# use.
|
||||
#
|
||||
# Unified headers are the way of the future, according to
|
||||
# https://android.googlesource.com/platform/ndk/+/ndk-r14/CHANGELOG.md and
|
||||
# https://android.googlesource.com/platform/ndk/+/master/docs/UnifiedHeaders.md.
|
||||
# Traditional (deprecated) headers have been removed entirely as of NDK r16.
|
||||
# https://android.googlesource.com/platform/ndk/+/ndk-release-r16/CHANGELOG.md.
|
||||
with open(clang_path, 'r') as file:
|
||||
clang_script_contents = file.read()
|
||||
matches = re.finditer(r'\s-D__ANDROID_API__=([\d]+)\s',
|
||||
clang_script_contents)
|
||||
match = next(matches, None)
|
||||
if match:
|
||||
android_api = int(match.group(1))
|
||||
extra_args.extend(['-D', 'android_api_level=%d' % android_api])
|
||||
if next(matches, None):
|
||||
raise AssertionError('__ANDROID_API__ defined too many times')
|
||||
extra_args = ['-D', 'android_api_level=' + parsed.api_level]
|
||||
|
||||
for tool in ('ar', 'nm', 'readelf'):
|
||||
os.environ['%s_target' % tool.upper()] = (
|
||||
os.path.join(ndk_bin_dir, '%s-%s' % (arch_triplet, tool)))
|
||||
# ARM only includes 'v7a' in the tool prefix for clang
|
||||
tool_prefix = ('arm-linux-androideabi' if parsed.arch == 'arm' else
|
||||
ARCH_TO_ARCH_TRIPLET[parsed.arch])
|
||||
|
||||
return gyp_crashpad.main(
|
||||
['-D', 'OS=android',
|
||||
'-D', 'target_arch=%s' % arch,
|
||||
'-D', 'clang=%d' % (1 if parsed.compiler == 'clang' else 0),
|
||||
'-f', 'ninja-android'] +
|
||||
extra_args +
|
||||
extra_command_line_args)
|
||||
for tool in ('ar', 'nm', 'readelf'):
|
||||
os.environ['%s_target' % tool.upper()] = (os.path.join(
|
||||
ndk_bin_dir, '%s-%s' % (tool_prefix, tool)))
|
||||
|
||||
return gyp_crashpad.main([
|
||||
'-D', 'OS=android', '-D',
|
||||
'target_arch=%s' % parsed.arch, '-D', 'clang=1', '-f', 'ninja-android'
|
||||
] + extra_args + extra_command_line_args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
@ -23,7 +23,6 @@ import subprocess
|
||||
import sys
|
||||
import urllib2
|
||||
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Sysroot revision from:
|
||||
@ -33,42 +32,43 @@ PATH = 'chrome-linux-sysroot/toolchain'
|
||||
REVISION = '3c248ba4290a5ad07085b7af07e6785bf1ae5b66'
|
||||
FILENAME = 'debian_stretch_amd64_sysroot.tar.xz'
|
||||
|
||||
|
||||
def main():
|
||||
url = '%s/%s/%s/%s' % (SERVER, PATH, REVISION, FILENAME)
|
||||
url = '%s/%s/%s/%s' % (SERVER, PATH, REVISION, FILENAME)
|
||||
|
||||
sysroot = os.path.join(SCRIPT_DIR, os.pardir,
|
||||
'third_party', 'linux', 'sysroot')
|
||||
sysroot = os.path.join(SCRIPT_DIR, os.pardir, 'third_party', 'linux',
|
||||
'sysroot')
|
||||
|
||||
stamp = os.path.join(sysroot, '.stamp')
|
||||
if os.path.exists(stamp):
|
||||
with open(stamp) as s:
|
||||
if s.read() == url:
|
||||
return
|
||||
stamp = os.path.join(sysroot, '.stamp')
|
||||
if os.path.exists(stamp):
|
||||
with open(stamp) as s:
|
||||
if s.read() == url:
|
||||
return
|
||||
|
||||
print 'Installing Debian root image from %s' % url
|
||||
print 'Installing Debian root image from %s' % url
|
||||
|
||||
if os.path.isdir(sysroot):
|
||||
shutil.rmtree(sysroot)
|
||||
os.mkdir(sysroot)
|
||||
tarball = os.path.join(sysroot, FILENAME)
|
||||
print 'Downloading %s' % url
|
||||
if os.path.isdir(sysroot):
|
||||
shutil.rmtree(sysroot)
|
||||
os.mkdir(sysroot)
|
||||
tarball = os.path.join(sysroot, FILENAME)
|
||||
print 'Downloading %s' % url
|
||||
|
||||
for _ in range(3):
|
||||
response = urllib2.urlopen(url)
|
||||
with open(tarball, 'wb') as f:
|
||||
f.write(response.read())
|
||||
break
|
||||
else:
|
||||
raise Exception('Failed to download %s' % url)
|
||||
for _ in range(3):
|
||||
response = urllib2.urlopen(url)
|
||||
with open(tarball, 'wb') as f:
|
||||
f.write(response.read())
|
||||
break
|
||||
else:
|
||||
raise Exception('Failed to download %s' % url)
|
||||
|
||||
subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot])
|
||||
subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot])
|
||||
|
||||
os.remove(tarball)
|
||||
os.remove(tarball)
|
||||
|
||||
with open(stamp, 'w') as s:
|
||||
s.write(url)
|
||||
with open(stamp, 'w') as s:
|
||||
s.write(url)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
sys.exit(0)
|
||||
main()
|
||||
sys.exit(0)
|
||||
|
BIN
build/ios/Default.png
Normal file
BIN
build/ios/Default.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
10
build/ios/Unittest-Info.plist
Normal file
10
build/ios/Unittest-Info.plist
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${IOS_BUNDLE_ID_PREFIX}.gtest.${GTEST_BUNDLE_ID_SUFFIX:rfc1034identifier}</string>
|
||||
<key>UIApplicationDelegate</key>
|
||||
<string>CrashpadUnitTestDelegate</string>
|
||||
</dict>
|
||||
</plist>
|
283
build/ios/convert_gn_xcodeproj.py
Executable file
283
build/ios/convert_gn_xcodeproj.py
Executable file
@ -0,0 +1,283 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
"""Convert GN Xcode projects to platform and configuration independent targets.
|
||||
|
||||
GN generates Xcode projects that build one configuration only. However, typical
|
||||
iOS development involves using the Xcode IDE to toggle the platform and
|
||||
configuration. This script replaces the 'gn' configuration with 'Debug',
|
||||
'Release' and 'Profile', and changes the ninja invocation to honor these
|
||||
configurations.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import copy
|
||||
import filecmp
|
||||
import json
|
||||
import hashlib
|
||||
import os
|
||||
import plistlib
|
||||
import random
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
class XcodeProject(object):
|
||||
|
||||
def __init__(self, objects, counter=0):
|
||||
self.objects = objects
|
||||
self.counter = 0
|
||||
|
||||
def AddObject(self, parent_name, obj):
|
||||
while True:
|
||||
self.counter += 1
|
||||
str_id = "%s %s %d" % (parent_name, obj['isa'], self.counter)
|
||||
new_id = hashlib.sha1(str_id).hexdigest()[:24].upper()
|
||||
|
||||
# Make sure ID is unique. It's possible there could be an id
|
||||
# conflict since this is run after GN runs.
|
||||
if new_id not in self.objects:
|
||||
self.objects[new_id] = obj
|
||||
return new_id
|
||||
|
||||
|
||||
def CopyFileIfChanged(source_path, target_path):
|
||||
"""Copy |source_path| to |target_path| is different."""
|
||||
target_dir = os.path.dirname(target_path)
|
||||
if not os.path.isdir(target_dir):
|
||||
os.makedirs(target_dir)
|
||||
if (not os.path.exists(target_path) or
|
||||
not filecmp.cmp(source_path, target_path)):
|
||||
shutil.copyfile(source_path, target_path)
|
||||
|
||||
|
||||
def LoadXcodeProjectAsJSON(path):
|
||||
"""Return Xcode project at |path| as a JSON string."""
|
||||
return subprocess.check_output(
|
||||
['plutil', '-convert', 'json', '-o', '-', path])
|
||||
|
||||
|
||||
def WriteXcodeProject(output_path, json_string):
|
||||
"""Save Xcode project to |output_path| as XML."""
|
||||
with tempfile.NamedTemporaryFile() as temp_file:
|
||||
temp_file.write(json_string)
|
||||
temp_file.flush()
|
||||
subprocess.check_call(['plutil', '-convert', 'xml1', temp_file.name])
|
||||
CopyFileIfChanged(temp_file.name, output_path)
|
||||
|
||||
|
||||
def UpdateProductsProject(file_input, file_output, configurations, root_dir):
|
||||
"""Update Xcode project to support multiple configurations.
|
||||
|
||||
Args:
|
||||
file_input: path to the input Xcode project
|
||||
file_output: path to the output file
|
||||
configurations: list of string corresponding to the configurations that
|
||||
need to be supported by the tweaked Xcode projects, must contains at
|
||||
least one value.
|
||||
"""
|
||||
json_data = json.loads(LoadXcodeProjectAsJSON(file_input))
|
||||
project = XcodeProject(json_data['objects'])
|
||||
|
||||
objects_to_remove = []
|
||||
for value in project.objects.values():
|
||||
isa = value['isa']
|
||||
|
||||
# Teach build shell script to look for the configuration and platform.
|
||||
if isa == 'PBXShellScriptBuildPhase':
|
||||
value['shellScript'] = value['shellScript'].replace(
|
||||
'ninja -C .',
|
||||
'ninja -C "../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}"')
|
||||
|
||||
# Add new configuration, using the first one as default.
|
||||
if isa == 'XCConfigurationList':
|
||||
value['defaultConfigurationName'] = configurations[0]
|
||||
objects_to_remove.extend(value['buildConfigurations'])
|
||||
|
||||
build_config_template = project.objects[value['buildConfigurations']
|
||||
[0]]
|
||||
build_settings = build_config_template['buildSettings']
|
||||
build_settings['CONFIGURATION_BUILD_DIR'] = (
|
||||
'$(PROJECT_DIR)/../$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)')
|
||||
build_settings['CODE_SIGN_IDENTITY'] = ''
|
||||
|
||||
value['buildConfigurations'] = []
|
||||
for configuration in configurations:
|
||||
new_build_config = copy.copy(build_config_template)
|
||||
new_build_config['name'] = configuration
|
||||
value['buildConfigurations'].append(
|
||||
project.AddObject('products', new_build_config))
|
||||
|
||||
for object_id in objects_to_remove:
|
||||
del project.objects[object_id]
|
||||
|
||||
AddMarkdownToProject(project, root_dir, json_data['rootObject'])
|
||||
|
||||
objects = collections.OrderedDict(sorted(project.objects.iteritems()))
|
||||
WriteXcodeProject(file_output, json.dumps(json_data))
|
||||
|
||||
|
||||
def AddMarkdownToProject(project, root_dir, root_object):
|
||||
list_files_cmd = ['git', '-C', root_dir, 'ls-files', '*.md']
|
||||
paths = subprocess.check_output(list_files_cmd).splitlines()
|
||||
ios_internal_dir = os.path.join(root_dir, 'ios_internal')
|
||||
if os.path.exists(ios_internal_dir):
|
||||
list_files_cmd = ['git', '-C', ios_internal_dir, 'ls-files', '*.md']
|
||||
ios_paths = subprocess.check_output(list_files_cmd).splitlines()
|
||||
paths.extend(["ios_internal/" + path for path in ios_paths])
|
||||
for path in paths:
|
||||
new_markdown_entry = {
|
||||
"fileEncoding": "4",
|
||||
"isa": "PBXFileReference",
|
||||
"lastKnownFileType": "net.daringfireball.markdown",
|
||||
"name": os.path.basename(path),
|
||||
"path": path,
|
||||
"sourceTree": "<group>"
|
||||
}
|
||||
new_markdown_entry_id = project.AddObject('sources', new_markdown_entry)
|
||||
folder = GetFolderForPath(project, root_object, os.path.dirname(path))
|
||||
folder['children'].append(new_markdown_entry_id)
|
||||
|
||||
|
||||
def GetFolderForPath(project, rootObject, path):
|
||||
objects = project.objects
|
||||
# 'Sources' is always the first child of
|
||||
# project->rootObject->mainGroup->children.
|
||||
root = objects[objects[objects[rootObject]['mainGroup']]['children'][0]]
|
||||
if not path:
|
||||
return root
|
||||
for folder in path.split('/'):
|
||||
children = root['children']
|
||||
new_root = None
|
||||
for child in children:
|
||||
if (objects[child]['isa'] == 'PBXGroup' and
|
||||
objects[child]['name'] == folder):
|
||||
new_root = objects[child]
|
||||
break
|
||||
if not new_root:
|
||||
# If the folder isn't found we could just cram it into the leaf
|
||||
# existing folder, but that leads to folders with tons of README.md
|
||||
# inside.
|
||||
new_group = {
|
||||
"children": [],
|
||||
"isa": "PBXGroup",
|
||||
"name": folder,
|
||||
"sourceTree": "<group>"
|
||||
}
|
||||
new_group_id = project.AddObject('sources', new_group)
|
||||
children.append(new_group_id)
|
||||
new_root = objects[new_group_id]
|
||||
root = new_root
|
||||
return root
|
||||
|
||||
|
||||
def DisableNewBuildSystem(output_dir):
|
||||
"""Disables the new build system due to crbug.com/852522 """
|
||||
xcwspacesharedsettings = os.path.join(output_dir, 'all.xcworkspace',
|
||||
'xcshareddata',
|
||||
'WorkspaceSettings.xcsettings')
|
||||
if os.path.isfile(xcwspacesharedsettings):
|
||||
json_data = json.loads(LoadXcodeProjectAsJSON(xcwspacesharedsettings))
|
||||
else:
|
||||
json_data = {}
|
||||
json_data['BuildSystemType'] = 'Original'
|
||||
WriteXcodeProject(xcwspacesharedsettings, json.dumps(json_data))
|
||||
|
||||
|
||||
def ConvertGnXcodeProject(root_dir, input_dir, output_dir, configurations):
|
||||
'''Tweak the Xcode project generated by gn to support multiple
|
||||
configurations.
|
||||
|
||||
The Xcode projects generated by "gn gen --ide" only supports a single
|
||||
platform and configuration (as the platform and configuration are set per
|
||||
output directory). This method takes as input such projects and add support
|
||||
for multiple configurations and platforms (to allow devs to select them in
|
||||
Xcode).
|
||||
|
||||
Args:
|
||||
input_dir: directory containing the XCode projects created by "gn gen
|
||||
--ide"
|
||||
output_dir: directory where the tweaked Xcode projects will be saved
|
||||
configurations: list of string corresponding to the configurations that
|
||||
need to be supported by the tweaked Xcode projects, must contains at
|
||||
least one value.
|
||||
'''
|
||||
# Update products project.
|
||||
products = os.path.join('products.xcodeproj', 'project.pbxproj')
|
||||
product_input = os.path.join(input_dir, products)
|
||||
product_output = os.path.join(output_dir, products)
|
||||
UpdateProductsProject(product_input, product_output, configurations,
|
||||
root_dir)
|
||||
|
||||
# Copy all workspace.
|
||||
xcwspace = os.path.join('all.xcworkspace', 'contents.xcworkspacedata')
|
||||
CopyFileIfChanged(os.path.join(input_dir, xcwspace),
|
||||
os.path.join(output_dir, xcwspace))
|
||||
|
||||
# TODO(crbug.com/852522): Disable new BuildSystemType.
|
||||
DisableNewBuildSystem(output_dir)
|
||||
|
||||
# TODO(crbug.com/679110): gn has been modified to remove 'sources.xcodeproj'
|
||||
# and keep 'all.xcworkspace' and 'products.xcodeproj'. The following code is
|
||||
# here to support both old and new projects setup and will be removed once
|
||||
# gn has rolled past it.
|
||||
sources = os.path.join('sources.xcodeproj', 'project.pbxproj')
|
||||
if os.path.isfile(os.path.join(input_dir, sources)):
|
||||
CopyFileIfChanged(os.path.join(input_dir, sources),
|
||||
os.path.join(output_dir, sources))
|
||||
|
||||
|
||||
def Main(args):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Convert GN Xcode projects for iOS.')
|
||||
parser.add_argument(
|
||||
'input', help='directory containing [product|all] Xcode projects.')
|
||||
parser.add_argument(
|
||||
'output', help='directory where to generate the iOS configuration.')
|
||||
parser.add_argument('--add-config',
|
||||
dest='configurations',
|
||||
default=[],
|
||||
action='append',
|
||||
help='configuration to add to the Xcode project')
|
||||
parser.add_argument('--root',
|
||||
type=os.path.abspath,
|
||||
required=True,
|
||||
help='root directory of the project')
|
||||
args = parser.parse_args(args)
|
||||
|
||||
if not os.path.isdir(args.input):
|
||||
sys.stderr.write('Input directory does not exists.\n')
|
||||
return 1
|
||||
|
||||
required = set(['products.xcodeproj', 'all.xcworkspace'])
|
||||
if not required.issubset(os.listdir(args.input)):
|
||||
sys.stderr.write(
|
||||
'Input directory does not contain all necessary Xcode projects.\n')
|
||||
return 1
|
||||
|
||||
if not args.configurations:
|
||||
sys.stderr.write(
|
||||
'At least one configuration required, see --add-config.\n')
|
||||
return 1
|
||||
|
||||
ConvertGnXcodeProject(args.root, args.input, args.output,
|
||||
args.configurations)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(Main(sys.argv[1:]))
|
39
build/ios/setup_ios_gn.config
Normal file
39
build/ios/setup_ios_gn.config
Normal file
@ -0,0 +1,39 @@
|
||||
# Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
[goma]
|
||||
# Controls whether goma is enabled or not. If you generally use goma but
|
||||
# want to disable goma for a single build, consider using the environment
|
||||
# variable GOMA_DISABLED.
|
||||
enabled = False
|
||||
install = "$GOMA_DIR"
|
||||
|
||||
[xcode]
|
||||
# Controls settings for the generated Xcode project. If jobs is non-zero
|
||||
# it will be passed to the ninja invocation in Xcode project.
|
||||
jobs = 0
|
||||
|
||||
[build]
|
||||
# Controls the build output. The only supported values are "64-bit", "32-bit"
|
||||
# and "fat" (for a fat binary supporting both "32-bit" and "64-bit" cpus).
|
||||
arch = "64-bit"
|
||||
|
||||
[gn_args]
|
||||
# Values in that section will be copied verbatim in the generated args.gn file.
|
||||
target_os = "ios"
|
||||
|
||||
[filters]
|
||||
# List of target files to pass to --filters argument of gn gen when generating
|
||||
# the Xcode project. By default, list all targets from ios/ and ios_internal/
|
||||
# and the targets corresponding to the unit tests run on the bots.
|
350
build/ios/setup_ios_gn.py
Executable file
350
build/ios/setup_ios_gn.py
Executable file
@ -0,0 +1,350 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import argparse
|
||||
import convert_gn_xcodeproj
|
||||
import errno
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import ConfigParser
|
||||
|
||||
try:
|
||||
import cStringIO as StringIO
|
||||
except ImportError:
|
||||
import StringIO
|
||||
|
||||
SUPPORTED_TARGETS = ('iphoneos', 'iphonesimulator')
|
||||
SUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official', 'Coverage')
|
||||
|
||||
|
||||
class ConfigParserWithStringInterpolation(ConfigParser.SafeConfigParser):
|
||||
'''A .ini file parser that supports strings and environment variables.'''
|
||||
|
||||
ENV_VAR_PATTERN = re.compile(r'\$([A-Za-z0-9_]+)')
|
||||
|
||||
def values(self, section):
|
||||
return map(lambda (k, v): self._UnquoteString(self._ExpandEnvVar(v)),
|
||||
ConfigParser.SafeConfigParser.items(self, section))
|
||||
|
||||
def getstring(self, section, option):
|
||||
return self._UnquoteString(self._ExpandEnvVar(self.get(section,
|
||||
option)))
|
||||
|
||||
def _UnquoteString(self, string):
|
||||
if not string or string[0] != '"' or string[-1] != '"':
|
||||
return string
|
||||
return string[1:-1]
|
||||
|
||||
def _ExpandEnvVar(self, value):
|
||||
match = self.ENV_VAR_PATTERN.search(value)
|
||||
if not match:
|
||||
return value
|
||||
name, (begin, end) = match.group(1), match.span(0)
|
||||
prefix, suffix = value[:begin], self._ExpandEnvVar(value[end:])
|
||||
return prefix + os.environ.get(name, '') + suffix
|
||||
|
||||
|
||||
class GnGenerator(object):
|
||||
'''Holds configuration for a build and method to generate gn default
|
||||
files.'''
|
||||
|
||||
FAT_BUILD_DEFAULT_ARCH = '64-bit'
|
||||
|
||||
TARGET_CPU_VALUES = {
|
||||
'iphoneos': {
|
||||
'32-bit': '"arm"',
|
||||
'64-bit': '"arm64"',
|
||||
},
|
||||
'iphonesimulator': {
|
||||
'32-bit': '"x86"',
|
||||
'64-bit': '"x64"',
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, settings, config, target):
|
||||
assert target in SUPPORTED_TARGETS
|
||||
assert config in SUPPORTED_CONFIGS
|
||||
self._settings = settings
|
||||
self._config = config
|
||||
self._target = target
|
||||
|
||||
def _GetGnArgs(self):
|
||||
"""Build the list of arguments to pass to gn.
|
||||
|
||||
Returns:
|
||||
A list of tuple containing gn variable names and variable values (it
|
||||
is not a dictionary as the order needs to be preserved).
|
||||
"""
|
||||
args = []
|
||||
|
||||
args.append(('is_debug', self._config in ('Debug', 'Coverage')))
|
||||
|
||||
if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1':
|
||||
args.append(('use_system_xcode', False))
|
||||
|
||||
cpu_values = self.TARGET_CPU_VALUES[self._target]
|
||||
build_arch = self._settings.getstring('build', 'arch')
|
||||
if build_arch == 'fat':
|
||||
target_cpu = cpu_values[self.FAT_BUILD_DEFAULT_ARCH]
|
||||
args.append(('target_cpu', target_cpu))
|
||||
args.append(
|
||||
('additional_target_cpus',
|
||||
[cpu for cpu in cpu_values.itervalues() if cpu != target_cpu]))
|
||||
else:
|
||||
args.append(('target_cpu', cpu_values[build_arch]))
|
||||
|
||||
# Add user overrides after the other configurations so that they can
|
||||
# refer to them and override them.
|
||||
args.extend(self._settings.items('gn_args'))
|
||||
return args
|
||||
|
||||
def Generate(self, gn_path, root_path, out_path):
|
||||
buf = StringIO.StringIO()
|
||||
self.WriteArgsGn(buf)
|
||||
WriteToFileIfChanged(os.path.join(out_path, 'args.gn'),
|
||||
buf.getvalue(),
|
||||
overwrite=True)
|
||||
|
||||
subprocess.check_call(
|
||||
self.GetGnCommand(gn_path, root_path, out_path, True))
|
||||
|
||||
def CreateGnRules(self, gn_path, root_path, out_path):
|
||||
buf = StringIO.StringIO()
|
||||
self.WriteArgsGn(buf)
|
||||
WriteToFileIfChanged(os.path.join(out_path, 'args.gn'),
|
||||
buf.getvalue(),
|
||||
overwrite=True)
|
||||
|
||||
buf = StringIO.StringIO()
|
||||
gn_command = self.GetGnCommand(gn_path, root_path, out_path, False)
|
||||
self.WriteBuildNinja(buf, gn_command)
|
||||
WriteToFileIfChanged(os.path.join(out_path, 'build.ninja'),
|
||||
buf.getvalue(),
|
||||
overwrite=False)
|
||||
|
||||
buf = StringIO.StringIO()
|
||||
self.WriteBuildNinjaDeps(buf)
|
||||
WriteToFileIfChanged(os.path.join(out_path, 'build.ninja.d'),
|
||||
buf.getvalue(),
|
||||
overwrite=False)
|
||||
|
||||
def WriteArgsGn(self, stream):
|
||||
stream.write('# This file was generated by setup-gn.py. Do not edit\n')
|
||||
stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n')
|
||||
stream.write('# to configure settings.\n')
|
||||
stream.write('\n')
|
||||
|
||||
if self._settings.has_section('$imports$'):
|
||||
for import_rule in self._settings.values('$imports$'):
|
||||
stream.write('import("%s")\n' % import_rule)
|
||||
stream.write('\n')
|
||||
|
||||
gn_args = self._GetGnArgs()
|
||||
for name, value in gn_args:
|
||||
if isinstance(value, bool):
|
||||
stream.write('%s = %s\n' % (name, str(value).lower()))
|
||||
elif isinstance(value, list):
|
||||
stream.write('%s = [%s' %
|
||||
(name, '\n' if len(value) > 1 else ''))
|
||||
if len(value) == 1:
|
||||
prefix = ' '
|
||||
suffix = ' '
|
||||
else:
|
||||
prefix = ' '
|
||||
suffix = ',\n'
|
||||
for item in value:
|
||||
if isinstance(item, bool):
|
||||
stream.write('%s%s%s' %
|
||||
(prefix, str(item).lower(), suffix))
|
||||
else:
|
||||
stream.write('%s%s%s' % (prefix, item, suffix))
|
||||
stream.write(']\n')
|
||||
else:
|
||||
stream.write('%s = %s\n' % (name, value))
|
||||
|
||||
def WriteBuildNinja(self, stream, gn_command):
|
||||
stream.write('rule gn\n')
|
||||
stream.write(' command = %s\n' % NinjaEscapeCommand(gn_command))
|
||||
stream.write(' description = Regenerating ninja files\n')
|
||||
stream.write('\n')
|
||||
stream.write('build build.ninja: gn\n')
|
||||
stream.write(' generator = 1\n')
|
||||
stream.write(' depfile = build.ninja.d\n')
|
||||
|
||||
def WriteBuildNinjaDeps(self, stream):
|
||||
stream.write('build.ninja: nonexistant_file.gn\n')
|
||||
|
||||
def GetGnCommand(self, gn_path, src_path, out_path, generate_xcode_project):
|
||||
gn_command = [gn_path, '--root=%s' % os.path.realpath(src_path), '-q']
|
||||
if generate_xcode_project:
|
||||
gn_command.append('--ide=xcode')
|
||||
gn_command.append('--root-target=gn_all')
|
||||
if self._settings.getboolean('goma', 'enabled'):
|
||||
ninja_jobs = self._settings.getint('xcode', 'jobs') or 200
|
||||
gn_command.append('--ninja-extra-args=-j%s' % ninja_jobs)
|
||||
if self._settings.has_section('filters'):
|
||||
target_filters = self._settings.values('filters')
|
||||
if target_filters:
|
||||
gn_command.append('--filters=%s' % ';'.join(target_filters))
|
||||
# TODO(justincohen): --check is currently failing in crashpad.
|
||||
# else:
|
||||
# gn_command.append('--check')
|
||||
gn_command.append('gen')
|
||||
gn_command.append('//%s' % os.path.relpath(os.path.abspath(out_path),
|
||||
os.path.abspath(src_path)))
|
||||
return gn_command
|
||||
|
||||
|
||||
def WriteToFileIfChanged(filename, content, overwrite):
|
||||
'''Write |content| to |filename| if different. If |overwrite| is False
|
||||
and the file already exists it is left untouched.'''
|
||||
if os.path.exists(filename):
|
||||
if not overwrite:
|
||||
return
|
||||
with open(filename) as file:
|
||||
if file.read() == content:
|
||||
return
|
||||
if not os.path.isdir(os.path.dirname(filename)):
|
||||
os.makedirs(os.path.dirname(filename))
|
||||
with open(filename, 'w') as file:
|
||||
file.write(content)
|
||||
|
||||
|
||||
def NinjaNeedEscape(arg):
|
||||
'''Returns True if |arg| needs to be escaped when written to .ninja file.'''
|
||||
return ':' in arg or '*' in arg or ';' in arg
|
||||
|
||||
|
||||
def NinjaEscapeCommand(command):
|
||||
'''Escapes |command| in order to write it to .ninja file.'''
|
||||
result = []
|
||||
for arg in command:
|
||||
if NinjaNeedEscape(arg):
|
||||
arg = arg.replace(':', '$:')
|
||||
arg = arg.replace(';', '\\;')
|
||||
arg = arg.replace('*', '\\*')
|
||||
else:
|
||||
result.append(arg)
|
||||
return ' '.join(result)
|
||||
|
||||
|
||||
def FindGn():
|
||||
'''Returns absolute path to gn binary looking at the PATH env variable.'''
|
||||
for path in os.environ['PATH'].split(os.path.pathsep):
|
||||
gn_path = os.path.join(path, 'gn')
|
||||
if os.path.isfile(gn_path) and os.access(gn_path, os.X_OK):
|
||||
return gn_path
|
||||
return None
|
||||
|
||||
|
||||
def GenerateXcodeProject(gn_path, root_dir, out_dir, settings):
|
||||
'''Convert GN generated Xcode project into multi-configuration Xcode
|
||||
project.'''
|
||||
|
||||
temp_path = tempfile.mkdtemp(
|
||||
prefix=os.path.abspath(os.path.join(out_dir, '_temp')))
|
||||
try:
|
||||
generator = GnGenerator(settings, 'Debug', 'iphonesimulator')
|
||||
generator.Generate(gn_path, root_dir, temp_path)
|
||||
convert_gn_xcodeproj.ConvertGnXcodeProject(
|
||||
root_dir, os.path.join(temp_path), os.path.join(out_dir, 'build'),
|
||||
SUPPORTED_CONFIGS)
|
||||
finally:
|
||||
if os.path.exists(temp_path):
|
||||
shutil.rmtree(temp_path)
|
||||
|
||||
|
||||
def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings):
|
||||
'''Generates all template configurations for gn.'''
|
||||
for config in SUPPORTED_CONFIGS:
|
||||
for target in SUPPORTED_TARGETS:
|
||||
build_dir = os.path.join(out_dir, '%s-%s' % (config, target))
|
||||
generator = GnGenerator(settings, config, target)
|
||||
generator.CreateGnRules(gn_path, root_dir, build_dir)
|
||||
|
||||
|
||||
def Main(args):
|
||||
default_root = os.path.normpath(
|
||||
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate build directories for use with gn.')
|
||||
parser.add_argument(
|
||||
'root',
|
||||
default=default_root,
|
||||
nargs='?',
|
||||
help='root directory where to generate multiple out configurations')
|
||||
parser.add_argument('--import',
|
||||
action='append',
|
||||
dest='import_rules',
|
||||
default=[],
|
||||
help='path to file defining default gn variables')
|
||||
args = parser.parse_args(args)
|
||||
|
||||
# Load configuration (first global and then any user overrides).
|
||||
settings = ConfigParserWithStringInterpolation()
|
||||
settings.read([
|
||||
os.path.splitext(__file__)[0] + '.config',
|
||||
os.path.expanduser('~/.setup-gn'),
|
||||
])
|
||||
|
||||
# Add private sections corresponding to --import argument.
|
||||
if args.import_rules:
|
||||
settings.add_section('$imports$')
|
||||
for i, import_rule in enumerate(args.import_rules):
|
||||
if not import_rule.startswith('//'):
|
||||
import_rule = '//%s' % os.path.relpath(
|
||||
os.path.abspath(import_rule), os.path.abspath(args.root))
|
||||
settings.set('$imports$', '$rule%d$' % i, import_rule)
|
||||
|
||||
# Validate settings.
|
||||
if settings.getstring('build', 'arch') not in ('64-bit', '32-bit', 'fat'):
|
||||
sys.stderr.write('ERROR: invalid value for build.arch: %s\n' %
|
||||
settings.getstring('build', 'arch'))
|
||||
sys.exit(1)
|
||||
|
||||
if settings.getboolean('goma', 'enabled'):
|
||||
if settings.getint('xcode', 'jobs') < 0:
|
||||
sys.stderr.write('ERROR: invalid value for xcode.jobs: %s\n' %
|
||||
settings.get('xcode', 'jobs'))
|
||||
sys.exit(1)
|
||||
goma_install = os.path.expanduser(settings.getstring('goma', 'install'))
|
||||
if not os.path.isdir(goma_install):
|
||||
sys.stderr.write('WARNING: goma.install directory not found: %s\n' %
|
||||
settings.get('goma', 'install'))
|
||||
sys.stderr.write('WARNING: disabling goma\n')
|
||||
settings.set('goma', 'enabled', 'false')
|
||||
|
||||
# Find gn binary in PATH.
|
||||
gn_path = FindGn()
|
||||
if gn_path is None:
|
||||
sys.stderr.write('ERROR: cannot find gn in PATH\n')
|
||||
sys.exit(1)
|
||||
|
||||
out_dir = os.path.join(args.root, 'out')
|
||||
if not os.path.isdir(out_dir):
|
||||
os.makedirs(out_dir)
|
||||
|
||||
GenerateXcodeProject(gn_path, args.root, out_dir, settings)
|
||||
GenerateGnBuildRules(gn_path, args.root, out_dir, settings)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(Main(sys.argv[1:]))
|
@ -13,7 +13,6 @@
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Helper script to [re]start or stop a helper Fuchsia QEMU instance to be used
|
||||
for running tests without a device.
|
||||
"""
|
||||
@ -30,105 +29,117 @@ import tempfile
|
||||
import time
|
||||
|
||||
try:
|
||||
from subprocess import DEVNULL
|
||||
from subprocess import DEVNULL
|
||||
except ImportError:
|
||||
DEVNULL = open(os.devnull, 'r+b')
|
||||
DEVNULL = open(os.devnull, 'r+b')
|
||||
|
||||
CRASHPAD_ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
os.pardir)
|
||||
|
||||
|
||||
def _Stop(pid_file):
|
||||
if os.path.isfile(pid_file):
|
||||
with open(pid_file, 'rb') as f:
|
||||
pid = int(f.read().strip())
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except:
|
||||
print('Unable to kill pid %d, continuing' % pid, file=sys.stderr)
|
||||
os.unlink(pid_file)
|
||||
if os.path.isfile(pid_file):
|
||||
with open(pid_file, 'rb') as f:
|
||||
pid = int(f.read().strip())
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
except:
|
||||
print('Unable to kill pid %d, continuing' % pid, file=sys.stderr)
|
||||
os.unlink(pid_file)
|
||||
|
||||
|
||||
def _CheckForTun():
|
||||
"""Check for networking. TODO(scottmg): Currently, this is Linux-specific.
|
||||
"""
|
||||
returncode = subprocess.call(
|
||||
['tunctl', '-b', '-u', getpass.getuser(), '-t', 'qemu'],
|
||||
stdout=DEVNULL, stderr=DEVNULL)
|
||||
if returncode != 0:
|
||||
print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
|
||||
file=sys.stderr)
|
||||
print(' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only',
|
||||
file=sys.stderr)
|
||||
return 2
|
||||
return 0
|
||||
"""Check for networking. TODO(scottmg): Currently, this is Linux-specific.
|
||||
"""
|
||||
returncode = subprocess.call(
|
||||
['tunctl', '-b', '-u',
|
||||
getpass.getuser(), '-t', 'qemu'],
|
||||
stdout=DEVNULL,
|
||||
stderr=DEVNULL)
|
||||
if returncode != 0:
|
||||
print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
|
||||
file=sys.stderr)
|
||||
print(
|
||||
' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only',
|
||||
file=sys.stderr)
|
||||
return 2
|
||||
return 0
|
||||
|
||||
|
||||
def _Start(pid_file):
|
||||
tun_result = _CheckForTun()
|
||||
if tun_result != 0:
|
||||
return tun_result
|
||||
tun_result = _CheckForTun()
|
||||
if tun_result != 0:
|
||||
return tun_result
|
||||
|
||||
arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
|
||||
fuchsia_dir = os.path.join(CRASHPAD_ROOT, 'third_party', 'fuchsia')
|
||||
qemu_path = os.path.join(fuchsia_dir, 'qemu', arch, 'bin',
|
||||
'qemu-system-x86_64')
|
||||
kernel_data_dir = os.path.join(fuchsia_dir, 'sdk', arch, 'target', 'x86_64')
|
||||
kernel_path = os.path.join(kernel_data_dir, 'zircon.bin')
|
||||
initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
|
||||
arch = 'mac-amd64' if sys.platform == 'darwin' else 'linux-amd64'
|
||||
fuchsia_dir = os.path.join(CRASHPAD_ROOT, 'third_party', 'fuchsia')
|
||||
qemu_path = os.path.join(fuchsia_dir, 'qemu', arch, 'bin',
|
||||
'qemu-system-x86_64')
|
||||
kernel_data_dir = os.path.join(fuchsia_dir, 'sdk', arch, 'target', 'x86_64')
|
||||
kernel_path = os.path.join(kernel_data_dir, 'zircon.bin')
|
||||
initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
|
||||
|
||||
mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
|
||||
instance_name = 'crashpad_qemu_' + \
|
||||
''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8))
|
||||
mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
|
||||
instance_name = (
|
||||
'crashpad_qemu_' +
|
||||
''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8)))
|
||||
|
||||
# These arguments are from the Fuchsia repo in zircon/scripts/run-zircon.
|
||||
popen = subprocess.Popen([
|
||||
qemu_path,
|
||||
'-m', '2048',
|
||||
'-nographic',
|
||||
'-kernel', kernel_path,
|
||||
'-initrd', initrd_path,
|
||||
'-smp', '4',
|
||||
'-serial', 'stdio',
|
||||
'-monitor', 'none',
|
||||
'-machine', 'q35',
|
||||
'-cpu', 'host,migratable=no',
|
||||
'-enable-kvm',
|
||||
'-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
|
||||
'-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
|
||||
'-append', 'TERM=dumb zircon.nodename=' + instance_name,
|
||||
], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL)
|
||||
# These arguments are from the Fuchsia repo in zircon/scripts/run-zircon.
|
||||
|
||||
with open(pid_file, 'wb') as f:
|
||||
f.write('%d\n' % popen.pid)
|
||||
# yapf: disable
|
||||
popen = subprocess.Popen([
|
||||
qemu_path,
|
||||
'-m', '2048',
|
||||
'-nographic',
|
||||
'-kernel', kernel_path,
|
||||
'-initrd', initrd_path,
|
||||
'-smp', '4',
|
||||
'-serial', 'stdio',
|
||||
'-monitor', 'none',
|
||||
'-machine', 'q35',
|
||||
'-cpu', 'host,migratable=no',
|
||||
'-enable-kvm',
|
||||
'-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
|
||||
'-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
|
||||
'-append', 'TERM=dumb zircon.nodename=' + instance_name,
|
||||
],
|
||||
stdin=DEVNULL,
|
||||
stdout=DEVNULL,
|
||||
stderr=DEVNULL)
|
||||
# yapf: enable
|
||||
|
||||
for i in range(10):
|
||||
netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools', 'netaddr')
|
||||
if subprocess.call([netaddr_path, '--nowait', instance_name],
|
||||
stdout=open(os.devnull), stderr=open(os.devnull)) == 0:
|
||||
break
|
||||
time.sleep(.5)
|
||||
else:
|
||||
print('instance did not respond after start', file=sys.stderr)
|
||||
return 1
|
||||
with open(pid_file, 'wb') as f:
|
||||
f.write('%d\n' % popen.pid)
|
||||
|
||||
return 0
|
||||
for i in range(10):
|
||||
netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools',
|
||||
'netaddr')
|
||||
if subprocess.call([netaddr_path, '--nowait', instance_name],
|
||||
stdout=open(os.devnull),
|
||||
stderr=open(os.devnull)) == 0:
|
||||
break
|
||||
time.sleep(.5)
|
||||
else:
|
||||
print('instance did not respond after start', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def main(args):
|
||||
if len(args) != 1 or args[0] not in ('start', 'stop'):
|
||||
print('usage: run_fuchsia_qemu.py start|stop', file=sys.stderr)
|
||||
return 1
|
||||
if len(args) != 1 or args[0] not in ('start', 'stop'):
|
||||
print('usage: run_fuchsia_qemu.py start|stop', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
command = args[0]
|
||||
command = args[0]
|
||||
|
||||
pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid')
|
||||
_Stop(pid_file)
|
||||
if command == 'start':
|
||||
return _Start(pid_file)
|
||||
pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid')
|
||||
_Stop(pid_file)
|
||||
if command == 'start':
|
||||
return _Start(pid_file)
|
||||
|
||||
return 0
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -18,9 +18,32 @@ if (crashpad_is_in_chromium) {
|
||||
import("//testing/test.gni")
|
||||
} else {
|
||||
template("test") {
|
||||
executable(target_name) {
|
||||
testonly = true
|
||||
forward_variables_from(invoker, "*")
|
||||
if (crashpad_is_ios) {
|
||||
import("//third_party/mini_chromium/mini_chromium/build/ios/rules.gni")
|
||||
|
||||
_launch_image_bundle_target = target_name + "_launch_image"
|
||||
bundle_data(_launch_image_bundle_target) {
|
||||
forward_variables_from(invoker, [ "testonly" ])
|
||||
sources = [ "//build/ios/Default.png" ]
|
||||
outputs = [ "{{bundle_contents_dir}}/{{source_file_part}}" ]
|
||||
}
|
||||
|
||||
ios_xctest_test(target_name) {
|
||||
testonly = true
|
||||
xctest_module_target = "//test/ios:google_test_runner"
|
||||
info_plist = "//build/ios/Unittest-Info.plist"
|
||||
extra_substitutions = [ "GTEST_BUNDLE_ID_SUFFIX=$target_name" ]
|
||||
forward_variables_from(invoker, "*")
|
||||
if (!defined(deps)) {
|
||||
deps = []
|
||||
}
|
||||
deps += [ ":$_launch_image_bundle_target" ]
|
||||
}
|
||||
} else {
|
||||
executable(target_name) {
|
||||
testonly = true
|
||||
forward_variables_from(invoker, "*")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,13 @@ static_library("client") {
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_ios) {
|
||||
sources += [
|
||||
"crash_report_database_mac.mm",
|
||||
"crashpad_client_ios.cc",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
set_sources_assignment_filter([])
|
||||
sources += [
|
||||
@ -77,22 +84,36 @@ static_library("client") {
|
||||
|
||||
public_configs = [ "..:crashpad_config" ]
|
||||
|
||||
deps = [
|
||||
"../compat",
|
||||
public_deps = [
|
||||
"../third_party/mini_chromium:base",
|
||||
"../util",
|
||||
]
|
||||
|
||||
deps = []
|
||||
|
||||
if (crashpad_is_win) {
|
||||
libs = [ "rpcrt4.lib" ]
|
||||
cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
|
||||
}
|
||||
|
||||
if (crashpad_is_fuchsia && crashpad_is_in_fuchsia) {
|
||||
# TODO(justincohen): Temporary dependency to bring up the iOS client.
|
||||
if (crashpad_is_ios) {
|
||||
deps += [
|
||||
"//zircon/public/lib/fdio",
|
||||
"../minidump",
|
||||
"../snapshot",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
deps += [ "../third_party/lss" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_fuchsia) {
|
||||
deps += [ "../third_party/fuchsia" ]
|
||||
if (crashpad_is_in_fuchsia) {
|
||||
deps += [ "//zircon/public/lib/fdio" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source_set("client_test") {
|
||||
@ -116,6 +137,17 @@ source_set("client_test") {
|
||||
sources += [ "crashpad_client_win_test.cc" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_ios) {
|
||||
sources += [ "crashpad_client_ios_test.mm" ]
|
||||
sources -= [
|
||||
"annotation_list_test.cc",
|
||||
"annotation_test.cc",
|
||||
"crash_report_database_test.cc",
|
||||
"prune_crash_reports_test.cc",
|
||||
"settings_test.cc",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
sources += [ "crashpad_client_linux_test.cc" ]
|
||||
}
|
||||
@ -131,9 +163,9 @@ source_set("client_test") {
|
||||
"../util",
|
||||
]
|
||||
|
||||
data_deps = [
|
||||
"../handler:crashpad_handler",
|
||||
]
|
||||
if (!crashpad_is_ios && !crashpad_is_fuchsia) {
|
||||
data_deps = [ "../handler:crashpad_handler" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_win) {
|
||||
data_deps += [ "../handler:crashpad_handler_console" ]
|
||||
|
@ -23,6 +23,7 @@
|
||||
'dependencies': [
|
||||
'../compat/compat.gyp:crashpad_compat',
|
||||
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||
'../third_party/lss/lss.gyp:lss',
|
||||
'../util/util.gyp:crashpad_util',
|
||||
],
|
||||
'include_dirs': [
|
||||
|
@ -61,14 +61,14 @@ std::vector<std::string> BuildHandlerArgvStrings(
|
||||
return argv_strings;
|
||||
}
|
||||
|
||||
void ConvertArgvStrings(const std::vector<std::string>& argv_strings,
|
||||
std::vector<const char*>* argv) {
|
||||
argv->clear();
|
||||
argv->reserve(argv_strings.size() + 1);
|
||||
for (const auto& arg : argv_strings) {
|
||||
argv->push_back(arg.c_str());
|
||||
void StringVectorToCStringVector(const std::vector<std::string>& strings,
|
||||
std::vector<const char*>* c_strings) {
|
||||
c_strings->clear();
|
||||
c_strings->reserve(strings.size() + 1);
|
||||
for (const auto& str : strings) {
|
||||
c_strings->push_back(str.c_str());
|
||||
}
|
||||
argv->push_back(nullptr);
|
||||
c_strings->push_back(nullptr);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -40,11 +40,11 @@ std::vector<std::string> BuildHandlerArgvStrings(
|
||||
//! \brief Flattens a string vector into a const char* vector suitable for use
|
||||
//! in an exec() call.
|
||||
//!
|
||||
//! \param[in] argv_strings Arguments to be passed to child process, typically
|
||||
//! created by BuildHandlerArgvStrings().
|
||||
//! \param[out] argv argv suitable for starting the child process.
|
||||
void ConvertArgvStrings(const std::vector<std::string>& argv_strings,
|
||||
std::vector<const char*>* argv);
|
||||
//! \param[in] strings A vector of string data. This vector must remain valid
|
||||
//! for the lifetime of \a c_strings.
|
||||
//! \param[out] c_strings A vector of pointers to the string data in \a strings.
|
||||
void StringVectorToCStringVector(const std::vector<std::string>& strings,
|
||||
std::vector<const char*>* c_strings);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
|
@ -27,7 +27,8 @@ CrashReportDatabase::Report::Report()
|
||||
uploaded(false),
|
||||
last_upload_attempt_time(0),
|
||||
upload_attempts(0),
|
||||
upload_explicitly_requested(false) {}
|
||||
upload_explicitly_requested(false),
|
||||
total_size(0u) {}
|
||||
|
||||
CrashReportDatabase::NewReport::NewReport()
|
||||
: writer_(std::make_unique<FileWriter>()),
|
||||
@ -64,6 +65,15 @@ bool CrashReportDatabase::NewReport::Initialize(
|
||||
return true;
|
||||
}
|
||||
|
||||
FileReaderInterface* CrashReportDatabase::NewReport::Reader() {
|
||||
auto reader = std::make_unique<FileReader>();
|
||||
if (!reader->Open(file_remover_.get())) {
|
||||
return nullptr;
|
||||
}
|
||||
reader_ = std::move(reader);
|
||||
return reader_.get();
|
||||
}
|
||||
|
||||
CrashReportDatabase::UploadReport::UploadReport()
|
||||
: Report(),
|
||||
reader_(std::make_unique<FileReader>()),
|
||||
|
@ -15,6 +15,7 @@
|
||||
#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
|
||||
#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <map>
|
||||
@ -98,6 +99,10 @@ class CrashReportDatabase {
|
||||
//! Whether this crash report was explicitly requested by user to be
|
||||
//! uploaded. This can be true only if report is in the 'pending' state.
|
||||
bool upload_explicitly_requested;
|
||||
|
||||
//! The total size in bytes taken by the report, including any potential
|
||||
//! attachments.
|
||||
uint64_t total_size;
|
||||
};
|
||||
|
||||
//! \brief A crash report that is in the process of being written.
|
||||
@ -108,9 +113,13 @@ class CrashReportDatabase {
|
||||
NewReport();
|
||||
~NewReport();
|
||||
|
||||
//! An open FileWriter with which to write the report.
|
||||
//! \brief An open FileWriter with which to write the report.
|
||||
FileWriter* Writer() const { return writer_.get(); }
|
||||
|
||||
//! \brief Returns a FileReaderInterface to the report, or `nullptr` with a
|
||||
//! message logged.
|
||||
FileReaderInterface* Reader();
|
||||
|
||||
//! A unique identifier by which this report will always be known to the
|
||||
//! database.
|
||||
const UUID& ReportID() const { return uuid_; }
|
||||
@ -137,6 +146,7 @@ class CrashReportDatabase {
|
||||
const base::FilePath::StringType& extension);
|
||||
|
||||
std::unique_ptr<FileWriter> writer_;
|
||||
std::unique_ptr<FileReader> reader_;
|
||||
ScopedRemoveFile file_remover_;
|
||||
std::vector<std::unique_ptr<FileWriter>> attachment_writers_;
|
||||
std::vector<ScopedRemoveFile> attachment_removers_;
|
||||
@ -163,7 +173,7 @@ class CrashReportDatabase {
|
||||
//! This is not implemented on macOS or Windows.
|
||||
std::map<std::string, FileReader*> GetAttachments() const {
|
||||
return attachment_map_;
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
friend class CrashReportDatabase;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "client/crash_report_database.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <utility>
|
||||
@ -25,6 +26,7 @@
|
||||
#include "util/file/directory_reader.h"
|
||||
#include "util/file/filesystem.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/misc/memory_sanitizer.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -160,6 +162,34 @@ class ScopedLockFile {
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedLockFile);
|
||||
};
|
||||
|
||||
off_t GetFileSize(const base::FilePath& filepath) {
|
||||
struct stat statbuf;
|
||||
if (stat(filepath.value().c_str(), &statbuf) == 0) {
|
||||
return statbuf.st_size;
|
||||
}
|
||||
PLOG(ERROR) << "stat " << filepath.value();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AddAttachmentSize(const base::FilePath& attachments_dir, uint64_t* size) {
|
||||
// Early return if the attachment directory does not exist.
|
||||
if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
|
||||
return;
|
||||
}
|
||||
DirectoryReader reader;
|
||||
if (!reader.Open(attachments_dir)) {
|
||||
return;
|
||||
}
|
||||
base::FilePath attachment_filename;
|
||||
DirectoryReader::Result result;
|
||||
while ((result = reader.NextFile(&attachment_filename)) ==
|
||||
DirectoryReader::Result::kSuccess) {
|
||||
const base::FilePath attachment_filepath(
|
||||
attachments_dir.Append(attachment_filename));
|
||||
*size += GetFileSize(attachment_filepath);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class CrashReportDatabaseGeneric : public CrashReportDatabase {
|
||||
@ -253,7 +283,7 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase {
|
||||
void RemoveAttachmentsByUUID(const UUID& uuid);
|
||||
|
||||
// Reads the metadata for a report from path and returns it in report.
|
||||
static bool ReadMetadata(const base::FilePath& path, Report* report);
|
||||
bool ReadMetadata(const base::FilePath& path, Report* report);
|
||||
|
||||
// Wraps ReadMetadata and removes the report from the database on failure.
|
||||
bool CleaningReadMetadata(const base::FilePath& path, Report* report);
|
||||
@ -303,6 +333,9 @@ void CrashReportDatabase::UploadReport::InitializeAttachments() {
|
||||
base::FilePath attachments_dir =
|
||||
static_cast<CrashReportDatabaseGeneric*>(database_)->AttachmentsPath(
|
||||
uuid);
|
||||
if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
|
||||
return;
|
||||
}
|
||||
DirectoryReader reader;
|
||||
if (!reader.Open(attachments_dir)) {
|
||||
return;
|
||||
@ -842,7 +875,6 @@ void CrashReportDatabaseGeneric::CleanOrphanedAttachments() {
|
||||
base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory));
|
||||
DirectoryReader reader;
|
||||
if (!reader.Open(root_attachments_dir)) {
|
||||
LOG(ERROR) << "no attachments dir";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -883,6 +915,9 @@ void CrashReportDatabaseGeneric::CleanOrphanedAttachments() {
|
||||
|
||||
void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) {
|
||||
base::FilePath attachments_dir = AttachmentsPath(uuid);
|
||||
if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
|
||||
return;
|
||||
}
|
||||
DirectoryReader reader;
|
||||
if (!reader.Open(attachments_dir)) {
|
||||
return;
|
||||
@ -899,7 +934,6 @@ void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) {
|
||||
LoggingRemoveDirectory(attachments_dir);
|
||||
}
|
||||
|
||||
// static
|
||||
bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
||||
Report* report) {
|
||||
const base::FilePath metadata_path(
|
||||
@ -910,7 +944,8 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!report->uuid.InitializeFromString(
|
||||
UUID uuid;
|
||||
if (!uuid.InitializeFromString(
|
||||
path.BaseName().RemoveFinalExtension().value())) {
|
||||
LOG(ERROR) << "Couldn't interpret report uuid";
|
||||
return false;
|
||||
@ -930,6 +965,12 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Seed the total size with the main report size and then add the sizes of any
|
||||
// potential attachments.
|
||||
uint64_t total_size = GetFileSize(path);
|
||||
AddAttachmentSize(AttachmentsPath(uuid), &total_size);
|
||||
|
||||
report->uuid = uuid;
|
||||
report->upload_attempts = metadata.upload_attempts;
|
||||
report->last_upload_attempt_time = metadata.last_upload_attempt_time;
|
||||
report->creation_time = metadata.creation_time;
|
||||
@ -937,6 +978,7 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
||||
report->upload_explicitly_requested =
|
||||
(metadata.attributes & kAttributeUploadExplicitlyRequested) != 0;
|
||||
report->file_path = path;
|
||||
report->total_size = total_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -966,6 +1008,11 @@ bool CrashReportDatabaseGeneric::WriteNewMetadata(const base::FilePath& path) {
|
||||
}
|
||||
|
||||
ReportMetadata metadata;
|
||||
#if defined(MEMORY_SANITIZER)
|
||||
// memset() + re-initialization is required to zero padding bytes for MSan.
|
||||
memset(&metadata, 0, sizeof(metadata));
|
||||
#endif // defined(MEMORY_SANITIZER)
|
||||
metadata = {};
|
||||
metadata.creation_time = time(nullptr);
|
||||
|
||||
return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata));
|
||||
@ -986,6 +1033,11 @@ bool CrashReportDatabaseGeneric::WriteMetadata(const base::FilePath& path,
|
||||
}
|
||||
|
||||
ReportMetadata metadata;
|
||||
#if defined(MEMORY_SANITIZER)
|
||||
// memset() + re-initialization is required to zero padding bytes for MSan.
|
||||
memset(&metadata, 0, sizeof(metadata));
|
||||
#endif // defined(MEMORY_SANITIZER)
|
||||
metadata = {};
|
||||
metadata.creation_time = report.creation_time;
|
||||
metadata.last_upload_attempt_time = report.last_upload_attempt_time;
|
||||
metadata.upload_attempts = report.upload_attempts;
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/scoped_generic.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/string_piece.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
@ -279,7 +280,7 @@ bool CrashReportDatabaseMac::Initialize(bool may_create) {
|
||||
}
|
||||
|
||||
// Create the three processing directories for the database.
|
||||
for (size_t i = 0; i < arraysize(kReportDirectories); ++i) {
|
||||
for (size_t i = 0; i < base::size(kReportDirectories); ++i) {
|
||||
if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i])))
|
||||
return false;
|
||||
}
|
||||
@ -681,6 +682,14 @@ bool CrashReportDatabaseMac::ReadReportMetadataLocked(
|
||||
return false;
|
||||
}
|
||||
|
||||
// There are no attachments on Mac so the total size is the main report size.
|
||||
struct stat statbuf;
|
||||
if (stat(path.value().c_str(), &statbuf) != 0) {
|
||||
PLOG(ERROR) << "stat " << path.value();
|
||||
return false;
|
||||
}
|
||||
report->total_size = statbuf.st_size;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "test/errors.h"
|
||||
#include "test/file.h"
|
||||
#include "test/filesystem.h"
|
||||
#include "test/gtest_disabled.h"
|
||||
#include "test/scoped_temp_dir.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/file/filesystem.h"
|
||||
@ -31,8 +30,7 @@ namespace {
|
||||
|
||||
class CrashReportDatabaseTest : public testing::Test {
|
||||
public:
|
||||
CrashReportDatabaseTest() {
|
||||
}
|
||||
CrashReportDatabaseTest() {}
|
||||
|
||||
protected:
|
||||
// testing::Test:
|
||||
@ -41,9 +39,7 @@ class CrashReportDatabaseTest : public testing::Test {
|
||||
ASSERT_TRUE(db_);
|
||||
}
|
||||
|
||||
void ResetDatabase() {
|
||||
db_.reset();
|
||||
}
|
||||
void ResetDatabase() { db_.reset(); }
|
||||
|
||||
CrashReportDatabase* db() { return db_.get(); }
|
||||
base::FilePath path() const {
|
||||
@ -57,6 +53,12 @@ class CrashReportDatabaseTest : public testing::Test {
|
||||
static constexpr char kTest[] = "test";
|
||||
ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest)));
|
||||
|
||||
char contents[sizeof(kTest)];
|
||||
FileReaderInterface* reader = new_report->Reader();
|
||||
ASSERT_TRUE(reader->ReadExactly(contents, sizeof(contents)));
|
||||
EXPECT_EQ(memcmp(contents, kTest, sizeof(contents)), 0);
|
||||
EXPECT_EQ(reader->ReadExactly(contents, 1), 0);
|
||||
|
||||
UUID uuid;
|
||||
EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid),
|
||||
CrashReportDatabase::kNoError);
|
||||
@ -101,6 +103,7 @@ class CrashReportDatabaseTest : public testing::Test {
|
||||
EXPECT_EQ(report.last_upload_attempt_time, 0);
|
||||
EXPECT_EQ(report.upload_attempts, 0);
|
||||
EXPECT_FALSE(report.upload_explicitly_requested);
|
||||
EXPECT_GE(report.total_size, 0u);
|
||||
}
|
||||
|
||||
void RelocateDatabase() {
|
||||
@ -673,7 +676,7 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) {
|
||||
TEST_F(CrashReportDatabaseTest, Attachments) {
|
||||
#if defined(OS_MACOSX) || defined(OS_WIN)
|
||||
// Attachments aren't supported on Mac and Windows yet.
|
||||
DISABLED_TEST();
|
||||
GTEST_SKIP();
|
||||
#else
|
||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
|
||||
@ -719,7 +722,7 @@ TEST_F(CrashReportDatabaseTest, Attachments) {
|
||||
TEST_F(CrashReportDatabaseTest, OrphanedAttachments) {
|
||||
#if defined(OS_MACOSX) || defined(OS_WIN)
|
||||
// Attachments aren't supported on Mac and Windows yet.
|
||||
DISABLED_TEST();
|
||||
GTEST_SKIP();
|
||||
#else
|
||||
// TODO: This is using paths that are specific to the generic implementation
|
||||
// and will need to be generalized for Mac and Windows.
|
||||
@ -835,6 +838,66 @@ TEST_F(CrashReportDatabaseTest, CleanBrokenDatabase) {
|
||||
}
|
||||
#endif // !OS_MACOSX && !OS_WIN
|
||||
|
||||
TEST_F(CrashReportDatabaseTest, TotalSize_MainReportOnly) {
|
||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
|
||||
CrashReportDatabase::kNoError);
|
||||
|
||||
// Main report.
|
||||
static constexpr char main_report_data[] = "dlbvandslhb";
|
||||
ASSERT_TRUE(
|
||||
new_report->Writer()->Write(main_report_data, sizeof(main_report_data)));
|
||||
|
||||
UUID uuid;
|
||||
ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),
|
||||
CrashReportDatabase::kNoError);
|
||||
|
||||
CrashReportDatabase::Report report;
|
||||
ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),
|
||||
CrashReportDatabase::kNoError);
|
||||
|
||||
EXPECT_EQ(report.total_size, sizeof(main_report_data));
|
||||
}
|
||||
|
||||
TEST_F(CrashReportDatabaseTest, GetReportSize_RightSizeWithAttachments) {
|
||||
#if defined(OS_MACOSX) || defined(OS_WIN)
|
||||
// Attachments aren't supported on Mac and Windows yet.
|
||||
return;
|
||||
#else
|
||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
|
||||
CrashReportDatabase::kNoError);
|
||||
|
||||
// Main report.
|
||||
static constexpr char main_report_data[] = "dlbvandslhb";
|
||||
ASSERT_TRUE(
|
||||
new_report->Writer()->Write(main_report_data, sizeof(main_report_data)));
|
||||
|
||||
// First attachment.
|
||||
FileWriter* attachment_1 = new_report->AddAttachment("my_attachment_1");
|
||||
ASSERT_NE(attachment_1, nullptr);
|
||||
static constexpr char attachment_1_data[] = "vKDnidhvbiudshoihbvdsoiuh nhh";
|
||||
attachment_1->Write(attachment_1_data, sizeof(attachment_1_data));
|
||||
|
||||
// Second attachment.
|
||||
FileWriter* attachment_2 = new_report->AddAttachment("my_attachment_2");
|
||||
ASSERT_NE(attachment_2, nullptr);
|
||||
static constexpr char attachment_2_data[] = "sgvsvgusiyguysigfkhpmo-[";
|
||||
attachment_2->Write(attachment_2_data, sizeof(attachment_2_data));
|
||||
|
||||
UUID uuid;
|
||||
ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),
|
||||
CrashReportDatabase::kNoError);
|
||||
|
||||
CrashReportDatabase::Report report;
|
||||
ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),
|
||||
CrashReportDatabase::kNoError);
|
||||
EXPECT_EQ(report.total_size,
|
||||
sizeof(main_report_data) + sizeof(attachment_1_data) +
|
||||
sizeof(attachment_2_data));
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
@ -458,7 +458,18 @@ void Metadata::Read() {
|
||||
LOG(ERROR) << "invalid string table index";
|
||||
return;
|
||||
}
|
||||
reports.push_back(ReportDisk(record, report_dir_, string_table));
|
||||
ReportDisk report_disk(record, report_dir_, string_table);
|
||||
|
||||
// There are no attachments on Windows so the total size is the main
|
||||
// report size.
|
||||
struct _stati64 statbuf;
|
||||
if (_wstat64(report_disk.file_path.value().c_str(), &statbuf) == 0) {
|
||||
report_disk.total_size = statbuf.st_size;
|
||||
} else {
|
||||
LOG(ERROR) << "failed to stat report";
|
||||
}
|
||||
|
||||
reports.push_back(report_disk);
|
||||
}
|
||||
}
|
||||
reports_.swap(reports);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -24,6 +25,7 @@
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "build/build_config.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/misc/capture_context.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
@ -78,6 +80,10 @@ class CrashpadClient {
|
||||
//! On Fuchsia, this method binds to the exception port of the current default
|
||||
//! job, and starts a Crashpad handler to monitor that port.
|
||||
//!
|
||||
//! On Linux, this method starts a Crashpad handler, connected to this process
|
||||
//! via an `AF_UNIX` socket pair and installs signal handlers to request crash
|
||||
//! dumps on the client's socket end.
|
||||
//!
|
||||
//! \param[in] handler The path to a Crashpad handler executable.
|
||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||
//! started with this path as its `--database` argument.
|
||||
@ -112,6 +118,199 @@ class CrashpadClient {
|
||||
bool restartable,
|
||||
bool asynchronous_start);
|
||||
|
||||
#if defined(OS_ANDROID) || defined(OS_LINUX) || DOXYGEN
|
||||
//! \brief Retrieve the socket and process ID for the handler.
|
||||
//!
|
||||
//! `StartHandler()` must have successfully been called before calling this
|
||||
//! method.
|
||||
//!
|
||||
//! \param[out] sock The socket connected to the handler, if not `nullptr`.
|
||||
//! \param[out] pid The handler's process ID, if not `nullptr`.
|
||||
//! \return `true` on success. Otherwise `false` with a message logged.
|
||||
static bool GetHandlerSocket(int* sock, pid_t* pid);
|
||||
|
||||
//! \brief Sets the socket to a presumably-running Crashpad handler process
|
||||
//! which was started with StartHandler().
|
||||
//!
|
||||
//! This method installs a signal handler to request crash dumps on \a sock.
|
||||
//!
|
||||
//! \param[in] sock A socket connected to a Crashpad handler.
|
||||
//! \param[in] pid The process ID of the handler, used to set the handler as
|
||||
//! this process' ptracer. 0 indicates it is not necessary to set the
|
||||
//! handler as this process' ptracer. -1 indicates that the handler's
|
||||
//! process ID should be determined by communicating over the socket.
|
||||
bool SetHandlerSocket(ScopedFileHandle sock, pid_t pid);
|
||||
#endif // OS_ANDROID || OS_LINUX || DOXYGEN
|
||||
|
||||
#if defined(OS_ANDROID) || DOXYGEN
|
||||
//! \brief Installs a signal handler to execute `/system/bin/app_process` and
|
||||
//! load a Java class in response to a crash.
|
||||
//!
|
||||
//! \param[in] class_name The fully qualified class name to load, which must
|
||||
//! define a `main()` method to be invoked by `app_process`. Arguments
|
||||
//! will be passed to this method as though it were the Crashpad handler.
|
||||
//! This class is expected to load a native library defining
|
||||
//! crashpad::HandlerMain() and pass the arguments to it.
|
||||
//! \param[in] env A vector of environment variables of the form `var=value`
|
||||
//! defining the environment in which to execute `app_process`. If this
|
||||
//! value is `nullptr`, the application's environment at the time of the
|
||||
//! crash will be used.
|
||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||
//! started with this path as its `--database` argument.
|
||||
//! \param[in] metrics_dir The path to an already existing directory where
|
||||
//! metrics files can be stored. The handler will be started with this
|
||||
//! path as its `--metrics-dir` argument.
|
||||
//! \param[in] url The URL of an upload server. The handler will be started
|
||||
//! with this URL as its `--url` argument.
|
||||
//! \param[in] annotations Process annotations to set in each crash report.
|
||||
//! The handler will be started with an `--annotation` argument for each
|
||||
//! element in this map.
|
||||
//! \param[in] arguments Additional arguments to pass to the Crashpad handler.
|
||||
//! Arguments passed in other parameters and arguments required to perform
|
||||
//! the handshake are the responsibility of this method, and must not be
|
||||
//! specified in this parameter.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool StartJavaHandlerAtCrash(
|
||||
const std::string& class_name,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments);
|
||||
|
||||
//! \brief Executes `/system/bin/app_process` and loads a Java class.
|
||||
//!
|
||||
//! \param[in] class_name The fully qualified class name to load, which must
|
||||
//! define a `main()` method to be invoked by `app_process`. Arguments
|
||||
//! will be passed to this method as though it were the Crashpad handler.
|
||||
//! This class is expected to load a native library defining
|
||||
//! crashpad::HandlerMain() and pass the arguments to it.
|
||||
//! \param[in] env A vector of environment variables of the form `var=value`
|
||||
//! defining the environment in which to execute `app_process`. If this
|
||||
//! value is `nullptr`, the application's current environment will be
|
||||
//! used.
|
||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||
//! started with this path as its `--database` argument.
|
||||
//! \param[in] metrics_dir The path to an already existing directory where
|
||||
//! metrics files can be stored. The handler will be started with this
|
||||
//! path as its `--metrics-dir` argument.
|
||||
//! \param[in] url The URL of an upload server. The handler will be started
|
||||
//! with this URL as its `--url` argument.
|
||||
//! \param[in] annotations Process annotations to set in each crash report.
|
||||
//! The handler will be started with an `--annotation` argument for each
|
||||
//! element in this map.
|
||||
//! \param[in] arguments Additional arguments to pass to the Crashpad handler.
|
||||
//! Arguments passed in other parameters and arguments required to perform
|
||||
//! the handshake are the responsibility of this method, and must not be
|
||||
//! specified in this parameter.
|
||||
//! \param[in] socket The server end of a socket pair. The client end should
|
||||
//! be used with an ExceptionHandlerClient.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
static bool StartJavaHandlerForClient(
|
||||
const std::string& class_name,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
int socket);
|
||||
|
||||
//! \brief Installs a signal handler to start a Crashpad handler process by
|
||||
//! loading it with `/system/bin/linker`.
|
||||
//!
|
||||
//! This method is only supported by Android Q+.
|
||||
//!
|
||||
//! \param[in] handler_trampoline The path to a Crashpad handler trampoline
|
||||
//! executable, possibly located within an apk, e.g.
|
||||
//! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so".
|
||||
//! \param[in] handler_library The name of a library exporting the symbol
|
||||
//! `CrashpadHandlerMain()`. The path to this library must be present in
|
||||
//! `LD_LIBRARY_PATH`.
|
||||
//! \param[in] is_64_bit `true` if \a handler_trampoline and \a
|
||||
//! handler_library are 64-bit objects. They must have the same bitness.
|
||||
//! \param[in] env A vector of environment variables of the form `var=value`
|
||||
//! defining the environment in which to execute `app_process`. If this
|
||||
//! value is `nullptr`, the application's environment at the time of the
|
||||
//! crash will be used.
|
||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||
//! started with this path as its `--database` argument.
|
||||
//! \param[in] metrics_dir The path to an already existing directory where
|
||||
//! metrics files can be stored. The handler will be started with this
|
||||
//! path as its `--metrics-dir` argument.
|
||||
//! \param[in] url The URL of an upload server. The handler will be started
|
||||
//! with this URL as its `--url` argument.
|
||||
//! \param[in] annotations Process annotations to set in each crash report.
|
||||
//! The handler will be started with an `--annotation` argument for each
|
||||
//! element in this map.
|
||||
//! \param[in] arguments Additional arguments to pass to the Crashpad handler.
|
||||
//! Arguments passed in other parameters and arguments required to perform
|
||||
//! the handshake are the responsibility of this method, and must not be
|
||||
//! specified in this parameter.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
bool StartHandlerWithLinkerAtCrash(
|
||||
const std::string& handler_trampoline,
|
||||
const std::string& handler_library,
|
||||
bool is_64_bit,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments);
|
||||
|
||||
//! \brief Starts a Crashpad handler process with an initial client by loading
|
||||
//! it with `/system/bin/linker`.
|
||||
//!
|
||||
//! This method is only supported by Android Q+.
|
||||
//!
|
||||
//! \param[in] handler_trampoline The path to a Crashpad handler trampoline
|
||||
//! executable, possibly located within an apk, e.g.
|
||||
//! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so".
|
||||
//! \param[in] handler_library The name of a library exporting the symbol
|
||||
//! `CrashpadHandlerMain()`. The path to this library must be present in
|
||||
//! `LD_LIBRARY_PATH`.
|
||||
//! \param[in] is_64_bit `true` if \a handler_trampoline and \a
|
||||
//! handler_library are 64-bit objects. They must have the same bitness.
|
||||
//! \param[in] env A vector of environment variables of the form `var=value`
|
||||
//! defining the environment in which to execute `app_process`. If this
|
||||
//! value is `nullptr`, the application's current environment will be
|
||||
//! used.
|
||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||
//! started with this path as its `--database` argument.
|
||||
//! \param[in] metrics_dir The path to an already existing directory where
|
||||
//! metrics files can be stored. The handler will be started with this
|
||||
//! path as its `--metrics-dir` argument.
|
||||
//! \param[in] url The URL of an upload server. The handler will be started
|
||||
//! with this URL as its `--url` argument.
|
||||
//! \param[in] annotations Process annotations to set in each crash report.
|
||||
//! The handler will be started with an `--annotation` argument for each
|
||||
//! element in this map.
|
||||
//! \param[in] arguments Additional arguments to pass to the Crashpad handler.
|
||||
//! Arguments passed in other parameters and arguments required to perform
|
||||
//! the handshake are the responsibility of this method, and must not be
|
||||
//! specified in this parameter.
|
||||
//! \param[in] socket The server end of a socket pair. The client end should
|
||||
//! be used with an ExceptionHandlerClient.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
static bool StartHandlerWithLinkerForClient(
|
||||
const std::string& handler_trampoline,
|
||||
const std::string& handler_library,
|
||||
bool is_64_bit,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
int socket);
|
||||
#endif // OS_ANDROID || DOXYGEN
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
|
||||
//! \brief Installs a signal handler to launch a handler process in reponse to
|
||||
//! a crash.
|
||||
@ -135,7 +334,7 @@ class CrashpadClient {
|
||||
//! specified in this parameter.
|
||||
//!
|
||||
//! \return `true` on success, `false` on failure with a message logged.
|
||||
static bool StartHandlerAtCrash(
|
||||
bool StartHandlerAtCrash(
|
||||
const base::FilePath& handler,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
@ -188,6 +387,12 @@ class CrashpadClient {
|
||||
//! CaptureContext() or similar.
|
||||
static void DumpWithoutCrash(NativeCPUContext* context);
|
||||
|
||||
//! \brief Disables any installed crash handler, including any
|
||||
//! FirstChanceHandler and crashes the current process.
|
||||
//!
|
||||
//! \param[in] message A message to be logged before crashing.
|
||||
static void CrashWithoutDump(const std::string& message);
|
||||
|
||||
//! \brief The type for custom handlers installed by clients.
|
||||
using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);
|
||||
|
||||
@ -209,8 +414,38 @@ class CrashpadClient {
|
||||
//! \param[in] handler The custom crash signal handler to install.
|
||||
static void SetFirstChanceExceptionHandler(FirstChanceHandler handler);
|
||||
|
||||
//! \brief Configures a set of signals that shouldn't have Crashpad signal
|
||||
//! handlers installed.
|
||||
//!
|
||||
//! This method should be called before calling StartHandler(),
|
||||
//! SetHandlerSocket(), or other methods that install Crashpad signal
|
||||
//! handlers.
|
||||
//!
|
||||
//! \param[in] unhandled_signals The set of unhandled signals
|
||||
void SetUnhandledSignals(const std::set<int>& unhandled_signals);
|
||||
#endif // OS_LINUX || OS_ANDROID || DOXYGEN
|
||||
|
||||
#if defined(OS_IOS) || DOXYGEN
|
||||
//! \brief Configures the process to direct its crashes to the iOS in-process
|
||||
//! Crashpad handler.
|
||||
//!
|
||||
//! This method is only defined on iOS.
|
||||
//!
|
||||
//! TODO(justincohen): This method will need to take database, metrics_dir,
|
||||
//! url and annotations eventually.
|
||||
void StartCrashpadInProcessHandler();
|
||||
|
||||
// TODO(justincohen): This method is purely for bringing up iOS interfaces.
|
||||
//! \brief Requests that the handler capture a dump even though there hasn't
|
||||
//! been a crash.
|
||||
//!
|
||||
//! A handler must have already been installed before calling this method.
|
||||
//!
|
||||
//! \param[in] context A NativeCPUContext, generally captured by
|
||||
//! CaptureContext() or similar.
|
||||
static void DumpWithoutCrash(NativeCPUContext* context);
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX) || DOXYGEN
|
||||
//! \brief Sets the process’ crash handler to a Mach service registered with
|
||||
//! the bootstrap server.
|
||||
@ -384,12 +619,24 @@ class CrashpadClient {
|
||||
static void UseSystemDefaultHandler();
|
||||
#endif
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
//! \brief Sets a timestamp on the signal handler to be passed on to
|
||||
//! crashpad_handler and then eventually Chrome OS's crash_reporter.
|
||||
//!
|
||||
//! \note This method is used by clients that use `StartHandler()` to start
|
||||
//! a handler and not by clients that use any other handler starting
|
||||
//! methods.
|
||||
static void SetCrashLoopBefore(uint64_t crash_loop_before_time);
|
||||
#endif
|
||||
|
||||
private:
|
||||
#if defined(OS_MACOSX)
|
||||
base::mac::ScopedMachSendRight exception_port_;
|
||||
#elif defined(OS_WIN)
|
||||
std::wstring ipc_pipe_;
|
||||
ScopedKernelHANDLE handler_start_thread_;
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
std::set<int> unhandled_signals_;
|
||||
#endif // OS_MACOSX
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
|
||||
|
@ -15,15 +15,15 @@
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
#include <lib/fdio/spawn.h>
|
||||
#include <zircon/process.h>
|
||||
#include <lib/zx/channel.h>
|
||||
#include <lib/zx/job.h>
|
||||
#include <lib/zx/process.h>
|
||||
#include <zircon/processargs.h>
|
||||
|
||||
#include "base/fuchsia/fuchsia_logging.h"
|
||||
#include "base/fuchsia/scoped_zx_handle.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "client/client_argv_handling.h"
|
||||
#include "util/fuchsia/system_exception_port_key.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -43,52 +43,44 @@ bool CrashpadClient::StartHandler(
|
||||
DCHECK_EQ(restartable, false); // Not used on Fuchsia.
|
||||
DCHECK_EQ(asynchronous_start, false); // Not used on Fuchsia.
|
||||
|
||||
zx_handle_t exception_port_raw;
|
||||
zx_status_t status = zx_port_create(0, &exception_port_raw);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status) << "zx_port_create";
|
||||
return false;
|
||||
}
|
||||
base::ScopedZxHandle exception_port(exception_port_raw);
|
||||
|
||||
status = zx_task_bind_exception_port(
|
||||
zx_job_default(), exception_port.get(), kSystemExceptionPortKey, 0);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status) << "zx_task_bind_exception_port";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> argv_strings = BuildHandlerArgvStrings(
|
||||
handler, database, metrics_dir, url, annotations, arguments);
|
||||
|
||||
std::vector<const char*> argv;
|
||||
ConvertArgvStrings(argv_strings, &argv);
|
||||
StringVectorToCStringVector(argv_strings, &argv);
|
||||
|
||||
// Follow the same protocol as devmgr and crashlogger in Zircon (that is,
|
||||
// process handle as handle 0, with type USER0, exception port handle as
|
||||
// handle 1, also with type PA_USER0) so that it's trivial to replace
|
||||
// crashlogger with crashpad_handler. The exception port is passed on, so
|
||||
// released here. Currently it is assumed that this process's default job
|
||||
// handle is the exception port that should be monitored. In the future, it
|
||||
// might be useful for this to be configurable by the client.
|
||||
constexpr size_t kActionCount = 2;
|
||||
fdio_spawn_action_t actions[] = {
|
||||
{.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
|
||||
.h = {.id = PA_HND(PA_USER0, 0), .handle = ZX_HANDLE_INVALID}},
|
||||
{.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
|
||||
.h = {.id = PA_HND(PA_USER0, 1), .handle = ZX_HANDLE_INVALID}},
|
||||
};
|
||||
|
||||
status = zx_handle_duplicate(
|
||||
zx_job_default(), ZX_RIGHT_SAME_RIGHTS, &actions[0].h.handle);
|
||||
// Set up handles to send to the spawned process:
|
||||
// 0. PA_USER0 job
|
||||
// 1. PA_USER0 exception channel
|
||||
//
|
||||
// Currently it is assumed that this process's default job handle is the
|
||||
// exception channel that should be monitored. In the future, it might be
|
||||
// useful for this to be configurable by the client.
|
||||
zx::job job;
|
||||
zx_status_t status =
|
||||
zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &job);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status) << "zx_handle_duplicate";
|
||||
return false;
|
||||
}
|
||||
actions[1].h.handle = exception_port.release();
|
||||
|
||||
zx::channel exception_channel;
|
||||
status = job.create_exception_channel(0, &exception_channel);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status) << "zx_task_create_exception_channel";
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr size_t kActionCount = 2;
|
||||
fdio_spawn_action_t actions[] = {
|
||||
{.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
|
||||
.h = {.id = PA_HND(PA_USER0, 0), .handle = job.release()}},
|
||||
{.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
|
||||
.h = {.id = PA_HND(PA_USER0, 1), .handle = exception_channel.release()}},
|
||||
};
|
||||
|
||||
char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH];
|
||||
zx_handle_t child_raw;
|
||||
zx::process child;
|
||||
// TODO(scottmg): https://crashpad.chromium.org/bug/196, FDIO_SPAWN_CLONE_ALL
|
||||
// is useful during bringup, but should probably be made minimal for real
|
||||
// usage.
|
||||
@ -99,9 +91,8 @@ bool CrashpadClient::StartHandler(
|
||||
nullptr,
|
||||
kActionCount,
|
||||
actions,
|
||||
&child_raw,
|
||||
child.reset_and_get_address(),
|
||||
error_message);
|
||||
base::ScopedZxHandle child(child_raw);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status) << "fdio_spawn_etc: " << error_message;
|
||||
return false;
|
||||
|
228
client/crashpad_client_ios.cc
Normal file
228
client/crashpad_client_ios.cc
Normal file
@ -0,0 +1,228 @@
|
||||
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <ios>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/mac/scoped_mach_port.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "snapshot/ios/process_snapshot_ios.h"
|
||||
#include "util/ios/exception_processor.h"
|
||||
#include "util/ios/ios_system_data_collector.h"
|
||||
#include "util/mach/exc_server_variants.h"
|
||||
#include "util/mach/exception_ports.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
#include "util/mach/mach_message.h"
|
||||
#include "util/mach/mach_message_server.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/posix/signals.h"
|
||||
#include "util/thread/thread.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
// A base class for signal handler and Mach exception server.
|
||||
class CrashHandler : public Thread, public UniversalMachExcServer::Interface {
|
||||
public:
|
||||
static CrashHandler* Get() {
|
||||
static CrashHandler* instance = new CrashHandler();
|
||||
return instance;
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
InstallMachExceptionHandler();
|
||||
CHECK(Signals::InstallHandler(SIGABRT, CatchSignal, 0, &old_action_));
|
||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||
}
|
||||
|
||||
void DumpWithoutCrash(NativeCPUContext* context) {
|
||||
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||
mach_exception_data_type_t code[2] = {};
|
||||
static constexpr int kSimulatedException = -1;
|
||||
HandleMachException(MACH_EXCEPTION_CODES,
|
||||
mach_thread_self(),
|
||||
kSimulatedException,
|
||||
code,
|
||||
base::size(code),
|
||||
MACHINE_THREAD_STATE,
|
||||
reinterpret_cast<ConstThreadState>(context),
|
||||
MACHINE_THREAD_STATE_COUNT);
|
||||
}
|
||||
|
||||
private:
|
||||
CrashHandler() = default;
|
||||
|
||||
void InstallMachExceptionHandler() {
|
||||
exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
|
||||
CHECK(exception_port_.is_valid());
|
||||
|
||||
kern_return_t kr = mach_port_insert_right(mach_task_self(),
|
||||
exception_port_.get(),
|
||||
exception_port_.get(),
|
||||
MACH_MSG_TYPE_MAKE_SEND);
|
||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_right";
|
||||
|
||||
// TODO: Use SwapExceptionPort instead and put back EXC_MASK_BREAKPOINT.
|
||||
const exception_mask_t mask =
|
||||
ExcMaskAll() &
|
||||
~(EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_BREAKPOINT |
|
||||
EXC_MASK_RPC_ALERT | EXC_MASK_GUARD);
|
||||
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL);
|
||||
exception_ports.GetExceptionPorts(mask, &original_handlers_);
|
||||
exception_ports.SetExceptionPort(
|
||||
mask,
|
||||
exception_port_.get(),
|
||||
EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES,
|
||||
MACHINE_THREAD_STATE);
|
||||
|
||||
Start();
|
||||
}
|
||||
|
||||
// Thread:
|
||||
|
||||
void ThreadMain() override {
|
||||
UniversalMachExcServer universal_mach_exc_server(this);
|
||||
while (true) {
|
||||
mach_msg_return_t mr =
|
||||
MachMessageServer::Run(&universal_mach_exc_server,
|
||||
exception_port_.get(),
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kPersistent,
|
||||
MachMessageServer::kReceiveLargeIgnore,
|
||||
kMachMessageTimeoutWaitIndefinitely);
|
||||
MACH_CHECK(mr == MACH_SEND_INVALID_DEST, mr) << "MachMessageServer::Run";
|
||||
}
|
||||
}
|
||||
|
||||
// UniversalMachExcServer::Interface:
|
||||
|
||||
kern_return_t CatchMachException(exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
ConstThreadState old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
const mach_msg_trailer_t* trailer,
|
||||
bool* destroy_complex_request) override {
|
||||
*destroy_complex_request = true;
|
||||
|
||||
// TODO(justincohen): Forward exceptions to original_handlers_ with
|
||||
// UniversalExceptionRaise.
|
||||
|
||||
// iOS shouldn't have any child processes, but just in case, those will
|
||||
// inherit the task exception ports, and this process isn’t prepared to
|
||||
// handle them
|
||||
if (task != mach_task_self()) {
|
||||
LOG(WARNING) << "task 0x" << std::hex << task << " != 0x"
|
||||
<< mach_task_self();
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
HandleMachException(behavior,
|
||||
thread,
|
||||
exception,
|
||||
code,
|
||||
code_count,
|
||||
*flavor,
|
||||
old_state,
|
||||
old_state_count);
|
||||
|
||||
// Respond with KERN_FAILURE so the system will continue to handle this
|
||||
// exception as a crash.
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
void HandleMachException(exception_behavior_t behavior,
|
||||
thread_t thread,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t flavor,
|
||||
ConstThreadState old_state,
|
||||
mach_msg_type_number_t old_state_count) {
|
||||
// TODO(justincohen): This is incomplete.
|
||||
ProcessSnapshotIOS process_snapshot;
|
||||
process_snapshot.Initialize(system_data_);
|
||||
process_snapshot.SetExceptionFromMachException(behavior,
|
||||
thread,
|
||||
exception,
|
||||
code,
|
||||
code_count,
|
||||
flavor,
|
||||
old_state,
|
||||
old_state_count);
|
||||
}
|
||||
|
||||
// The signal handler installed at OS-level.
|
||||
static void CatchSignal(int signo, siginfo_t* siginfo, void* context) {
|
||||
Get()->HandleAndReraiseSignal(
|
||||
signo, siginfo, reinterpret_cast<ucontext_t*>(context));
|
||||
}
|
||||
|
||||
void HandleAndReraiseSignal(int signo,
|
||||
siginfo_t* siginfo,
|
||||
ucontext_t* context) {
|
||||
// TODO(justincohen): This is incomplete.
|
||||
ProcessSnapshotIOS process_snapshot;
|
||||
process_snapshot.Initialize(system_data_);
|
||||
process_snapshot.SetExceptionFromSignal(siginfo, context);
|
||||
|
||||
// Always call system handler.
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, &old_action_);
|
||||
}
|
||||
|
||||
base::mac::ScopedMachReceiveRight exception_port_;
|
||||
ExceptionPorts::ExceptionHandlerVector original_handlers_;
|
||||
struct sigaction old_action_ = {};
|
||||
IOSSystemDataCollector system_data_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashHandler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashpadClient::CrashpadClient() {}
|
||||
|
||||
CrashpadClient::~CrashpadClient() {}
|
||||
|
||||
void CrashpadClient::StartCrashpadInProcessHandler() {
|
||||
InstallObjcExceptionPreprocessor();
|
||||
|
||||
CrashHandler* crash_handler = CrashHandler::Get();
|
||||
DCHECK(crash_handler);
|
||||
crash_handler->Initialize();
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
||||
CrashHandler* crash_handler = CrashHandler::Get();
|
||||
DCHECK(crash_handler);
|
||||
crash_handler->DumpWithoutCrash(context);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
73
client/crashpad_client_ios_test.mm
Normal file
73
client/crashpad_client_ios_test.mm
Normal file
@ -0,0 +1,73 @@
|
||||
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "testing/platform_test.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
using CrashpadIOSClient = PlatformTest;
|
||||
|
||||
TEST_F(CrashpadIOSClient, DumpWithoutCrash) {
|
||||
CrashpadClient client;
|
||||
client.StartCrashpadInProcessHandler();
|
||||
|
||||
NativeCPUContext context;
|
||||
#if defined(ARCH_CPU_X86_64)
|
||||
CaptureContext(&context);
|
||||
#elif defined(ARCH_CPU_ARM64)
|
||||
// TODO(justincohen): Implement CaptureContext for ARM64.
|
||||
mach_msg_type_number_t thread_state_count = MACHINE_THREAD_STATE_COUNT;
|
||||
kern_return_t kr =
|
||||
thread_get_state(mach_thread_self(),
|
||||
MACHINE_THREAD_STATE,
|
||||
reinterpret_cast<thread_state_t>(&context),
|
||||
&thread_state_count);
|
||||
ASSERT_EQ(kr, KERN_SUCCESS);
|
||||
#endif
|
||||
client.DumpWithoutCrash(&context);
|
||||
}
|
||||
|
||||
// This test is covered by a similar XCUITest, but for development purposes
|
||||
// it's sometimes easier and faster to run as a gtest. However, there's no
|
||||
// way to correctly run this as a gtest. Leave the test here, disabled, for use
|
||||
// during development only.
|
||||
TEST_F(CrashpadIOSClient, DISABLED_ThrowNSException) {
|
||||
CrashpadClient client;
|
||||
client.StartCrashpadInProcessHandler();
|
||||
[NSException raise:@"GtestNSException" format:@"ThrowException"];
|
||||
}
|
||||
|
||||
// This test is covered by a similar XCUITest, but for development purposes
|
||||
// it's sometimes easier and faster to run as a gtest. However, there's no
|
||||
// way to correctly run this as a gtest. Leave the test here, disabled, for use
|
||||
// during development only.
|
||||
TEST_F(CrashpadIOSClient, DISABLED_ThrowException) {
|
||||
CrashpadClient client;
|
||||
client.StartCrashpadInProcessHandler();
|
||||
std::vector<int> empty_vector;
|
||||
empty_vector.at(42);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -16,6 +16,7 @@
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
@ -25,10 +26,14 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "client/client_argv_handling.h"
|
||||
#include "third_party/lss/lss.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/file/filesystem.h"
|
||||
#include "util/linux/exception_handler_client.h"
|
||||
#include "util/linux/exception_information.h"
|
||||
#include "util/linux/scoped_pr_set_dumpable.h"
|
||||
#include "util/linux/scoped_pr_set_ptracer.h"
|
||||
#include "util/linux/socket.h"
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
#include "util/posix/double_fork_and_exec.h"
|
||||
#include "util/posix/signals.h"
|
||||
@ -41,51 +46,103 @@ std::string FormatArgumentInt(const std::string& name, int value) {
|
||||
return base::StringPrintf("--%s=%d", name.c_str(), value);
|
||||
}
|
||||
|
||||
std::string FormatArgumentAddress(const std::string& name, void* addr) {
|
||||
std::string FormatArgumentAddress(const std::string& name, const void* addr) {
|
||||
return base::StringPrintf("--%s=%p", name.c_str(), addr);
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
|
||||
std::vector<std::string> BuildAppProcessArgs(
|
||||
const std::string& class_name,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
int socket) {
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
static constexpr char kAppProcess[] = "/system/bin/app_process64";
|
||||
#else
|
||||
static constexpr char kAppProcess[] = "/system/bin/app_process32";
|
||||
#endif
|
||||
|
||||
std::vector<std::string> argv;
|
||||
argv.push_back(kAppProcess);
|
||||
argv.push_back("/system/bin");
|
||||
argv.push_back("--application");
|
||||
argv.push_back(class_name);
|
||||
|
||||
std::vector<std::string> handler_argv =
|
||||
BuildHandlerArgvStrings(base::FilePath(kAppProcess),
|
||||
database,
|
||||
metrics_dir,
|
||||
url,
|
||||
annotations,
|
||||
arguments);
|
||||
|
||||
if (socket != kInvalidFileHandle) {
|
||||
handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
|
||||
}
|
||||
|
||||
argv.insert(argv.end(), handler_argv.begin(), handler_argv.end());
|
||||
return argv;
|
||||
}
|
||||
|
||||
std::vector<std::string> BuildArgsToLaunchWithLinker(
|
||||
const std::string& handler_trampoline,
|
||||
const std::string& handler_library,
|
||||
bool is_64_bit,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
int socket) {
|
||||
std::vector<std::string> argv;
|
||||
if (is_64_bit) {
|
||||
argv.push_back("/system/bin/linker64");
|
||||
} else {
|
||||
argv.push_back("/system/bin/linker");
|
||||
}
|
||||
argv.push_back(handler_trampoline);
|
||||
argv.push_back(handler_library);
|
||||
|
||||
std::vector<std::string> handler_argv = BuildHandlerArgvStrings(
|
||||
base::FilePath(), database, metrics_dir, url, annotations, arguments);
|
||||
|
||||
if (socket != kInvalidFileHandle) {
|
||||
handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket));
|
||||
}
|
||||
|
||||
argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end());
|
||||
return argv;
|
||||
}
|
||||
|
||||
#endif // OS_ANDROID
|
||||
|
||||
// A base class for Crashpad signal handler implementations.
|
||||
class SignalHandler {
|
||||
public:
|
||||
virtual void HandleCrashFatal(int signo,
|
||||
siginfo_t* siginfo,
|
||||
void* context) = 0;
|
||||
virtual bool HandleCrashNonFatal(int signo,
|
||||
siginfo_t* siginfo,
|
||||
void* context) = 0;
|
||||
// Returns the currently installed signal hander. May be `nullptr` if no
|
||||
// handler has been installed.
|
||||
static SignalHandler* Get() { return handler_; }
|
||||
|
||||
// Disables any installed Crashpad signal handler for the calling thread. If a
|
||||
// crash signal is received, any previously installed (non-Crashpad) signal
|
||||
// handler will be restored and the signal reraised.
|
||||
static void DisableForThread() { disabled_for_thread_ = true; }
|
||||
|
||||
void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
|
||||
first_chance_handler_ = handler;
|
||||
}
|
||||
|
||||
protected:
|
||||
SignalHandler() = default;
|
||||
~SignalHandler() = default;
|
||||
// The base implementation for all signal handlers, suitable for calling
|
||||
// directly to simulate signal delivery.
|
||||
bool HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
||||
if (disabled_for_thread_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
|
||||
};
|
||||
|
||||
// Launches a single use handler to snapshot this process.
|
||||
class LaunchAtCrashHandler : public SignalHandler {
|
||||
public:
|
||||
static LaunchAtCrashHandler* Get() {
|
||||
static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool Initialize(std::vector<std::string>* argv_in) {
|
||||
argv_strings_.swap(*argv_in);
|
||||
|
||||
argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
|
||||
&exception_information_));
|
||||
|
||||
ConvertArgvStrings(argv_strings_, &argv_);
|
||||
return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr);
|
||||
}
|
||||
|
||||
bool HandleCrashNonFatal(int signo,
|
||||
siginfo_t* siginfo,
|
||||
void* context) override {
|
||||
if (first_chance_handler_ &&
|
||||
first_chance_handler_(
|
||||
signo, siginfo, static_cast<ucontext_t*>(context))) {
|
||||
@ -98,29 +155,100 @@ class LaunchAtCrashHandler : public SignalHandler {
|
||||
exception_information_.context_address =
|
||||
FromPointerCast<decltype(exception_information_.context_address)>(
|
||||
context);
|
||||
exception_information_.thread_id = syscall(SYS_gettid);
|
||||
exception_information_.thread_id = sys_gettid();
|
||||
|
||||
ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ false);
|
||||
ScopedPrSetDumpable set_dumpable(false);
|
||||
HandleCrashImpl();
|
||||
return false;
|
||||
}
|
||||
|
||||
protected:
|
||||
SignalHandler() = default;
|
||||
|
||||
bool Install(const std::set<int>* unhandled_signals) {
|
||||
DCHECK(!handler_);
|
||||
handler_ = this;
|
||||
return Signals::InstallCrashHandlers(
|
||||
HandleOrReraiseSignal, 0, &old_actions_, unhandled_signals);
|
||||
}
|
||||
|
||||
const ExceptionInformation& GetExceptionInfo() {
|
||||
return exception_information_;
|
||||
}
|
||||
|
||||
virtual void HandleCrashImpl() = 0;
|
||||
|
||||
private:
|
||||
// The signal handler installed at OS-level.
|
||||
static void HandleOrReraiseSignal(int signo,
|
||||
siginfo_t* siginfo,
|
||||
void* context) {
|
||||
if (handler_->HandleCrash(signo, siginfo, context)) {
|
||||
return;
|
||||
}
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(
|
||||
siginfo, handler_->old_actions_.ActionForSignal(signo));
|
||||
}
|
||||
|
||||
Signals::OldActions old_actions_ = {};
|
||||
ExceptionInformation exception_information_ = {};
|
||||
CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
|
||||
|
||||
static SignalHandler* handler_;
|
||||
|
||||
static thread_local bool disabled_for_thread_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SignalHandler);
|
||||
};
|
||||
SignalHandler* SignalHandler::handler_ = nullptr;
|
||||
thread_local bool SignalHandler::disabled_for_thread_ = false;
|
||||
|
||||
// Launches a single use handler to snapshot this process.
|
||||
class LaunchAtCrashHandler : public SignalHandler {
|
||||
public:
|
||||
static LaunchAtCrashHandler* Get() {
|
||||
static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler();
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool Initialize(std::vector<std::string>* argv_in,
|
||||
const std::vector<std::string>* envp,
|
||||
const std::set<int>* unhandled_signals) {
|
||||
argv_strings_.swap(*argv_in);
|
||||
|
||||
if (envp) {
|
||||
envp_strings_ = *envp;
|
||||
StringVectorToCStringVector(envp_strings_, &envp_);
|
||||
set_envp_ = true;
|
||||
}
|
||||
|
||||
argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception",
|
||||
&GetExceptionInfo()));
|
||||
|
||||
StringVectorToCStringVector(argv_strings_, &argv_);
|
||||
return Install(unhandled_signals);
|
||||
}
|
||||
|
||||
void HandleCrashImpl() override {
|
||||
ScopedPrSetPtracer set_ptracer(sys_getpid(), /* may_log= */ false);
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
if (pid == 0) {
|
||||
execv(argv_[0], const_cast<char* const*>(argv_.data()));
|
||||
if (set_envp_) {
|
||||
execve(argv_[0],
|
||||
const_cast<char* const*>(argv_.data()),
|
||||
const_cast<char* const*>(envp_.data()));
|
||||
} else {
|
||||
execv(argv_[0], const_cast<char* const*>(argv_.data()));
|
||||
}
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(pid, &status, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
void HandleCrashFatal(int signo, siginfo_t* siginfo, void* context) override {
|
||||
if (HandleCrashNonFatal(signo, siginfo, context)) {
|
||||
return;
|
||||
}
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
@ -128,22 +256,107 @@ class LaunchAtCrashHandler : public SignalHandler {
|
||||
|
||||
~LaunchAtCrashHandler() = delete;
|
||||
|
||||
static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
||||
auto state = Get();
|
||||
state->HandleCrashFatal(signo, siginfo, context);
|
||||
}
|
||||
|
||||
std::vector<std::string> argv_strings_;
|
||||
std::vector<const char*> argv_;
|
||||
ExceptionInformation exception_information_;
|
||||
std::vector<std::string> envp_strings_;
|
||||
std::vector<const char*> envp_;
|
||||
bool set_envp_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
|
||||
};
|
||||
|
||||
// A pointer to the currently installed crash signal handler. This allows
|
||||
// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash
|
||||
// using the currently configured crash handling strategy.
|
||||
static SignalHandler* g_crash_handler;
|
||||
class RequestCrashDumpHandler : public SignalHandler {
|
||||
public:
|
||||
static RequestCrashDumpHandler* Get() {
|
||||
static RequestCrashDumpHandler* instance = new RequestCrashDumpHandler();
|
||||
return instance;
|
||||
}
|
||||
|
||||
// pid < 0 indicates the handler pid should be determined by communicating
|
||||
// over the socket.
|
||||
// pid == 0 indicates it is not necessary to set the handler as this process'
|
||||
// ptracer. e.g. if the handler has CAP_SYS_PTRACE or if this process is in a
|
||||
// user namespace and the handler's uid matches the uid of the process that
|
||||
// created the namespace.
|
||||
// pid > 0 directly indicates what the handler's pid is expected to be, so
|
||||
// retrieving this information from the handler is not necessary.
|
||||
bool Initialize(ScopedFileHandle sock,
|
||||
pid_t pid,
|
||||
const std::set<int>* unhandled_signals) {
|
||||
ExceptionHandlerClient client(sock.get(), true);
|
||||
if (pid < 0) {
|
||||
ucred creds;
|
||||
if (!client.GetHandlerCredentials(&creds)) {
|
||||
return false;
|
||||
}
|
||||
pid = creds.pid;
|
||||
}
|
||||
if (pid > 0 && prctl(PR_SET_PTRACER, pid, 0, 0, 0) != 0) {
|
||||
PLOG(WARNING) << "prctl";
|
||||
// TODO(jperaza): If this call to set the ptracer failed, it might be
|
||||
// possible to try again just before a dump request, in case the
|
||||
// environment has changed. Revisit ExceptionHandlerClient::SetPtracer()
|
||||
// and consider saving the result of this call in ExceptionHandlerClient
|
||||
// or as a member in this signal handler. ExceptionHandlerClient hasn't
|
||||
// been responsible for maintaining state and a new ExceptionHandlerClient
|
||||
// has been constructed as a local whenever a client needs to communicate
|
||||
// with the handler. ExceptionHandlerClient lifetimes and ownership will
|
||||
// need to be reconsidered if it becomes responsible for state.
|
||||
}
|
||||
sock_to_handler_.reset(sock.release());
|
||||
handler_pid_ = pid;
|
||||
return Install(unhandled_signals);
|
||||
}
|
||||
|
||||
bool GetHandlerSocket(int* sock, pid_t* pid) {
|
||||
if (!sock_to_handler_.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
if (sock) {
|
||||
*sock = sock_to_handler_.get();
|
||||
}
|
||||
if (pid) {
|
||||
*pid = handler_pid_;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void HandleCrashImpl() override {
|
||||
ExceptionHandlerProtocol::ClientInformation info = {};
|
||||
info.exception_information_address =
|
||||
FromPointerCast<VMAddress>(&GetExceptionInfo());
|
||||
#if defined(OS_CHROMEOS)
|
||||
info.crash_loop_before_time = crash_loop_before_time_;
|
||||
#endif
|
||||
|
||||
ExceptionHandlerClient client(sock_to_handler_.get(), true);
|
||||
client.RequestCrashDump(info);
|
||||
}
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
void SetCrashLoopBefore(uint64_t crash_loop_before_time) {
|
||||
crash_loop_before_time_ = crash_loop_before_time;
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
RequestCrashDumpHandler() = default;
|
||||
|
||||
~RequestCrashDumpHandler() = delete;
|
||||
|
||||
ScopedFileHandle sock_to_handler_;
|
||||
pid_t handler_pid_ = -1;
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
// An optional UNIX timestamp passed to us from Chrome.
|
||||
// This will pass to crashpad_handler and then to Chrome OS crash_reporter.
|
||||
// This should really be a time_t, but it's basically an opaque value (we
|
||||
// don't anything with it except pass it along).
|
||||
uint64_t crash_loop_before_time_ = 0;
|
||||
#endif
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RequestCrashDumpHandler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
@ -160,14 +373,134 @@ bool CrashpadClient::StartHandler(
|
||||
const std::vector<std::string>& arguments,
|
||||
bool restartable,
|
||||
bool asynchronous_start) {
|
||||
// TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever
|
||||
// supports accepting new connections.
|
||||
// https://crashpad.chromium.org/bug/30
|
||||
NOTREACHED();
|
||||
return false;
|
||||
DCHECK(!asynchronous_start);
|
||||
|
||||
ScopedFileHandle client_sock, handler_sock;
|
||||
if (!UnixCredentialSocket::CreateCredentialSocketpair(&client_sock,
|
||||
&handler_sock)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
||||
handler, database, metrics_dir, url, annotations, arguments);
|
||||
|
||||
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
|
||||
argv.push_back("--shared-client-connection");
|
||||
if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t handler_pid = -1;
|
||||
if (!IsRegularFile(base::FilePath("/proc/sys/kernel/yama/ptrace_scope"))) {
|
||||
handler_pid = 0;
|
||||
}
|
||||
|
||||
auto signal_handler = RequestCrashDumpHandler::Get();
|
||||
return signal_handler->Initialize(
|
||||
std::move(client_sock), handler_pid, &unhandled_signals_);
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID) || defined(OS_LINUX)
|
||||
// static
|
||||
bool CrashpadClient::GetHandlerSocket(int* sock, pid_t* pid) {
|
||||
auto signal_handler = RequestCrashDumpHandler::Get();
|
||||
return signal_handler->GetHandlerSocket(sock, pid);
|
||||
}
|
||||
|
||||
bool CrashpadClient::SetHandlerSocket(ScopedFileHandle sock, pid_t pid) {
|
||||
auto signal_handler = RequestCrashDumpHandler::Get();
|
||||
return signal_handler->Initialize(std::move(sock), pid, &unhandled_signals_);
|
||||
}
|
||||
#endif // OS_ANDROID || OS_LINUX
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
|
||||
bool CrashpadClient::StartJavaHandlerAtCrash(
|
||||
const std::string& class_name,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments) {
|
||||
std::vector<std::string> argv = BuildAppProcessArgs(class_name,
|
||||
database,
|
||||
metrics_dir,
|
||||
url,
|
||||
annotations,
|
||||
arguments,
|
||||
kInvalidFileHandle);
|
||||
|
||||
auto signal_handler = LaunchAtCrashHandler::Get();
|
||||
return signal_handler->Initialize(&argv, env, &unhandled_signals_);
|
||||
}
|
||||
|
||||
// static
|
||||
bool CrashpadClient::StartJavaHandlerForClient(
|
||||
const std::string& class_name,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
int socket) {
|
||||
std::vector<std::string> argv = BuildAppProcessArgs(
|
||||
class_name, database, metrics_dir, url, annotations, arguments, socket);
|
||||
return DoubleForkAndExec(argv, env, socket, false, nullptr);
|
||||
}
|
||||
|
||||
bool CrashpadClient::StartHandlerWithLinkerAtCrash(
|
||||
const std::string& handler_trampoline,
|
||||
const std::string& handler_library,
|
||||
bool is_64_bit,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments) {
|
||||
std::vector<std::string> argv =
|
||||
BuildArgsToLaunchWithLinker(handler_trampoline,
|
||||
handler_library,
|
||||
is_64_bit,
|
||||
database,
|
||||
metrics_dir,
|
||||
url,
|
||||
annotations,
|
||||
arguments,
|
||||
kInvalidFileHandle);
|
||||
auto signal_handler = LaunchAtCrashHandler::Get();
|
||||
return signal_handler->Initialize(&argv, env, &unhandled_signals_);
|
||||
}
|
||||
|
||||
// static
|
||||
bool CrashpadClient::StartHandlerWithLinkerForClient(
|
||||
const std::string& handler_trampoline,
|
||||
const std::string& handler_library,
|
||||
bool is_64_bit,
|
||||
const std::vector<std::string>* env,
|
||||
const base::FilePath& database,
|
||||
const base::FilePath& metrics_dir,
|
||||
const std::string& url,
|
||||
const std::map<std::string, std::string>& annotations,
|
||||
const std::vector<std::string>& arguments,
|
||||
int socket) {
|
||||
std::vector<std::string> argv =
|
||||
BuildArgsToLaunchWithLinker(handler_trampoline,
|
||||
handler_library,
|
||||
is_64_bit,
|
||||
database,
|
||||
metrics_dir,
|
||||
url,
|
||||
annotations,
|
||||
arguments,
|
||||
socket);
|
||||
return DoubleForkAndExec(argv, env, socket, false, nullptr);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool CrashpadClient::StartHandlerAtCrash(
|
||||
const base::FilePath& handler,
|
||||
const base::FilePath& database,
|
||||
@ -179,12 +512,7 @@ bool CrashpadClient::StartHandlerAtCrash(
|
||||
handler, database, metrics_dir, url, annotations, arguments);
|
||||
|
||||
auto signal_handler = LaunchAtCrashHandler::Get();
|
||||
if (signal_handler->Initialize(&argv)) {
|
||||
DCHECK(!g_crash_handler);
|
||||
g_crash_handler = signal_handler;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_);
|
||||
}
|
||||
|
||||
// static
|
||||
@ -199,14 +527,17 @@ bool CrashpadClient::StartHandlerForClient(
|
||||
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
||||
handler, database, metrics_dir, url, annotations, arguments);
|
||||
|
||||
argv.push_back(FormatArgumentInt("initial-client", socket));
|
||||
argv.push_back(FormatArgumentInt("initial-client-fd", socket));
|
||||
|
||||
return DoubleForkAndExec(argv, socket, true, nullptr);
|
||||
return DoubleForkAndExec(argv, nullptr, socket, true, nullptr);
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
||||
DCHECK(g_crash_handler);
|
||||
if (!SignalHandler::Get()) {
|
||||
DLOG(ERROR) << "Crashpad isn't enabled";
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(ARCH_CPU_ARMEL)
|
||||
memset(context->uc_regspace, 0, sizeof(context->uc_regspace));
|
||||
@ -220,15 +551,34 @@ void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
||||
siginfo.si_signo = Signals::kSimulatedSigno;
|
||||
siginfo.si_errno = 0;
|
||||
siginfo.si_code = 0;
|
||||
g_crash_handler->HandleCrashNonFatal(
|
||||
SignalHandler::Get()->HandleCrash(
|
||||
siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashpadClient::CrashWithoutDump(const std::string& message) {
|
||||
SignalHandler::DisableForThread();
|
||||
LOG(FATAL) << message;
|
||||
}
|
||||
|
||||
// static
|
||||
void CrashpadClient::SetFirstChanceExceptionHandler(
|
||||
FirstChanceHandler handler) {
|
||||
DCHECK(g_crash_handler);
|
||||
g_crash_handler->SetFirstChanceHandler(handler);
|
||||
DCHECK(SignalHandler::Get());
|
||||
SignalHandler::Get()->SetFirstChanceHandler(handler);
|
||||
}
|
||||
|
||||
void CrashpadClient::SetUnhandledSignals(const std::set<int>& signals) {
|
||||
DCHECK(!SignalHandler::Get());
|
||||
unhandled_signals_ = signals;
|
||||
}
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
// static
|
||||
void CrashpadClient::SetCrashLoopBefore(uint64_t crash_loop_before_time) {
|
||||
auto request_crash_dump_handler = RequestCrashDumpHandler::Get();
|
||||
request_crash_dump_handler->SetCrashLoopBefore(crash_loop_before_time);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -14,8 +14,8 @@
|
||||
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
@ -37,76 +37,98 @@
|
||||
#include "util/file/filesystem.h"
|
||||
#include "util/linux/exception_handler_client.h"
|
||||
#include "util/linux/exception_information.h"
|
||||
#include "util/linux/socket.h"
|
||||
#include "util/misc/address_types.h"
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
#include "util/posix/signals.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include <android/set_abort_message.h>
|
||||
#include "dlfcn_internal.h"
|
||||
|
||||
// Normally this comes from set_abort_message.h, but only at API level 21.
|
||||
extern "C" void android_set_abort_message(const char* msg)
|
||||
__attribute__((weak));
|
||||
#endif
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
struct StartHandlerForSelfTestOptions {
|
||||
bool start_handler_at_crash;
|
||||
bool simulate_crash;
|
||||
bool set_first_chance_handler;
|
||||
};
|
||||
|
||||
class StartHandlerForSelfTest
|
||||
: public testing::TestWithParam<std::tuple<bool, bool, bool>> {
|
||||
public:
|
||||
StartHandlerForSelfTest() = default;
|
||||
~StartHandlerForSelfTest() = default;
|
||||
|
||||
void SetUp() override {
|
||||
std::tie(options_.start_handler_at_crash,
|
||||
options_.simulate_crash,
|
||||
options_.set_first_chance_handler) = GetParam();
|
||||
}
|
||||
|
||||
const StartHandlerForSelfTestOptions& Options() const { return options_; }
|
||||
|
||||
private:
|
||||
StartHandlerForSelfTestOptions options_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfTest);
|
||||
};
|
||||
|
||||
bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) {
|
||||
return true;
|
||||
}
|
||||
|
||||
TEST(CrashpadClient, SimulateCrash) {
|
||||
ScopedTempDir temp_dir;
|
||||
|
||||
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
|
||||
FILE_PATH_LITERAL("crashpad_handler"));
|
||||
|
||||
crashpad::CrashpadClient client;
|
||||
ASSERT_TRUE(client.StartHandlerAtCrash(handler_path,
|
||||
base::FilePath(temp_dir.path()),
|
||||
base::FilePath(),
|
||||
"",
|
||||
std::map<std::string, std::string>(),
|
||||
std::vector<std::string>()));
|
||||
|
||||
auto database =
|
||||
CrashReportDatabase::InitializeWithoutCreating(temp_dir.path());
|
||||
ASSERT_TRUE(database);
|
||||
|
||||
{
|
||||
CrashpadClient::SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
|
||||
|
||||
CRASHPAD_SIMULATE_CRASH();
|
||||
|
||||
std::vector<CrashReportDatabase::Report> reports;
|
||||
ASSERT_EQ(database->GetPendingReports(&reports),
|
||||
CrashReportDatabase::kNoError);
|
||||
EXPECT_EQ(reports.size(), 0u);
|
||||
|
||||
reports.clear();
|
||||
ASSERT_EQ(database->GetCompletedReports(&reports),
|
||||
CrashReportDatabase::kNoError);
|
||||
EXPECT_EQ(reports.size(), 0u);
|
||||
}
|
||||
|
||||
{
|
||||
CrashpadClient::SetFirstChanceExceptionHandler(nullptr);
|
||||
|
||||
CRASHPAD_SIMULATE_CRASH();
|
||||
|
||||
std::vector<CrashReportDatabase::Report> reports;
|
||||
ASSERT_EQ(database->GetPendingReports(&reports),
|
||||
CrashReportDatabase::kNoError);
|
||||
EXPECT_EQ(reports.size(), 1u);
|
||||
|
||||
reports.clear();
|
||||
ASSERT_EQ(database->GetCompletedReports(&reports),
|
||||
CrashReportDatabase::kNoError);
|
||||
EXPECT_EQ(reports.size(), 0u);
|
||||
}
|
||||
bool InstallHandler(CrashpadClient* client,
|
||||
bool start_at_crash,
|
||||
const base::FilePath& handler_path,
|
||||
const base::FilePath& database_path) {
|
||||
return start_at_crash
|
||||
? client->StartHandlerAtCrash(handler_path,
|
||||
database_path,
|
||||
base::FilePath(),
|
||||
"",
|
||||
std::map<std::string, std::string>(),
|
||||
std::vector<std::string>())
|
||||
: client->StartHandler(handler_path,
|
||||
database_path,
|
||||
base::FilePath(),
|
||||
"",
|
||||
std::map<std::string, std::string>(),
|
||||
std::vector<std::string>(),
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
constexpr char kTestAnnotationName[] = "name_of_annotation";
|
||||
constexpr char kTestAnnotationValue[] = "value_of_annotation";
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
constexpr char kTestAbortMessage[] = "test abort message";
|
||||
#endif
|
||||
|
||||
void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
||||
ProcessSnapshotMinidump minidump_snapshot;
|
||||
ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// This part of the test requires Q. The API level on Q devices will be 28
|
||||
// until the API is finalized, so we can't check API level yet. For now, test
|
||||
// for the presence of a libc symbol which was introduced in Q.
|
||||
if (crashpad::internal::Dlsym(RTLD_DEFAULT, "android_fdsan_close_with_tag")) {
|
||||
const auto& annotations = minidump_snapshot.AnnotationsSimpleMap();
|
||||
auto abort_message = annotations.find("abort_message");
|
||||
ASSERT_NE(annotations.end(), abort_message);
|
||||
EXPECT_EQ(kTestAbortMessage, abort_message->second);
|
||||
}
|
||||
#endif
|
||||
|
||||
for (const ModuleSnapshot* module : minidump_snapshot.Modules()) {
|
||||
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
|
||||
if (static_cast<Annotation::Type>(annotation.type) !=
|
||||
@ -126,7 +148,7 @@ void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
||||
ADD_FAILURE();
|
||||
}
|
||||
|
||||
CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
|
||||
CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
|
||||
FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
|
||||
|
||||
VMSize temp_dir_length;
|
||||
@ -135,6 +157,9 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
|
||||
std::string temp_dir(temp_dir_length, '\0');
|
||||
CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length);
|
||||
|
||||
StartHandlerForSelfTestOptions options;
|
||||
CheckedReadFileExactly(in, &options, sizeof(options));
|
||||
|
||||
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
|
||||
FILE_PATH_LITERAL("crashpad_handler"));
|
||||
|
||||
@ -144,26 +169,41 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
|
||||
test_annotation.Set(kTestAnnotationValue);
|
||||
|
||||
crashpad::CrashpadClient client;
|
||||
if (!client.StartHandlerAtCrash(handler_path,
|
||||
base::FilePath(temp_dir),
|
||||
base::FilePath(),
|
||||
"",
|
||||
std::map<std::string, std::string>(),
|
||||
std::vector<std::string>())) {
|
||||
if (!InstallHandler(&client,
|
||||
options.start_handler_at_crash,
|
||||
handler_path,
|
||||
base::FilePath(temp_dir))) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
if (android_set_abort_message) {
|
||||
android_set_abort_message(kTestAbortMessage);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options.simulate_crash) {
|
||||
if (options.set_first_chance_handler) {
|
||||
client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
|
||||
}
|
||||
CRASHPAD_SIMULATE_CRASH();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
__builtin_trap();
|
||||
|
||||
NOTREACHED();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
class StartHandlerAtCrashTest : public MultiprocessExec {
|
||||
class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
||||
public:
|
||||
StartHandlerAtCrashTest() : MultiprocessExec() {
|
||||
SetChildTestMainFunction("StartHandlerAtCrashChild");
|
||||
SetExpectedChildTerminationBuiltinTrap();
|
||||
StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options)
|
||||
: MultiprocessExec(), options_(options) {
|
||||
SetChildTestMainFunction("StartHandlerForSelfTestChild");
|
||||
if (!options.simulate_crash) {
|
||||
SetExpectedChildTerminationBuiltinTrap();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@ -174,6 +214,8 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
|
||||
WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length)));
|
||||
ASSERT_TRUE(LoggingWriteFile(
|
||||
WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length));
|
||||
ASSERT_TRUE(
|
||||
LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_)));
|
||||
|
||||
// Wait for child to finish.
|
||||
CheckedReadFileAtEOF(ReadPipeHandle());
|
||||
@ -189,7 +231,11 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
|
||||
reports.clear();
|
||||
ASSERT_EQ(database->GetPendingReports(&reports),
|
||||
CrashReportDatabase::kNoError);
|
||||
EXPECT_EQ(reports.size(), 1u);
|
||||
ASSERT_EQ(reports.size(), options_.set_first_chance_handler ? 0u : 1u);
|
||||
|
||||
if (options_.set_first_chance_handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<const CrashReportDatabase::UploadReport> report;
|
||||
ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report),
|
||||
@ -197,14 +243,26 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
|
||||
ValidateDump(report.get());
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest);
|
||||
StartHandlerForSelfTestOptions options_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfInChildTest);
|
||||
};
|
||||
|
||||
TEST(CrashpadClient, StartHandlerAtCrash) {
|
||||
StartHandlerAtCrashTest test;
|
||||
TEST_P(StartHandlerForSelfTest, StartHandlerInChild) {
|
||||
if (Options().set_first_chance_handler && !Options().simulate_crash) {
|
||||
// TODO(jperaza): test first chance handlers with real crashes.
|
||||
return;
|
||||
}
|
||||
StartHandlerForSelfInChildTest test(Options());
|
||||
test.Run();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(StartHandlerForSelfTestSuite,
|
||||
StartHandlerForSelfTest,
|
||||
testing::Combine(testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Bool()));
|
||||
|
||||
// Test state for starting the handler for another process.
|
||||
class StartHandlerForClientTest {
|
||||
public:
|
||||
@ -213,16 +271,8 @@ class StartHandlerForClientTest {
|
||||
|
||||
bool Initialize(bool sanitize) {
|
||||
sanitize_ = sanitize;
|
||||
|
||||
int socks[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) != 0) {
|
||||
PLOG(ERROR) << "socketpair";
|
||||
return false;
|
||||
}
|
||||
client_sock_.reset(socks[0]);
|
||||
server_sock_.reset(socks[1]);
|
||||
|
||||
return true;
|
||||
return UnixCredentialSocket::CreateCredentialSocketpair(&client_sock_,
|
||||
&server_sock_);
|
||||
}
|
||||
|
||||
bool StartHandlerOnDemand() {
|
||||
@ -298,7 +348,7 @@ class StartHandlerForClientTest {
|
||||
static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
||||
auto state = Get();
|
||||
|
||||
char c;
|
||||
char c = 0;
|
||||
CHECK(LoggingWriteFile(state->client_sock_, &c, sizeof(c)));
|
||||
|
||||
ExceptionInformation exception_information;
|
||||
@ -310,7 +360,7 @@ class StartHandlerForClientTest {
|
||||
context);
|
||||
exception_information.thread_id = syscall(SYS_gettid);
|
||||
|
||||
ClientInformation info;
|
||||
ExceptionHandlerProtocol::ClientInformation info;
|
||||
info.exception_information_address =
|
||||
FromPointerCast<decltype(info.exception_information_address)>(
|
||||
&exception_information);
|
||||
@ -324,7 +374,7 @@ class StartHandlerForClientTest {
|
||||
FromPointerCast<VMAddress>(&sanitization_info);
|
||||
}
|
||||
|
||||
ExceptionHandlerClient handler_client(state->client_sock_);
|
||||
ExceptionHandlerClient handler_client(state->client_sock_, false);
|
||||
CHECK_EQ(handler_client.RequestCrashDump(info), 0);
|
||||
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "util/mac/mac_util.h"
|
||||
#include "util/mach/bootstrap.h"
|
||||
#include "util/mach/child_port_handshake.h"
|
||||
#include "util/mach/exception_ports.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
@ -338,6 +339,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface {
|
||||
// this interface.
|
||||
if (!DoubleForkAndExec(
|
||||
argv,
|
||||
nullptr,
|
||||
server_write_fd.get(),
|
||||
true,
|
||||
restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
@ -35,10 +36,12 @@
|
||||
#include "util/misc/random_string.h"
|
||||
#include "util/win/address_types.h"
|
||||
#include "util/win/command_line.h"
|
||||
#include "util/win/context_wrappers.h"
|
||||
#include "util/win/critical_section_with_debug_info.h"
|
||||
#include "util/win/get_function.h"
|
||||
#include "util/win/handle.h"
|
||||
#include "util/win/initial_client_data.h"
|
||||
#include "util/win/loader_lock.h"
|
||||
#include "util/win/nt_internals.h"
|
||||
#include "util/win/ntstatus_logging.h"
|
||||
#include "util/win/process_info.h"
|
||||
@ -73,10 +76,10 @@ base::Lock* g_non_crash_dump_lock;
|
||||
ExceptionInformation g_non_crash_exception_information;
|
||||
|
||||
enum class StartupState : int {
|
||||
kNotReady = 0, // This must be value 0 because it is the initial value of a
|
||||
// global AtomicWord.
|
||||
kNotReady = 0, // This must be value 0 because it is the initial value of a
|
||||
// global AtomicWord.
|
||||
kSucceeded = 1, // The CreateProcess() for the handler succeeded.
|
||||
kFailed = 2, // The handler failed to start.
|
||||
kFailed = 2, // The handler failed to start.
|
||||
};
|
||||
|
||||
// This is a tri-state of type StartupState. It starts at 0 == kNotReady, and
|
||||
@ -93,9 +96,8 @@ base::subtle::AtomicWord g_handler_startup_state;
|
||||
CRITICAL_SECTION g_critical_section_with_debug_info;
|
||||
|
||||
void SetHandlerStartupState(StartupState state) {
|
||||
DCHECK(state == StartupState::kSucceeded ||
|
||||
state == StartupState::kFailed);
|
||||
base::subtle::Acquire_Store(&g_handler_startup_state,
|
||||
DCHECK(state == StartupState::kSucceeded || state == StartupState::kFailed);
|
||||
base::subtle::Release_Store(&g_handler_startup_state,
|
||||
static_cast<base::subtle::AtomicWord>(state));
|
||||
}
|
||||
|
||||
@ -103,7 +105,7 @@ StartupState BlockUntilHandlerStartedOrFailed() {
|
||||
// Wait until we know the handler has either succeeded or failed to start.
|
||||
base::subtle::AtomicWord startup_state;
|
||||
while (
|
||||
(startup_state = base::subtle::Release_Load(&g_handler_startup_state)) ==
|
||||
(startup_state = base::subtle::Acquire_Load(&g_handler_startup_state)) ==
|
||||
static_cast<int>(StartupState::kNotReady)) {
|
||||
Sleep(1);
|
||||
}
|
||||
@ -187,11 +189,7 @@ void HandleAbortSignal(int signum) {
|
||||
EXCEPTION_RECORD record = {};
|
||||
record.ExceptionCode = STATUS_FATAL_APP_EXIT;
|
||||
record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
|
||||
#else
|
||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);
|
||||
|
||||
EXCEPTION_POINTERS exception_pointers;
|
||||
exception_pointers.ContextRecord = &context;
|
||||
@ -349,6 +347,8 @@ class ScopedCallSetHandlerStartupState {
|
||||
|
||||
bool StartHandlerProcess(
|
||||
std::unique_ptr<BackgroundHandlerStartThreadData> data) {
|
||||
CHECK(!IsThreadInLoaderLock());
|
||||
|
||||
ScopedCallSetHandlerStartupState scoped_startup_state_caller;
|
||||
|
||||
std::wstring command_line;
|
||||
@ -476,17 +476,34 @@ bool StartHandlerProcess(
|
||||
}
|
||||
}
|
||||
|
||||
// If the embedded crashpad handler is being started via an entry point in a
|
||||
// DLL (the handler executable is rundll32.exe), then don't pass
|
||||
// the application name to CreateProcess as this appears to generate an
|
||||
// invalid command line where the first argument needed by rundll32 is not in
|
||||
// the correct format as required in:
|
||||
// https://support.microsoft.com/en-ca/help/164787/info-windows-rundll-and-rundll32-interface
|
||||
const base::StringPiece16 kRunDll32Exe(L"rundll32.exe");
|
||||
bool is_embedded_in_dll = false;
|
||||
if (data->handler.value().size() >= kRunDll32Exe.size() &&
|
||||
_wcsicmp(data->handler.value()
|
||||
.substr(data->handler.value().size() - kRunDll32Exe.size())
|
||||
.c_str(),
|
||||
kRunDll32Exe.data()) == 0) {
|
||||
is_embedded_in_dll = true;
|
||||
}
|
||||
|
||||
PROCESS_INFORMATION process_info;
|
||||
rv = CreateProcess(data->handler.value().c_str(),
|
||||
&command_line[0],
|
||||
nullptr,
|
||||
nullptr,
|
||||
true,
|
||||
creation_flags,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&startup_info.StartupInfo,
|
||||
&process_info);
|
||||
rv = CreateProcess(
|
||||
is_embedded_in_dll ? nullptr : data->handler.value().c_str(),
|
||||
&command_line[0],
|
||||
nullptr,
|
||||
nullptr,
|
||||
true,
|
||||
creation_flags,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&startup_info.StartupInfo,
|
||||
&process_info);
|
||||
if (!rv) {
|
||||
PLOG(ERROR) << "CreateProcess";
|
||||
return false;
|
||||
@ -756,11 +773,7 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
|
||||
constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed;
|
||||
EXCEPTION_RECORD record = {};
|
||||
record.ExceptionCode = kSimulatedExceptionCode;
|
||||
#if defined(ARCH_CPU_64_BITS)
|
||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
|
||||
#else
|
||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
|
||||
#endif // ARCH_CPU_64_BITS
|
||||
record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);
|
||||
|
||||
exception_pointers.ExceptionRecord = &record;
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/logging.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/test_paths.h"
|
||||
|
@ -141,15 +141,15 @@ struct CrashpadInfo {
|
||||
//!
|
||||
//! When handling an exception, the Crashpad handler will scan all modules in
|
||||
//! a process. The first one that has a CrashpadInfo structure populated with
|
||||
//! a value other than #kUnset for this field will dictate whether the handler
|
||||
//! is functional or not. If all modules with a CrashpadInfo structure specify
|
||||
//! #kUnset, the handler will be enabled. If disabled, the Crashpad handler
|
||||
//! will still run and receive exceptions, but will not take any action on an
|
||||
//! exception on its own behalf, except for the action necessary to determine
|
||||
//! that it has been disabled.
|
||||
//! a value other than TriState::kUnset for this field will dictate whether
|
||||
//! the handler is functional or not. If all modules with a CrashpadInfo
|
||||
//! structure specify TriState::kUnset, the handler will be enabled. If
|
||||
//! disabled, the Crashpad handler will still run and receive exceptions, but
|
||||
//! will not take any action on an exception on its own behalf, except for the
|
||||
//! action necessary to determine that it has been disabled.
|
||||
//!
|
||||
//! The Crashpad handler should not normally be disabled. More commonly, it
|
||||
//! is appropriate to disable crash report upload by calling
|
||||
//! The Crashpad handler should not normally be disabled. More commonly, it is
|
||||
//! appropriate to disable crash report upload by calling
|
||||
//! Settings::SetUploadsEnabled().
|
||||
void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {
|
||||
crashpad_handler_behavior_ = crashpad_handler_behavior;
|
||||
@ -160,15 +160,15 @@ struct CrashpadInfo {
|
||||
//!
|
||||
//! When handling an exception, the Crashpad handler will scan all modules in
|
||||
//! a process. The first one that has a CrashpadInfo structure populated with
|
||||
//! a value other than #kUnset for this field will dictate whether the
|
||||
//! exception is forwarded to the system’s crash reporter. If all modules with
|
||||
//! a CrashpadInfo structure specify #kUnset, forwarding will be enabled.
|
||||
//! Unless disabled, forwarding may still occur if the Crashpad handler is
|
||||
//! disabled by SetCrashpadHandlerState(). Even when forwarding is enabled,
|
||||
//! the Crashpad handler may choose not to forward all exceptions to the
|
||||
//! system’s crash reporter in cases where it has reason to believe that the
|
||||
//! system’s crash reporter would not normally have handled the exception in
|
||||
//! Crashpad’s absence.
|
||||
//! a value other than TriState::kUnset for this field will dictate whether
|
||||
//! the exception is forwarded to the system’s crash reporter. If all modules
|
||||
//! with a CrashpadInfo structure specify TriState::kUnset, forwarding will be
|
||||
//! enabled. Unless disabled, forwarding may still occur if the Crashpad
|
||||
//! handler is disabled by SetCrashpadHandlerState(). Even when forwarding is
|
||||
//! enabled, the Crashpad handler may choose not to forward all exceptions to
|
||||
//! the system’s crash reporter in cases where it has reason to believe that
|
||||
//! the system’s crash reporter would not normally have handled the exception
|
||||
//! in Crashpad’s absence.
|
||||
void set_system_crash_reporter_forwarding(
|
||||
TriState system_crash_reporter_forwarding) {
|
||||
system_crash_reporter_forwarding_ = system_crash_reporter_forwarding;
|
||||
@ -179,8 +179,8 @@ struct CrashpadInfo {
|
||||
//!
|
||||
//! When handling an exception, the Crashpad handler will scan all modules in
|
||||
//! a process. The first one that has a CrashpadInfo structure populated with
|
||||
//! a value other than #kUnset for this field will dictate whether the extra
|
||||
//! memory is captured.
|
||||
//! a value other than TriState::kUnset for this field will dictate whether
|
||||
//! the extra memory is captured.
|
||||
//!
|
||||
//! This causes Crashpad to include pages of data referenced by locals or
|
||||
//! other stack memory. Turning this on can increase the size of the minidump
|
||||
@ -208,7 +208,7 @@ struct CrashpadInfo {
|
||||
//! Note that streams will appear in the minidump in the reverse order to
|
||||
//! which they are added.
|
||||
//!
|
||||
//! TODO(scottmg) This is currently only supported on Windows.
|
||||
//! TODO(scottmg) This is currently not supported on Mac.
|
||||
//!
|
||||
//! \param[in] stream_type The stream type identifier to use. This should be
|
||||
//! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream`
|
||||
|
@ -26,16 +26,13 @@
|
||||
#define NOTE_ALIGN 4
|
||||
|
||||
// This section must be "a"llocated so that it appears in the final binary at
|
||||
// runtime, and "w"ritable so that the relocation to CRASHPAD_INFO_SYMBOL can
|
||||
// be performed.
|
||||
.section .note.crashpad.info,"aw",%note
|
||||
// runtime. The reference to CRASHPAD_INFO_SYMBOL uses an offset relative to
|
||||
// this note to avoid making this note writable, which triggers a bug in GNU
|
||||
// ld, or adding text relocations which require the target system to allow
|
||||
// making text segments writable. https://crbug.com/crashpad/260.
|
||||
.section .note.crashpad.info,"a",%note
|
||||
.balign NOTE_ALIGN
|
||||
# .globl indicates that it's available to link against other .o files. .hidden
|
||||
# indicates that it will not appear in the executable's symbol table.
|
||||
.globl CRASHPAD_NOTE_REFERENCE
|
||||
.hidden CRASHPAD_NOTE_REFERENCE
|
||||
.type CRASHPAD_NOTE_REFERENCE, %object
|
||||
CRASHPAD_NOTE_REFERENCE:
|
||||
CRASHPAD_NOTE:
|
||||
.long name_end - name // namesz
|
||||
.long desc_end - desc // descsz
|
||||
.long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type
|
||||
@ -45,15 +42,26 @@ name_end:
|
||||
.balign NOTE_ALIGN
|
||||
desc:
|
||||
#if defined(__LP64__)
|
||||
.quad CRASHPAD_INFO_SYMBOL
|
||||
.quad CRASHPAD_INFO_SYMBOL - desc
|
||||
#else
|
||||
#if defined(__LITTLE_ENDIAN__)
|
||||
.long CRASHPAD_INFO_SYMBOL
|
||||
.long 0
|
||||
#else
|
||||
.long 0
|
||||
.long CRASHPAD_INFO_SYMBOL
|
||||
#endif // __LITTLE_ENDIAN__
|
||||
.long CRASHPAD_INFO_SYMBOL - desc
|
||||
#endif // __LP64__
|
||||
desc_end:
|
||||
.size CRASHPAD_NOTE_REFERENCE, .-CRASHPAD_NOTE_REFERENCE
|
||||
.size CRASHPAD_NOTE, .-CRASHPAD_NOTE
|
||||
|
||||
// CRASHPAD_NOTE can't be referenced directly by GetCrashpadInfo() because the
|
||||
// relocation used to make the reference may require that the address be
|
||||
// 8-byte aligned and notes must have 4-byte alignment.
|
||||
.section .rodata,"a",%progbits
|
||||
.balign 8
|
||||
# .globl indicates that it's available to link against other .o files. .hidden
|
||||
# indicates that it will not appear in the executable's symbol table.
|
||||
.globl CRASHPAD_NOTE_REFERENCE
|
||||
.hidden CRASHPAD_NOTE_REFERENCE
|
||||
.type CRASHPAD_NOTE_REFERENCE, %object
|
||||
CRASHPAD_NOTE_REFERENCE:
|
||||
// The value of this quad isn't important. It exists to reference
|
||||
// CRASHPAD_NOTE, causing the linker to include the note into the binary
|
||||
// linking Crashpad. The subtraction from |name| is a convenience to allow the
|
||||
// value to be computed statically.
|
||||
.quad name - CRASHPAD_NOTE
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "client/prune_crash_reports.h"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
@ -24,7 +25,7 @@
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
void PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||
size_t PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||
PruneCondition* condition) {
|
||||
std::vector<CrashReportDatabase::Report> all_reports;
|
||||
CrashReportDatabase::OperationStatus status;
|
||||
@ -32,14 +33,14 @@ void PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||
status = database->GetPendingReports(&all_reports);
|
||||
if (status != CrashReportDatabase::kNoError) {
|
||||
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get pending reports";
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<CrashReportDatabase::Report> completed_reports;
|
||||
status = database->GetCompletedReports(&completed_reports);
|
||||
if (status != CrashReportDatabase::kNoError) {
|
||||
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get completed reports";
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
all_reports.insert(all_reports.end(), completed_reports.begin(),
|
||||
completed_reports.end());
|
||||
@ -50,16 +51,21 @@ void PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||
return lhs.creation_time > rhs.creation_time;
|
||||
});
|
||||
|
||||
size_t num_pruned = 0;
|
||||
for (const auto& report : all_reports) {
|
||||
if (condition->ShouldPruneReport(report)) {
|
||||
status = database->DeleteReport(report.uuid);
|
||||
if (status != CrashReportDatabase::kNoError) {
|
||||
LOG(ERROR) << "Database Pruning: Failed to remove report "
|
||||
<< report.uuid.ToString();
|
||||
} else {
|
||||
num_pruned++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return num_pruned;
|
||||
|
||||
// TODO(rsesek): For databases that do not use a directory structure, it is
|
||||
// possible for the metadata sidecar to become corrupted and thus leave
|
||||
// orphaned crash report files on-disk. https://crashpad.chromium.org/bug/66
|
||||
@ -96,19 +102,9 @@ DatabaseSizePruneCondition::~DatabaseSizePruneCondition() {}
|
||||
|
||||
bool DatabaseSizePruneCondition::ShouldPruneReport(
|
||||
const CrashReportDatabase::Report& report) {
|
||||
#if defined(OS_POSIX)
|
||||
struct stat statbuf;
|
||||
if (stat(report.file_path.value().c_str(), &statbuf) == 0) {
|
||||
#elif defined(OS_WIN)
|
||||
struct _stati64 statbuf;
|
||||
if (_wstat64(report.file_path.value().c_str(), &statbuf) == 0) {
|
||||
#else
|
||||
#error "Not implemented"
|
||||
#endif
|
||||
// Round up fractional KB to the next 1-KB boundary.
|
||||
measured_size_in_kb_ +=
|
||||
static_cast<size_t>((statbuf.st_size + 1023) / 1024);
|
||||
}
|
||||
// Round up fractional KB to the next 1-KB boundary.
|
||||
measured_size_in_kb_ +=
|
||||
static_cast<size_t>((report.total_size + 1023) / 1024);
|
||||
return measured_size_in_kb_ > max_size_in_kb_;
|
||||
}
|
||||
|
||||
|
@ -37,8 +37,10 @@ class PruneCondition;
|
||||
//! \param[in] database The database from which crash reports will be deleted.
|
||||
//! \param[in] condition The condition against which all reports in the database
|
||||
//! will be evaluated.
|
||||
void PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||
PruneCondition* condition);
|
||||
//!
|
||||
//! \return The number of deleted crash reports.
|
||||
size_t PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||
PruneCondition* condition);
|
||||
|
||||
std::unique_ptr<PruneCondition> GetDefaultDatabasePruneCondition();
|
||||
|
||||
|
@ -15,14 +15,15 @@
|
||||
#include "client/prune_crash_reports.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/rand_util.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "test/scoped_temp_dir.h"
|
||||
@ -81,56 +82,49 @@ TEST(PruneCrashReports, AgeCondition) {
|
||||
}
|
||||
|
||||
TEST(PruneCrashReports, SizeCondition) {
|
||||
ScopedTempDir temp_dir;
|
||||
|
||||
CrashReportDatabase::Report report_1k;
|
||||
report_1k.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file1024"));
|
||||
report_1k.total_size = 1024u;
|
||||
CrashReportDatabase::Report report_3k;
|
||||
report_3k.file_path = temp_dir.path().Append(FILE_PATH_LITERAL("file3072"));
|
||||
report_3k.total_size = 1024u * 3u;
|
||||
CrashReportDatabase::Report report_unset_size;
|
||||
|
||||
{
|
||||
ScopedFileHandle scoped_file_1k(
|
||||
LoggingOpenFileForWrite(report_1k.file_path,
|
||||
FileWriteMode::kCreateOrFail,
|
||||
FilePermissions::kOwnerOnly));
|
||||
ASSERT_TRUE(scoped_file_1k.is_valid());
|
||||
|
||||
std::string string;
|
||||
for (int i = 0; i < 128; ++i)
|
||||
string.push_back(static_cast<char>(i));
|
||||
|
||||
for (size_t i = 0; i < 1024; i += string.size()) {
|
||||
ASSERT_TRUE(LoggingWriteFile(scoped_file_1k.get(),
|
||||
string.c_str(), string.length()));
|
||||
}
|
||||
|
||||
ScopedFileHandle scoped_file_3k(
|
||||
LoggingOpenFileForWrite(report_3k.file_path,
|
||||
FileWriteMode::kCreateOrFail,
|
||||
FilePermissions::kOwnerOnly));
|
||||
ASSERT_TRUE(scoped_file_3k.is_valid());
|
||||
|
||||
for (size_t i = 0; i < 3072; i += string.size()) {
|
||||
ASSERT_TRUE(LoggingWriteFile(scoped_file_3k.get(),
|
||||
string.c_str(), string.length()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
DatabaseSizePruneCondition condition(1);
|
||||
DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1);
|
||||
// |report_1k| should not be pruned as the cumulated size is not past 1kB
|
||||
// yet.
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_1k));
|
||||
EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
|
||||
}
|
||||
|
||||
{
|
||||
DatabaseSizePruneCondition condition(1);
|
||||
// |report_3k| should be pruned as the cumulated size is now past 1kB.
|
||||
EXPECT_TRUE(condition.ShouldPruneReport(report_3k));
|
||||
}
|
||||
|
||||
{
|
||||
DatabaseSizePruneCondition condition(6);
|
||||
DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1);
|
||||
// |report_3k| should be pruned as the cumulated size is already past 1kB.
|
||||
EXPECT_TRUE(condition.ShouldPruneReport(report_3k));
|
||||
}
|
||||
|
||||
{
|
||||
DatabaseSizePruneCondition condition(/*max_size_in_kb=*/6);
|
||||
// |report_3k| should not be pruned as the cumulated size is not past 6kB
|
||||
// yet.
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_3k));
|
||||
// |report_3k| should not be pruned as the cumulated size is not past 6kB
|
||||
// yet.
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_3k));
|
||||
// |report_1k| should be pruned as the cumulated size is now past 6kB.
|
||||
EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
|
||||
}
|
||||
|
||||
{
|
||||
DatabaseSizePruneCondition condition(/*max_size_in_kb=*/0);
|
||||
// |report_unset_size| should not be pruned as its size is 0, regardless of
|
||||
// how many times we try to prune it.
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
|
||||
EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size));
|
||||
// |report_1k| should be pruned as the cumulated size is now past 0kB.
|
||||
EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
|
||||
}
|
||||
}
|
||||
@ -211,18 +205,16 @@ TEST(PruneCrashReports, PruneOrder) {
|
||||
using ::testing::Return;
|
||||
using ::testing::SetArgPointee;
|
||||
|
||||
const size_t kNumReports = 10;
|
||||
std::vector<CrashReportDatabase::Report> reports;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
for (size_t i = 0; i < kNumReports; ++i) {
|
||||
CrashReportDatabase::Report temp;
|
||||
temp.uuid.data_1 = i;
|
||||
temp.creation_time = NDaysAgo(i * 10);
|
||||
temp.uuid.data_1 = static_cast<uint32_t>(i);
|
||||
temp.creation_time = NDaysAgo(static_cast<int>(i) * 10);
|
||||
reports.push_back(temp);
|
||||
}
|
||||
// The randomness from std::rand() is not, so use a better rand() instead.
|
||||
const auto random_generator = [](ptrdiff_t rand_max) {
|
||||
return base::RandInt(0, base::checked_cast<int>(rand_max) - 1);
|
||||
};
|
||||
std::random_shuffle(reports.begin(), reports.end(), random_generator);
|
||||
std::mt19937 urng(std::random_device{}());
|
||||
std::shuffle(reports.begin(), reports.end(), urng);
|
||||
std::vector<CrashReportDatabase::Report> pending_reports(
|
||||
reports.begin(), reports.begin() + 5);
|
||||
std::vector<CrashReportDatabase::Report> completed_reports(
|
||||
@ -241,7 +233,7 @@ TEST(PruneCrashReports, PruneOrder) {
|
||||
}
|
||||
|
||||
StaticCondition delete_all(true);
|
||||
PruneCrashReportDatabase(&db, &delete_all);
|
||||
EXPECT_EQ(PruneCrashReportDatabase(&db, &delete_all), kNumReports);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -63,7 +63,8 @@ void Settings::ScopedLockedFileHandle::Destroy() {
|
||||
CheckedCloseFile(handle_);
|
||||
}
|
||||
if (!lockfile_path_.empty()) {
|
||||
DCHECK(LoggingRemoveFile(lockfile_path_));
|
||||
const bool success = LoggingRemoveFile(lockfile_path_);
|
||||
DCHECK(success);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,8 +85,8 @@ void ScopedLockedFileHandleTraits::Free(FileHandle handle) {
|
||||
#endif // OS_FUCHSIA
|
||||
|
||||
struct Settings::Data {
|
||||
static const uint32_t kSettingsMagic = 'CPds';
|
||||
static const uint32_t kSettingsVersion = 1;
|
||||
static constexpr uint32_t kSettingsMagic = 'CPds';
|
||||
static constexpr uint32_t kSettingsVersion = 1;
|
||||
|
||||
enum Options : uint32_t {
|
||||
kUploadsEnabled = 1 << 0,
|
||||
@ -225,10 +226,10 @@ Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting(
|
||||
FileHandle handle;
|
||||
if (log_open_error) {
|
||||
handle = LoggingOpenFileForReadAndWrite(
|
||||
file_path(), mode, FilePermissions::kWorldReadable);
|
||||
file_path(), mode, FilePermissions::kOwnerOnly);
|
||||
} else {
|
||||
handle = OpenFileForReadAndWrite(
|
||||
file_path(), mode, FilePermissions::kWorldReadable);
|
||||
file_path(), mode, FilePermissions::kOwnerOnly);
|
||||
}
|
||||
|
||||
return MakeScopedLockedFileHandle(
|
||||
|
@ -75,6 +75,10 @@ class Settings {
|
||||
//!
|
||||
//! The default value is `false`.
|
||||
//!
|
||||
//! \note
|
||||
//! This setting is ignored if --use-cros-crash-reporter is present
|
||||
//! (which it will be if invoked by Chrome on ChromeOS).
|
||||
//!
|
||||
//! \param[out] enabled Whether crash reports should be uploaded.
|
||||
//!
|
||||
//! \return On success, returns `true`, otherwise returns `false` with an
|
||||
|
@ -73,7 +73,7 @@ TEST(SimpleStringDictionary, SimpleStringDictionary) {
|
||||
EXPECT_FALSE(dict.GetValueForKey("key3"));
|
||||
|
||||
// Remove by setting value to nullptr
|
||||
dict.SetKeyValue("key2", nullptr);
|
||||
dict.SetKeyValue("key2", base::StringPiece(nullptr, 0));
|
||||
|
||||
// Now make sure it's not there anymore
|
||||
EXPECT_FALSE(dict.GetValueForKey("key2"));
|
||||
@ -254,13 +254,14 @@ TEST(SimpleStringDictionary, OutOfSpace) {
|
||||
|
||||
TEST(SimpleStringDictionaryDeathTest, SetKeyValueWithNullKey) {
|
||||
TSimpleStringDictionary<4, 6, 6> map;
|
||||
ASSERT_DEATH_CHECK(map.SetKeyValue(nullptr, "hello"), "key");
|
||||
ASSERT_DEATH_CHECK(map.SetKeyValue(base::StringPiece(nullptr, 0), "hello"),
|
||||
"key");
|
||||
}
|
||||
|
||||
TEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithNullKey) {
|
||||
TSimpleStringDictionary<4, 6, 6> map;
|
||||
map.SetKeyValue("hi", "there");
|
||||
ASSERT_DEATH_CHECK(map.GetValueForKey(nullptr), "key");
|
||||
ASSERT_DEATH_CHECK(map.GetValueForKey(base::StringPiece(nullptr, 0)), "key");
|
||||
EXPECT_STREQ("there", map.GetValueForKey("hi"));
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/mac/scoped_mach_port.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "build/build_config.h"
|
||||
#include "util/mach/exc_client_variants.h"
|
||||
@ -191,7 +191,7 @@ void SimulateCrash(const NativeCPUContext& cpu_context) {
|
||||
base::mac::ScopedMachSendRight thread(mach_thread_self());
|
||||
exception_type_t exception = kMachExceptionSimulated;
|
||||
mach_exception_data_type_t codes[] = {0, 0};
|
||||
mach_msg_type_number_t code_count = arraysize(codes);
|
||||
mach_msg_type_number_t code_count = base::size(codes);
|
||||
|
||||
// Look up the handler for EXC_CRASH exceptions in the same way that the
|
||||
// kernel would: try a thread handler, then a task handler, and finally a host
|
||||
@ -213,7 +213,7 @@ void SimulateCrash(const NativeCPUContext& cpu_context) {
|
||||
bool success = false;
|
||||
|
||||
for (size_t target_type_index = 0;
|
||||
!success && target_type_index < arraysize(kTargetTypes);
|
||||
!success && target_type_index < base::size(kTargetTypes);
|
||||
++target_type_index) {
|
||||
ExceptionPorts::ExceptionHandlerVector handlers;
|
||||
ExceptionPorts exception_ports(kTargetTypes[target_type_index],
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
@ -342,15 +343,13 @@ TEST(SimulateCrash, SimulateCrash) {
|
||||
#endif
|
||||
};
|
||||
|
||||
for (size_t target_index = 0;
|
||||
target_index < arraysize(kTargets);
|
||||
for (size_t target_index = 0; target_index < base::size(kTargets);
|
||||
++target_index) {
|
||||
TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
|
||||
SCOPED_TRACE(base::StringPrintf(
|
||||
"target_index %zu, target %d", target_index, target));
|
||||
|
||||
for (size_t behavior_index = 0;
|
||||
behavior_index < arraysize(kBehaviors);
|
||||
for (size_t behavior_index = 0; behavior_index < base::size(kBehaviors);
|
||||
++behavior_index) {
|
||||
exception_behavior_t behavior = kBehaviors[behavior_index];
|
||||
SCOPED_TRACE(base::StringPrintf(
|
||||
@ -364,8 +363,7 @@ TEST(SimulateCrash, SimulateCrash) {
|
||||
target, behavior, THREAD_STATE_NONE);
|
||||
test_simulate_crash_mac.Run();
|
||||
} else {
|
||||
for (size_t flavor_index = 0;
|
||||
flavor_index < arraysize(kFlavors);
|
||||
for (size_t flavor_index = 0; flavor_index < base::size(kFlavors);
|
||||
++flavor_index) {
|
||||
thread_state_flavor_t flavor = kFlavors[flavor_index];
|
||||
SCOPED_TRACE(base::StringPrintf(
|
||||
|
@ -13,7 +13,6 @@
|
||||
# limitations under the License.
|
||||
|
||||
GERRIT_HOST: True
|
||||
GERRIT_SQUASH_UPLOADS: True
|
||||
CODE_REVIEW_SERVER: https://chromium-review.googlesource.com/
|
||||
VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/
|
||||
PROJECT: crashpad
|
||||
|
@ -17,8 +17,14 @@ import("../build/crashpad_buildconfig.gni")
|
||||
config("compat_config") {
|
||||
include_dirs = []
|
||||
|
||||
if (crashpad_is_mac) {
|
||||
if (crashpad_is_mac || crashpad_is_ios) {
|
||||
include_dirs += [ "mac" ]
|
||||
} else {
|
||||
include_dirs += [ "non_mac" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_ios) {
|
||||
include_dirs += [ "ios" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
@ -34,10 +40,14 @@ config("compat_config") {
|
||||
} else {
|
||||
include_dirs += [ "non_win" ]
|
||||
}
|
||||
|
||||
if (!crashpad_is_linux && !crashpad_is_android && !crashpad_is_fuchsia) {
|
||||
include_dirs += [ "non_elf" ]
|
||||
}
|
||||
}
|
||||
|
||||
template("compat_target") {
|
||||
if (crashpad_is_mac) {
|
||||
if (crashpad_is_mac || crashpad_is_ios) {
|
||||
# There are no sources to compile, which doesn’t mix will with a
|
||||
# static_library.
|
||||
group(target_name) {
|
||||
@ -53,21 +63,39 @@ template("compat_target") {
|
||||
compat_target("compat") {
|
||||
sources = []
|
||||
|
||||
if (crashpad_is_mac) {
|
||||
if (crashpad_is_mac || crashpad_is_ios) {
|
||||
sources += [
|
||||
"mac/AvailabilityMacros.h",
|
||||
"mac/kern/exc_resource.h",
|
||||
"mac/mach-o/loader.h",
|
||||
"mac/mach/i386/thread_state.h",
|
||||
"mac/mach/mach.h",
|
||||
"mac/sys/resource.h",
|
||||
]
|
||||
} else {
|
||||
sources += [ "non_mac/mach/mach.h" ]
|
||||
sources += [
|
||||
"non_mac/mach-o/loader.h",
|
||||
"non_mac/mach/mach.h",
|
||||
"non_mac/mach/machine.h",
|
||||
"non_mac/mach/vm_prot.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_ios) {
|
||||
sources += [
|
||||
"ios/mach/exc.defs",
|
||||
"ios/mach/mach_exc.defs",
|
||||
"ios/mach/mach_types.defs",
|
||||
"ios/mach/machine/machine_types.defs",
|
||||
"ios/mach/std_types.defs",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
sources += [
|
||||
"linux/signal.h",
|
||||
"linux/sys/mman.cc",
|
||||
"linux/sys/mman.h",
|
||||
"linux/sys/ptrace.h",
|
||||
"linux/sys/user.h",
|
||||
]
|
||||
@ -75,6 +103,8 @@ compat_target("compat") {
|
||||
|
||||
if (crashpad_is_android) {
|
||||
sources += [
|
||||
"android/android/api-level.cc",
|
||||
"android/android/api-level.h",
|
||||
"android/dlfcn_internal.cc",
|
||||
"android/dlfcn_internal.h",
|
||||
"android/elf.h",
|
||||
@ -114,6 +144,10 @@ compat_target("compat") {
|
||||
]
|
||||
}
|
||||
|
||||
if (!crashpad_is_linux && !crashpad_is_android && !crashpad_is_fuchsia) {
|
||||
sources += [ "non_elf/elf.h" ]
|
||||
}
|
||||
|
||||
public_configs = [
|
||||
":compat_config",
|
||||
"..:crashpad_config",
|
||||
@ -121,7 +155,15 @@ compat_target("compat") {
|
||||
|
||||
deps = []
|
||||
|
||||
if (!crashpad_is_mac) {
|
||||
deps += [ "../third_party/xnu" ]
|
||||
}
|
||||
|
||||
if (crashpad_is_win) {
|
||||
deps += [ "../third_party/getopt" ]
|
||||
}
|
||||
|
||||
if (!crashpad_is_linux && !crashpad_is_android && !crashpad_is_fuchsia) {
|
||||
deps += [ "../third_party/glibc" ]
|
||||
}
|
||||
}
|
||||
|
50
compat/android/android/api-level.cc
Normal file
50
compat/android/android/api-level.cc
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2018 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <android/api-level.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
#include "dlfcn_internal.h"
|
||||
|
||||
#if __NDK_MAJOR__ < 20
|
||||
|
||||
extern "C" {
|
||||
|
||||
int android_get_device_api_level() {
|
||||
using FuncType = int (*)();
|
||||
static const FuncType bionic_get_device_api_level =
|
||||
reinterpret_cast<FuncType>(
|
||||
crashpad::internal::Dlsym(RTLD_NEXT, "android_get_device_api_level"));
|
||||
|
||||
if (bionic_get_device_api_level) {
|
||||
return bionic_get_device_api_level();
|
||||
}
|
||||
|
||||
char api_string[PROP_VALUE_MAX];
|
||||
int length = __system_property_get("ro.build.version.sdk", api_string);
|
||||
if (length <= 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int api_level = atoi(api_string);
|
||||
return api_level > 0 ? api_level : -1;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // __NDK_MAJOR__ < 20
|
39
compat/android/android/api-level.h
Normal file
39
compat/android/android/api-level.h
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright 2018 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_
|
||||
#define CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_
|
||||
|
||||
#include_next <android/api-level.h>
|
||||
#include <android/ndk-version.h>
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#if __NDK_MAJOR__ < 20
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Returns the API level of the device or -1 if it can't be determined. This
|
||||
// function is provided by NDK r20.
|
||||
int android_get_device_api_level();
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __NDK_MAJOR__ < 20
|
||||
|
||||
#endif // CRASHPAD_COMPAT_ANDROID_ANDROID_API_LEVEL_H_
|
@ -35,7 +35,7 @@ extern "C" void* __mmap2(void* addr,
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
T Align(T value, uint8_t alignment) {
|
||||
T Align(T value, size_t alignment) {
|
||||
return (value + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,8 @@
|
||||
{
|
||||
'target_name': 'crashpad_compat',
|
||||
'sources': [
|
||||
'android/android/api-level.cc',
|
||||
'android/android/api-level.h',
|
||||
'android/dlfcn_internal.cc',
|
||||
'android/dlfcn_internal.h',
|
||||
'android/elf.h',
|
||||
|
20
compat/ios/mach/exc.defs
Normal file
20
compat/ios/mach/exc.defs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_
|
||||
#define CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_
|
||||
|
||||
#include "third_party/xnu/osfmk/mach/exc.defs"
|
||||
|
||||
#endif // CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_
|
20
compat/ios/mach/mach_exc.defs
Normal file
20
compat/ios/mach/mach_exc.defs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_
|
||||
#define CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_
|
||||
|
||||
#include "third_party/xnu/osfmk/mach/mach_exc.defs"
|
||||
|
||||
#endif // CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_
|
20
compat/ios/mach/mach_types.defs
Normal file
20
compat/ios/mach/mach_types.defs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_
|
||||
#define CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_
|
||||
|
||||
#include "third_party/xnu/osfmk/mach/mach_types.defs"
|
||||
|
||||
#endif // CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2016 The Crashpad Authors. All rights reserved.
|
||||
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -12,16 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
|
||||
#define CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
|
||||
#ifndef CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
|
||||
#define CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
|
||||
|
||||
//! \file
|
||||
#include "third_party/xnu/osfmk/mach/machine/machine_types.defs"
|
||||
|
||||
//! \brief Not the safest way of computing an array’s size…
|
||||
//!
|
||||
//! `#%include "base/macros.h"` and use its `arraysize()` instead. This macro
|
||||
//! should only be used in rare situations where `arraysize()` does not
|
||||
//! function.
|
||||
#define ARRAYSIZE_UNSAFE(array) (sizeof(array) / sizeof(array[0]))
|
||||
|
||||
#endif // CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
|
||||
#endif // CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
|
20
compat/ios/mach/std_types.defs
Normal file
20
compat/ios/mach/std_types.defs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2020 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_
|
||||
#define CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_
|
||||
|
||||
#include "third_party/xnu/osfmk/mach/std_types.defs"
|
||||
|
||||
#endif // CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_
|
35
compat/linux/sys/mman.cc
Normal file
35
compat/linux/sys/mman.cc
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if defined(__GLIBC__)
|
||||
|
||||
extern "C" {
|
||||
|
||||
int memfd_create(const char* name, unsigned int flags) {
|
||||
using MemfdCreateType = int (*)(const char*, int);
|
||||
static const MemfdCreateType next_memfd_create =
|
||||
reinterpret_cast<MemfdCreateType>(dlsym(RTLD_NEXT, "memfd_create"));
|
||||
return next_memfd_create ? next_memfd_create(name, flags)
|
||||
: syscall(SYS_memfd_create, name, flags);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // __GLIBC__
|
40
compat/linux/sys/mman.h
Normal file
40
compat/linux/sys/mman.h
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
|
||||
#define CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
|
||||
|
||||
#include_next <sys/mman.h>
|
||||
|
||||
#include <features.h>
|
||||
|
||||
// There's no memfd_create() wrapper before glibc 2.27.
|
||||
// This can't select for glibc < 2.27 because linux-chromeos-rel bots build this
|
||||
// code using a sysroot which has glibc 2.27, but then run it on Ubuntu 16.04,
|
||||
// which doesn't.
|
||||
#if defined(__GLIBC__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int memfd_create(const char* name, unsigned int flags);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __GLIBC__
|
||||
|
||||
#endif // CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_
|
@ -26,7 +26,8 @@
|
||||
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
|
||||
static_cast<__ptrace_request>(25);
|
||||
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=873168
|
||||
#elif defined(__arm__) || (defined(__aarch64__) && __GLIBC_PREREQ(2,28))
|
||||
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
|
||||
static_cast<__ptrace_request>(22);
|
||||
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
|
||||
|
@ -59,4 +59,16 @@
|
||||
#define MAC_OS_X_VERSION_10_13 101300
|
||||
#endif
|
||||
|
||||
// 10.14 SDK
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_14
|
||||
#define MAC_OS_X_VERSION_10_14 101400
|
||||
#endif
|
||||
|
||||
// 10.15 SDK
|
||||
|
||||
#ifndef MAC_OS_X_VERSION_10_15
|
||||
#define MAC_OS_X_VERSION_10_15 101500
|
||||
#endif
|
||||
|
||||
#endif // CRASHPAD_COMPAT_MAC_AVAILABILITYMACROS_H_
|
||||
|
20
compat/non_elf/elf.h
Normal file
20
compat/non_elf/elf.h
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_NON_ELF_ELF_H_
|
||||
#define CRASHPAD_COMPAT_NON_ELF_ELF_H_
|
||||
|
||||
#include "third_party/glibc/elf/elf.h"
|
||||
|
||||
#endif // CRASHPAD_COMPAT_NON_ELF_ELF_H_
|
20
compat/non_mac/mach-o/loader.h
Normal file
20
compat/non_mac/mach-o/loader.h
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_
|
||||
#define CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_
|
||||
|
||||
#include "third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h"
|
||||
|
||||
#endif // CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_
|
21
compat/non_mac/mach/machine.h
Normal file
21
compat/non_mac/mach/machine.h
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_
|
||||
#define CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_
|
||||
|
||||
typedef int cpu_type_t;
|
||||
typedef int cpu_subtype_t;
|
||||
|
||||
#endif // CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_
|
20
compat/non_mac/mach/vm_prot.h
Normal file
20
compat/non_mac/mach/vm_prot.h
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_
|
||||
#define CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_
|
||||
|
||||
typedef int vm_prot_t;
|
||||
|
||||
#endif // CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_
|
@ -214,7 +214,7 @@ union __attribute__((packed, aligned(4))) CPU_INFORMATION {
|
||||
//! `cpuid 0x80000001` `edx`.
|
||||
//!
|
||||
//! This field is only valid if #VendorId identifies the CPU vendor as
|
||||
//! “AuthenticAMD”.
|
||||
//! “AuthenticAMD” or "HygonGenuine".
|
||||
uint32_t AMDExtendedCpuFeatures;
|
||||
} X86CpuInfo;
|
||||
|
||||
|
@ -20,6 +20,4 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned int pid_t;
|
||||
|
||||
#endif // CRASHPAD_COMPAT_WIN_SYS_TYPES_H_
|
||||
|
@ -106,68 +106,67 @@ GN and Ninja are part of the
|
||||
[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s
|
||||
no need to install them separately.
|
||||
|
||||
#### Fuchsia
|
||||
|
||||
In order to instruct gclient to download the Fuchsia SDK, you need to add the
|
||||
following to `~/crashpad/.gclient`.
|
||||
|
||||
```
|
||||
target_os=["fuchsia"]
|
||||
```
|
||||
|
||||
If you're using this tree to develop for multiple targets, you can also add
|
||||
other entries to the the list (e.g. `target_os=["fuchsia", "mac"]`).
|
||||
|
||||
#### Optional Linux Configs
|
||||
|
||||
To pull and use Crashpad's version of clang and sysroot, make the following
|
||||
changes.
|
||||
|
||||
Add the following to `~/crashpad/.gclient`.
|
||||
```
|
||||
"custom_vars": { "pull_linux_clang": True },
|
||||
```
|
||||
Add these args to `out/Default/args.gn`.
|
||||
```
|
||||
clang_path = "//third_party/linux/clang/linux-amd64"
|
||||
target_sysroot = "//third_party/linux/sysroot"
|
||||
```
|
||||
|
||||
### Android
|
||||
|
||||
Crashpad’s Android port is in its early stages. This build relies on
|
||||
cross-compilation. It’s possible to develop Crashpad for Android on any platform
|
||||
that the [Android NDK (Native Development
|
||||
Kit)](https://developer.android.com/ndk/) runs on.
|
||||
This build relies on cross-compilation. It’s possible to develop Crashpad for
|
||||
Android on any platform that the [Android NDK (Native Development Kit)]
|
||||
(https://developer.android.com/ndk/) runs on.
|
||||
|
||||
If it’s not already present on your system, [download the NDK package for your
|
||||
system](https://developer.android.com/ndk/downloads/) and expand it to a
|
||||
suitable location. These instructions assume that it’s been expanded to
|
||||
`~/android-ndk-r16`.
|
||||
|
||||
To build Crashpad, portions of the NDK must be reassembled into a [standalone
|
||||
toolchain](https://developer.android.com/ndk/guides/standalone_toolchain.html).
|
||||
This is a repackaged subset of the NDK suitable for cross-compiling for a single
|
||||
Android architecture (such as `arm`, `arm64`, `x86`, and `x86_64`) targeting a
|
||||
specific [Android API
|
||||
level](https://source.android.com/source/build-numbers.html). The standalone
|
||||
toolchain only needs to be built from the NDK one time for each set of options
|
||||
desired. To build a standalone toolchain targeting 64-bit ARM and API level 21
|
||||
(Android 5.0 “Lollipop”), run:
|
||||
|
||||
```
|
||||
$ cd ~
|
||||
$ python android-ndk-r16/build/tools/make_standalone_toolchain.py \
|
||||
--arch=arm64 --api=21 --install-dir=android-ndk-r16_arm64_api21
|
||||
```
|
||||
`~/android-ndk-r20`.
|
||||
|
||||
Note that Chrome uses Android API level 21 for 64-bit platforms and 16 for
|
||||
32-bit platforms. See Chrome’s
|
||||
[`build/config/android/config.gni`](https://chromium.googlesource.com/chromium/src/+/master/build/config/android/config.gni)
|
||||
which sets `_android_api_level` and `_android64_api_level`.
|
||||
|
||||
To configure a Crashpad build for Android using the standalone toolchain
|
||||
assembled above, use `gyp_crashpad_android.py`. This script is a wrapper for
|
||||
`gyp_crashpad.py` that sets several environment variables directing the build to
|
||||
the standalone toolchain, and several GYP options to identify an Android build.
|
||||
This must be done after any `gclient sync`, or instead of any `gclient runhooks`
|
||||
operation.
|
||||
To configure a Crashpad build for Android use `gyp_crashpad_android.py`. This
|
||||
script is a wrapper for `gyp_crashpad.py` that sets several environment
|
||||
variables directing the build to the toolchain, and several GYP options to
|
||||
identify an Android build. This must be done after any `gclient sync`, or
|
||||
instead of any `gclient runhooks` operation.
|
||||
|
||||
```
|
||||
$ cd ~/crashpad/crashpad
|
||||
$ python build/gyp_crashpad_android.py \
|
||||
--ndk ~/android-ndk-r16_arm64_api21 \
|
||||
--generator-output out/android_arm64_api21
|
||||
python build/gyp_crashpad_android.py \
|
||||
--ndk ~/usr/lib/android-ndk-r20 --arch arm64 --api-level 21 \
|
||||
--generator-output=out/android_arm64_api21 \
|
||||
```
|
||||
|
||||
`gyp_crashpad_android.py` detects the build type based on the characteristics of
|
||||
the standalone toolchain given in its `--ndk` argument.
|
||||
|
||||
`gyp_crashpad_android.py` sets the build up to use Clang by default. It’s also
|
||||
possible to use GCC by providing the `--compiler=gcc` argument to
|
||||
`gyp_crashpad_android.py`.
|
||||
|
||||
The Android port is incomplete, but targets known to be working include
|
||||
`crashpad_test`, `crashpad_util`, and their tests. This list will grow over
|
||||
time. To build, direct `ninja` to the specific `out` directory chosen by the
|
||||
To build, direct `ninja` to the specific `out` directory chosen by the
|
||||
`--generator-output` argument to `gyp_crashpad_android.py`.
|
||||
|
||||
```
|
||||
$ ninja -C out/android_arm64_api21/out/Debug \
|
||||
crashpad_test_test crashpad_util_test
|
||||
$ ninja -C out/android_arm64_api21/out/Debug all
|
||||
```
|
||||
|
||||
## Testing
|
||||
@ -193,6 +192,13 @@ $ cd ~/crashpad/crashpad
|
||||
$ python build/run_tests.py out/Debug
|
||||
```
|
||||
|
||||
To run a subset of the tests, use the --gtest\_filter flag, e.g., to run all the
|
||||
tests for MinidumpStringWriter:
|
||||
|
||||
```sh
|
||||
$ python build/run_tests.py out/Debug --gtest_filter MinidumpStringWriter*
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
On Windows, `end_to_end_test.py` requires the CDB debugger, installed with
|
||||
@ -220,6 +226,25 @@ variable. `run_tests.py` will upload test executables and data to a temporary
|
||||
location on the detected or selected device, run them, and clean up after itself
|
||||
when done.
|
||||
|
||||
### Fuchsia
|
||||
|
||||
To test on Fuchsia, you need a connected device running Fuchsia and then run:
|
||||
|
||||
```sh
|
||||
$ gn gen out/fuchsia --args='target_os="fuchsia" target_cpu="x64" is_debug=true'
|
||||
$ ninja -C out/fuchsia
|
||||
$ python build/run_tests.py out/fuchsia
|
||||
```
|
||||
|
||||
If you have multiple devices running, you will need to specify which device you
|
||||
want using their hostname, for instance:
|
||||
|
||||
```sh
|
||||
$ export ZIRCON_NODENAME=scare-brook-skip-dried; \
|
||||
python build/run_tests.py out/fuchsia; \
|
||||
unset ZIRCON_NODENAME
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Crashpad’s contribution process is very similar to [Chromium’s contribution
|
||||
@ -303,7 +328,7 @@ file.
|
||||
|
||||
## Buildbot
|
||||
|
||||
The [Crashpad Buildbot](https://build.chromium.org/p/client.crashpad/) performs
|
||||
automated builds and tests of Crashpad. Before checking out or updating the
|
||||
Crashpad source code, and after checking in a new change, it is prudent to check
|
||||
the Buildbot to ensure that “the tree is green.”
|
||||
The [Crashpad Buildbot](https://ci.chromium.org/p/crashpad/g/main/console)
|
||||
performs automated builds and tests of Crashpad. Before checking out or updating
|
||||
the Crashpad source code, and after checking in a new change, it is prudent to
|
||||
check the Buildbot to ensure that “the tree is green.”
|
||||
|
@ -240,7 +240,24 @@ of command line arguments in this mode.
|
||||
|
||||
#### Linux/Android
|
||||
|
||||
TODO(mmentovai): describe this. See this preliminary doc.
|
||||
On Linux, a registration is a connected socket pair between a client process and
|
||||
the Crashpad handler. This socket pair may be private or shared among many
|
||||
client processes.
|
||||
|
||||
##### Private Connections
|
||||
|
||||
Private connections are the default registration mode when starting the handler
|
||||
process in response to a crash or on behalf of another client. This mode is
|
||||
required to use a ptrace broker, which is in turn required to trace Android
|
||||
isolated processes.
|
||||
|
||||
##### Shared Connections
|
||||
|
||||
Shared connections are the default mode when using a long-lived handler. The
|
||||
same connected socket pair may be shared among any number of clients. The socket
|
||||
pair is created by the first process to start the handler at which point the
|
||||
client socket end may be shared with other clients by any convenient means (e.g.
|
||||
inheritance).
|
||||
|
||||
### Capturing Exceptions
|
||||
|
||||
@ -290,8 +307,16 @@ here.](https://crashpad.chromium.org/bug/133)
|
||||
|
||||
#### Linux/Android
|
||||
|
||||
TODO(mmentovai): describe this. See [this preliminary
|
||||
doc.](https://goto.google.com/crashpad-android-dd)
|
||||
On Linux, exceptions are dispatched as signals to the crashing thread. Crashpad
|
||||
signal handlers will send a message over the socket to the Crashpad handler
|
||||
notifying it of the crash and the location of exception information to be read
|
||||
from the crashing process. When using a shared socket connection, communication
|
||||
is entirely one-way. The client sends its dump request to the handler and then
|
||||
waits until the handler responds with a SIGCONT or a timeout occurs. When using
|
||||
a private socket connection, the handler may respond over the socket to
|
||||
communicate with a ptrace broker process. The broker is forked from the crashing
|
||||
process, executes ptrace requests against the crashing process, and sends the
|
||||
information over the socket to the handler.
|
||||
|
||||
### The CrashpadInfo structure
|
||||
|
||||
|
@ -18,24 +18,27 @@ limitations under the License.
|
||||
|
||||
## Completed
|
||||
|
||||
Crashpad currently consists of a crash-reporting client and some related tools
|
||||
for macOS and Windows. The core client work for both platforms is substantially
|
||||
complete. Crashpad became the crash reporter client for
|
||||
[Chromium](https://www.chromium.org/Home) on macOS as of [March
|
||||
Crashpad has complete crash-reporting clients and some related tools for macOS,
|
||||
Windows, Fuchsia, and Linux (including Android and Chromium OS). Crashpad became
|
||||
the crash reporter client for [Chromium](https://www.chromium.org/Home) on macOS
|
||||
as of [March
|
||||
2015](https://chromium.googlesource.com/chromium/src/\+/d413b2dcb54d523811d386f1ff4084f677a6d089),
|
||||
and on Windows as of [November
|
||||
2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c).
|
||||
Windows as of [November
|
||||
2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c),
|
||||
and Android as of [January
|
||||
2019](https://chromium.googlesource.com/chromium/src/+/f890e4b5495ab693d2d37aec3c378239946154f7).
|
||||
|
||||
|
||||
## In Progress
|
||||
|
||||
Initial work on a Crashpad client for
|
||||
[Android](https://crashpad.chromium.org/bug/30) has begun. This is currently in
|
||||
the early implementation phase.
|
||||
Chromium is transitioning to Crashpad for [Chromium OS and Desktop
|
||||
Linux](https://crbug.com/942279).
|
||||
|
||||
Work has begun on a Crashpad client for
|
||||
[iOS](https://crashpad.chromium.org/bug/31).
|
||||
|
||||
## Future
|
||||
|
||||
There are plans to bring Crashpad clients to other operating systems in the
|
||||
future, including a more generic non-Android Linux implementation. There are
|
||||
also plans to implement a [crash report
|
||||
There are also plans to implement a [crash report
|
||||
processor](https://crashpad.chromium.org/bug/29) as part of Crashpad. No
|
||||
timeline for completing this work has been set yet.
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.8.14
|
||||
# Doxyfile 1.8.18
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@ -17,10 +17,10 @@
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# This tag specifies the encoding used for all characters in the config file
|
||||
# that follow. The default is UTF-8 which is also the encoding used for all text
|
||||
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
|
||||
# built into libc) for the transcoding. See
|
||||
# This tag specifies the encoding used for all characters in the configuration
|
||||
# file that follow. The default is UTF-8 which is also the encoding used for all
|
||||
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
|
||||
# iconv built into libc) for the transcoding. See
|
||||
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
|
||||
# The default value is: UTF-8.
|
||||
|
||||
@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO
|
||||
|
||||
OUTPUT_LANGUAGE = English
|
||||
|
||||
# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
|
||||
# documentation generated by doxygen is written. Doxygen will use this
|
||||
# information to generate all generated output in the proper direction.
|
||||
# Possible values are: None, LTR, RTL and Context.
|
||||
# The default value is: None.
|
||||
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
|
||||
# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
|
||||
# descriptions after the members that are listed in the file and class
|
||||
# documentation (similar to Javadoc). Set to NO to disable this.
|
||||
@ -181,6 +189,16 @@ SHORT_NAMES = NO
|
||||
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
|
||||
# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
|
||||
# such as
|
||||
# /***************
|
||||
# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
|
||||
# Javadoc-style will behave just like regular comments and it will not be
|
||||
# interpreted by doxygen.
|
||||
# The default value is: NO.
|
||||
|
||||
JAVADOC_BANNER = NO
|
||||
|
||||
# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
|
||||
# line (until the first dot) of a Qt-style comment as the brief description. If
|
||||
# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
|
||||
@ -230,15 +248,13 @@ TAB_SIZE = 4
|
||||
# "Side Effects:". You can put \n's in the value part of an alias to insert
|
||||
# newlines (in the resulting output). You can put ^^ in the value part of an
|
||||
# alias to insert a newline as if a physical newline was in the original file.
|
||||
# When you need a literal { or } or , in the value part of an alias you have to
|
||||
# escape them by means of a backslash (\), this can lead to conflicts with the
|
||||
# commands \{ and \} for these it is advised to use the version @{ and @} or use
|
||||
# a double escape (\\{ and \\})
|
||||
|
||||
ALIASES =
|
||||
|
||||
# This tag can be used to specify a number of word-keyword mappings (TCL only).
|
||||
# A mapping has the form "name=value". For example adding "class=itcl::class"
|
||||
# will allow you to use the command class in the itcl::class meaning.
|
||||
|
||||
TCL_SUBST =
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
|
||||
# only. Doxygen will then generate output that is more tailored for C. For
|
||||
# instance, some of the names that are used will be different. The list of all
|
||||
@ -267,17 +283,26 @@ OPTIMIZE_FOR_FORTRAN = NO
|
||||
|
||||
OPTIMIZE_OUTPUT_VHDL = NO
|
||||
|
||||
# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
|
||||
# sources only. Doxygen will then generate output that is more tailored for that
|
||||
# language. For instance, namespaces will be presented as modules, types will be
|
||||
# separated into more groups, etc.
|
||||
# The default value is: NO.
|
||||
|
||||
OPTIMIZE_OUTPUT_SLICE = NO
|
||||
|
||||
# Doxygen selects the parser to use depending on the extension of the files it
|
||||
# parses. With this tag you can assign which parser to use for a given
|
||||
# extension. Doxygen has a built-in mapping, but you can override or extend it
|
||||
# using this tag. The format is ext=language, where ext is a file extension, and
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
|
||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
|
||||
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
|
||||
# Fortran. In the later case the parser tries to guess whether the code is fixed
|
||||
# or free formatted code, this is the default for Fortran type files), VHDL. For
|
||||
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
|
||||
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
|
||||
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
|
||||
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||
# tries to guess whether the code is fixed or free formatted code, this is the
|
||||
# default for Fortran type files). For instance to make doxygen treat .inc files
|
||||
# as Fortran files (default is PHP), and .f files as C (default is Fortran),
|
||||
# use: inc=Fortran f=C.
|
||||
#
|
||||
# Note: For files without extension you can use no_extension as a placeholder.
|
||||
#
|
||||
@ -288,7 +313,7 @@ EXTENSION_MAPPING =
|
||||
|
||||
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
|
||||
# according to the Markdown format, which allows for more readable
|
||||
# documentation. See http://daringfireball.net/projects/markdown/ for details.
|
||||
# documentation. See https://daringfireball.net/projects/markdown/ for details.
|
||||
# The output of markdown processing is further processed by doxygen, so you can
|
||||
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
|
||||
# case of backward compatibilities issues.
|
||||
@ -300,7 +325,7 @@ MARKDOWN_SUPPORT = YES
|
||||
# to that level are automatically included in the table of contents, even if
|
||||
# they do not have an id attribute.
|
||||
# Note: This feature currently applies only to Markdown headings.
|
||||
# Minimum value: 0, maximum value: 99, default value: 0.
|
||||
# Minimum value: 0, maximum value: 99, default value: 5.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
TOC_INCLUDE_HEADINGS = 0
|
||||
@ -436,6 +461,12 @@ EXTRACT_ALL = NO
|
||||
|
||||
EXTRACT_PRIVATE = NO
|
||||
|
||||
# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
|
||||
# methods of a class will be included in the documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_PRIV_VIRTUAL = NO
|
||||
|
||||
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
|
||||
# scope will be included in the documentation.
|
||||
# The default value is: NO.
|
||||
@ -490,8 +521,8 @@ HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
||||
# (class|struct|union) declarations. If set to NO, these declarations will be
|
||||
# included in the documentation.
|
||||
# declarations. If set to NO, these declarations will be included in the
|
||||
# documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
@ -514,7 +545,7 @@ INTERNAL_DOCS = NO
|
||||
# names in lower-case letters. If set to YES, upper-case letters are also
|
||||
# allowed. This is useful if you have classes or files whose names only differ
|
||||
# in case and if your file system supports case sensitive file names. Windows
|
||||
# and Mac users are advised to set this option to NO.
|
||||
# (including Cygwin) ands Mac users are advised to set this option to NO.
|
||||
# The default value is: system dependent.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
@ -746,7 +777,8 @@ WARN_IF_DOC_ERROR = YES
|
||||
# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
|
||||
# are documented, but have no documentation for their parameters or return
|
||||
# value. If set to NO, doxygen will only warn about wrong or incomplete
|
||||
# parameter documentation, but not about the absence of documentation.
|
||||
# parameter documentation, but not about the absence of documentation. If
|
||||
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_NO_PARAMDOC = NO
|
||||
@ -805,8 +837,10 @@ INPUT_ENCODING = UTF-8
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
|
||||
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||
# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
|
||||
# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.cc \
|
||||
@ -968,7 +1002,7 @@ INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
|
||||
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
|
||||
# function all documented functions referencing it will be listed.
|
||||
# entity all documented functions referencing it will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
REFERENCED_BY_RELATION = NO
|
||||
@ -1005,7 +1039,7 @@ SOURCE_TOOLTIPS = YES
|
||||
#
|
||||
# To use it do the following:
|
||||
# - Install the latest version of global
|
||||
# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
|
||||
# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
|
||||
# - Make sure the INPUT points to the root of the source tree
|
||||
# - Run doxygen as normal
|
||||
#
|
||||
@ -1183,9 +1217,9 @@ HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via Javascript. If disabled, the navigation index will
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
# consists of multiple levels of tabs that are statically embedded in every HTML
|
||||
# page. Disable this option to support browsers that do not have Javascript,
|
||||
# page. Disable this option to support browsers that do not have JavaScript,
|
||||
# like the Qt help browser.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
@ -1215,13 +1249,13 @@ HTML_INDEX_NUM_ENTRIES = 100
|
||||
|
||||
# If the GENERATE_DOCSET tag is set to YES, additional index files will be
|
||||
# generated that can be used as input for Apple's Xcode 3 integrated development
|
||||
# environment (see: https://developer.apple.com/tools/xcode/), introduced with
|
||||
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
|
||||
# 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
||||
# Makefile in the HTML output directory. Running make will produce the docset in
|
||||
# that directory and running make install will install the docset in
|
||||
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
|
||||
# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
|
||||
# for more information.
|
||||
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
|
||||
# genXcode/_index.html for more information.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
@ -1260,7 +1294,7 @@ DOCSET_PUBLISHER_NAME = Publisher
|
||||
# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
|
||||
# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
|
||||
# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
|
||||
# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
|
||||
# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
|
||||
# Windows.
|
||||
#
|
||||
# The HTML Help Workshop contains a compiler that can convert all HTML output
|
||||
@ -1336,7 +1370,7 @@ QCH_FILE =
|
||||
|
||||
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||
# Project output. For more information please see Qt Help Project / Namespace
|
||||
# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace).
|
||||
# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
|
||||
# The default value is: org.doxygen.Project.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@ -1344,7 +1378,8 @@ QHP_NAMESPACE = org.doxygen.Project
|
||||
|
||||
# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
|
||||
# Help Project output. For more information please see Qt Help Project / Virtual
|
||||
# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders).
|
||||
# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
|
||||
# folders).
|
||||
# The default value is: doc.
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
@ -1352,21 +1387,23 @@ QHP_VIRTUAL_FOLDER = doc
|
||||
|
||||
# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
|
||||
# filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_NAME =
|
||||
|
||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
||||
# custom filter to add. For more information please see Qt Help Project / Custom
|
||||
# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters).
|
||||
# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
|
||||
# filters).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
|
||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
||||
# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
|
||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
@ -1450,6 +1487,17 @@ TREEVIEW_WIDTH = 250
|
||||
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
|
||||
# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
|
||||
# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
|
||||
# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
|
||||
# the HTML output. These images will generally look nicer at scaled resolutions.
|
||||
# Possible values are: png The default and svg Looks nicer but requires the
|
||||
# pdf2svg tool.
|
||||
# The default value is: png.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_FORMULA_FORMAT = png
|
||||
|
||||
# Use this tag to change the font size of LaTeX formulas included as images in
|
||||
# the HTML documentation. When you change the font size after a successful
|
||||
# doxygen run you need to manually remove any form_*.png images from the HTML
|
||||
@ -1470,8 +1518,14 @@ FORMULA_FONTSIZE = 10
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
|
||||
FORMULA_MACROFILE =
|
||||
|
||||
# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
|
||||
# https://www.mathjax.org) which uses client side Javascript for the rendering
|
||||
# https://www.mathjax.org) which uses client side JavaScript for the rendering
|
||||
# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
|
||||
# installed or if you want to formulas look prettier in the HTML output. When
|
||||
# enabled you may also need to install MathJax separately and configure the path
|
||||
@ -1499,7 +1553,7 @@ MATHJAX_FORMAT = HTML-CSS
|
||||
# Content Delivery Network so you can quickly see the result without installing
|
||||
# MathJax. However, it is strongly recommended to install a local copy of
|
||||
# MathJax from https://www.mathjax.org before deployment.
|
||||
# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/.
|
||||
# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
|
||||
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||
|
||||
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||
@ -1541,7 +1595,7 @@ MATHJAX_CODEFILE =
|
||||
SEARCHENGINE = YES
|
||||
|
||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
||||
# implemented using a web server instead of a web client using Javascript. There
|
||||
# implemented using a web server instead of a web client using JavaScript. There
|
||||
# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
|
||||
# setting. When disabled, doxygen will generate a PHP script for searching and
|
||||
# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
|
||||
@ -1625,21 +1679,35 @@ LATEX_OUTPUT = latex
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
#
|
||||
# Note that when enabling USE_PDFLATEX this option is only used for generating
|
||||
# bitmaps for formulas in the HTML output, but not in the Makefile that is
|
||||
# written to the output directory.
|
||||
# The default file is: latex.
|
||||
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
|
||||
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
|
||||
# chosen this is overwritten by pdflatex. For specific output languages the
|
||||
# default can have been set differently, this depends on the implementation of
|
||||
# the output language.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_CMD_NAME = latex
|
||||
|
||||
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
|
||||
# index for LaTeX.
|
||||
# Note: This tag is used in the Makefile / make.bat.
|
||||
# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
|
||||
# (.tex).
|
||||
# The default file is: makeindex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
|
||||
# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
|
||||
# generate index for LaTeX. In case there is no backslash (\) as first character
|
||||
# it will be automatically added in the LaTeX code.
|
||||
# Note: This tag is used in the generated output file (.tex).
|
||||
# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
|
||||
# The default value is: makeindex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_MAKEINDEX_CMD = makeindex
|
||||
|
||||
# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
|
||||
# documents. This may be useful for small projects and may help to save some
|
||||
# trees in general.
|
||||
@ -1774,6 +1842,14 @@ LATEX_BIB_STYLE = plain
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# path from which the emoji images will be read. If a relative path is entered,
|
||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||
# LATEX_OUTPUT directory will be used.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_EMOJI_DIRECTORY =
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
@ -1813,9 +1889,9 @@ COMPACT_RTF = NO
|
||||
|
||||
RTF_HYPERLINKS = NO
|
||||
|
||||
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
|
||||
# file, i.e. a series of assignments. You only have to provide replacements,
|
||||
# missing definitions are set to their default value.
|
||||
# Load stylesheet definitions from file. Syntax is similar to doxygen's
|
||||
# configuration file, i.e. a series of assignments. You only have to provide
|
||||
# replacements, missing definitions are set to their default value.
|
||||
#
|
||||
# See also section "Doxygen usage" for information on how to generate the
|
||||
# default style sheet that doxygen normally uses.
|
||||
@ -1824,8 +1900,8 @@ RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
|
||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
||||
# similar to doxygen's config file. A template extensions file can be generated
|
||||
# using doxygen -e rtf extensionFile.
|
||||
# similar to doxygen's configuration file. A template extensions file can be
|
||||
# generated using doxygen -e rtf extensionFile.
|
||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||
|
||||
RTF_EXTENSIONS_FILE =
|
||||
@ -1911,6 +1987,13 @@ XML_OUTPUT = xml
|
||||
|
||||
XML_PROGRAMLISTING = YES
|
||||
|
||||
# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
|
||||
# namespace members in file scope as well, matching the HTML output.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_XML is set to YES.
|
||||
|
||||
XML_NS_MEMB_FILE_SCOPE = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the DOCBOOK output
|
||||
#---------------------------------------------------------------------------
|
||||
@ -2116,12 +2199,6 @@ EXTERNAL_GROUPS = YES
|
||||
|
||||
EXTERNAL_PAGES = YES
|
||||
|
||||
# The PERL_PATH should be the absolute path and name of the perl script
|
||||
# interpreter (i.e. the result of 'which perl').
|
||||
# The default file (with absolute path) is: /usr/bin/perl.
|
||||
|
||||
PERL_PATH = /usr/bin/perl
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
@ -2135,15 +2212,6 @@ PERL_PATH = /usr/bin/perl
|
||||
|
||||
CLASS_DIAGRAMS = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. Doxygen will then run the mscgen tool (see:
|
||||
# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
|
||||
# documentation. The MSCGEN_PATH tag allows you to specify the directory where
|
||||
# the mscgen tool resides. If left empty the tool is assumed to be found in the
|
||||
# default search path.
|
||||
|
||||
MSCGEN_PATH =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
|
@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
# coding: utf-8
|
||||
|
||||
# Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
#
|
||||
@ -24,26 +23,26 @@ import sys
|
||||
|
||||
|
||||
def main(args):
|
||||
script_dir = os.path.dirname(__file__)
|
||||
crashpad_dir = os.path.join(script_dir, os.pardir, os.pardir)
|
||||
script_dir = os.path.dirname(__file__)
|
||||
crashpad_dir = os.path.join(script_dir, os.pardir, os.pardir)
|
||||
|
||||
# Run from the Crashpad project root directory.
|
||||
os.chdir(crashpad_dir)
|
||||
# Run from the Crashpad project root directory.
|
||||
os.chdir(crashpad_dir)
|
||||
|
||||
output_dir = os.path.join('out', 'doc', 'doxygen')
|
||||
output_dir = os.path.join('out', 'doc', 'doxygen')
|
||||
|
||||
if os.path.isdir(output_dir) and not os.path.islink(output_dir):
|
||||
shutil.rmtree(output_dir)
|
||||
elif os.path.exists(output_dir):
|
||||
os.unlink(output_dir)
|
||||
if os.path.isdir(output_dir) and not os.path.islink(output_dir):
|
||||
shutil.rmtree(output_dir)
|
||||
elif os.path.exists(output_dir):
|
||||
os.unlink(output_dir)
|
||||
|
||||
os.makedirs(output_dir, 0o755)
|
||||
os.makedirs(output_dir, 0o755)
|
||||
|
||||
doxy_file = os.path.join('doc', 'support', 'crashpad.doxy')
|
||||
subprocess.check_call(['doxygen', doxy_file])
|
||||
doxy_file = os.path.join('doc', 'support', 'crashpad.doxy')
|
||||
subprocess.check_call(['doxygen', doxy_file])
|
||||
|
||||
return 0
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
sys.exit(main(sys.argv[1:]))
|
||||
|
209
handler/BUILD.gn
209
handler/BUILD.gn
@ -42,6 +42,8 @@ static_library("handler") {
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
set_sources_assignment_filter([])
|
||||
sources += [
|
||||
"linux/capture_snapshot.cc",
|
||||
"linux/capture_snapshot.h",
|
||||
"linux/crash_report_exception_handler.cc",
|
||||
"linux/crash_report_exception_handler.h",
|
||||
"linux/exception_handler_server.cc",
|
||||
@ -49,6 +51,13 @@ static_library("handler") {
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_linux) {
|
||||
sources += [
|
||||
"linux/cros_crash_report_exception_handler.cc",
|
||||
"linux/cros_crash_report_exception_handler.h",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_win) {
|
||||
sources += [
|
||||
"win/crash_report_exception_handler.cc",
|
||||
@ -56,25 +65,18 @@ static_library("handler") {
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_fuchsia) {
|
||||
sources += [
|
||||
"fuchsia/crash_report_exception_handler.cc",
|
||||
"fuchsia/crash_report_exception_handler.h",
|
||||
"fuchsia/exception_handler_server.cc",
|
||||
"fuchsia/exception_handler_server.h",
|
||||
]
|
||||
}
|
||||
|
||||
public_configs = [ "..:crashpad_config" ]
|
||||
|
||||
deps = [
|
||||
public_deps = [
|
||||
"../client",
|
||||
"../compat",
|
||||
"../third_party/mini_chromium:base",
|
||||
"../util",
|
||||
]
|
||||
|
||||
deps = [
|
||||
"../minidump",
|
||||
"../snapshot",
|
||||
"../third_party/mini_chromium:base",
|
||||
"../tools:tool_support",
|
||||
"../util",
|
||||
]
|
||||
|
||||
if (crashpad_is_win) {
|
||||
@ -82,12 +84,20 @@ static_library("handler") {
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_android) {
|
||||
# CrashpadHandlerMain is defined in a separate target so that it can be
|
||||
# overriden by implementers
|
||||
source_set("crashpad_handler_main") {
|
||||
sources = [ "crashpad_handler_main.cc" ]
|
||||
|
||||
deps = [ ":handler" ]
|
||||
}
|
||||
}
|
||||
|
||||
source_set("handler_test") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"minidump_to_upload_parameters_test.cc",
|
||||
]
|
||||
sources = [ "minidump_to_upload_parameters_test.cc" ]
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
sources += [ "linux/exception_handler_server_test.cc" ]
|
||||
@ -110,6 +120,8 @@ source_set("handler_test") {
|
||||
]
|
||||
|
||||
if (crashpad_is_win) {
|
||||
deps += [ "../minidump:test_support" ]
|
||||
|
||||
data_deps = [
|
||||
":crashpad_handler_test_extended_handler",
|
||||
":fake_handler_that_crashes_at_startup",
|
||||
@ -117,45 +129,28 @@ source_set("handler_test") {
|
||||
}
|
||||
}
|
||||
|
||||
crashpad_executable("crashpad_handler") {
|
||||
sources = [
|
||||
"main.cc",
|
||||
]
|
||||
if (!crashpad_is_ios) {
|
||||
crashpad_executable("crashpad_handler") {
|
||||
sources = [ "main.cc" ]
|
||||
|
||||
deps = [
|
||||
":handler",
|
||||
"../build:default_exe_manifest_win",
|
||||
"../compat",
|
||||
"../third_party/mini_chromium:base",
|
||||
]
|
||||
deps = [
|
||||
":handler",
|
||||
"../build:default_exe_manifest_win",
|
||||
"../compat",
|
||||
"../third_party/mini_chromium:base",
|
||||
"../tools:tool_support",
|
||||
]
|
||||
|
||||
if (crashpad_is_mac && crashpad_is_in_chromium) {
|
||||
if (is_component_build) {
|
||||
ldflags = [
|
||||
# The handler is in
|
||||
# Chromium.app/Contents/Versions/X/Chromium Framework.framework/Versions/A/Helpers/
|
||||
# so set rpath up to the base.
|
||||
"-rpath",
|
||||
"@loader_path/../../../../../../../..",
|
||||
|
||||
# The handler is also in
|
||||
# Content Shell.app/Contents/Frameworks/Content Shell Framework.framework/Helpers/
|
||||
# so set the rpath for that too.
|
||||
"-rpath",
|
||||
"@loader_path/../../../../..",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_win) {
|
||||
if (crashpad_is_in_chromium) {
|
||||
remove_configs = [ "//build/config/win:console" ]
|
||||
configs = [ "//build/config/win:windowed" ]
|
||||
} else {
|
||||
remove_configs =
|
||||
[ "//third_party/mini_chromium/mini_chromium/build:win_console" ]
|
||||
configs =
|
||||
[ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
|
||||
if (crashpad_is_win) {
|
||||
if (crashpad_is_in_chromium || crashpad_is_in_dart) {
|
||||
remove_configs = [ "//build/config/win:console" ]
|
||||
configs = [ "//build/config/win:windowed" ]
|
||||
} else {
|
||||
remove_configs =
|
||||
[ "//third_party/mini_chromium/mini_chromium/build:win_console" ]
|
||||
configs =
|
||||
[ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -166,42 +161,47 @@ crashpad_executable("crashpad_handler") {
|
||||
# handler executable an acceptable name.
|
||||
if (crashpad_is_android) {
|
||||
copy("crashpad_handler_named_as_so") {
|
||||
deps = [ ":crashpad_handler" ]
|
||||
|
||||
sources = [ "$root_out_dir/crashpad_handler" ]
|
||||
|
||||
outputs = [ "$root_out_dir/libcrashpad_handler.so" ]
|
||||
}
|
||||
|
||||
crashpad_executable("crashpad_handler_trampoline") {
|
||||
set_sources_assignment_filter([])
|
||||
output_name = "libcrashpad_handler_trampoline.so"
|
||||
|
||||
sources = [ "linux/handler_trampoline.cc" ]
|
||||
|
||||
ldflags = [ "-llog" ]
|
||||
|
||||
if (crashpad_is_in_chromium) {
|
||||
no_default_deps = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!crashpad_is_ios) {
|
||||
crashpad_executable("crashpad_handler_test_extended_handler") {
|
||||
testonly = true
|
||||
|
||||
sources = [ "crashpad_handler_test_extended_handler.cc" ]
|
||||
|
||||
deps = [
|
||||
":crashpad_handler",
|
||||
]
|
||||
|
||||
sources = [
|
||||
"$root_out_dir/crashpad_handler",
|
||||
]
|
||||
|
||||
outputs = [
|
||||
"$root_out_dir/libcrashpad_handler.so",
|
||||
":handler",
|
||||
"../build:default_exe_manifest_win",
|
||||
"../compat",
|
||||
"../minidump:test_support",
|
||||
"../third_party/mini_chromium:base",
|
||||
"../tools:tool_support",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
crashpad_executable("crashpad_handler_test_extended_handler") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"crashpad_handler_test_extended_handler.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
":handler",
|
||||
"../build:default_exe_manifest_win",
|
||||
"../compat",
|
||||
"../minidump:test_support",
|
||||
"../third_party/mini_chromium:base",
|
||||
"../tools:tool_support",
|
||||
]
|
||||
}
|
||||
|
||||
if (crashpad_is_win) {
|
||||
crashpad_executable("crashpad_handler_com") {
|
||||
sources = [
|
||||
"main.cc",
|
||||
]
|
||||
sources = [ "main.cc" ]
|
||||
|
||||
# Avoid .exp, .ilk, and .lib file collisions with crashpad_handler.exe by
|
||||
# having this target produce crashpad_handler_com.com. Don’t use this target
|
||||
@ -213,27 +213,20 @@ if (crashpad_is_win) {
|
||||
"../build:default_exe_manifest_win",
|
||||
"../compat",
|
||||
"../third_party/mini_chromium:base",
|
||||
"../tools:tool_support",
|
||||
]
|
||||
}
|
||||
|
||||
copy("crashpad_handler_console") {
|
||||
deps = [
|
||||
":crashpad_handler_com",
|
||||
]
|
||||
sources = [
|
||||
"$root_out_dir/crashpad_handler_com.com",
|
||||
]
|
||||
outputs = [
|
||||
"$root_out_dir/crashpad_handler.com",
|
||||
]
|
||||
deps = [ ":crashpad_handler_com" ]
|
||||
sources = [ "$root_out_dir/crashpad_handler_com.com" ]
|
||||
outputs = [ "$root_out_dir/crashpad_handler.com" ]
|
||||
}
|
||||
|
||||
crashpad_executable("crash_other_program") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/crash_other_program.cc",
|
||||
]
|
||||
sources = [ "win/crash_other_program.cc" ]
|
||||
|
||||
deps = [
|
||||
"../client",
|
||||
@ -246,9 +239,7 @@ if (crashpad_is_win) {
|
||||
crashpad_executable("crashy_program") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/crashy_test_program.cc",
|
||||
]
|
||||
sources = [ "win/crashy_test_program.cc" ]
|
||||
|
||||
deps = [
|
||||
"../client",
|
||||
@ -259,9 +250,7 @@ if (crashpad_is_win) {
|
||||
crashpad_executable("crashy_signal") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/crashy_signal.cc",
|
||||
]
|
||||
sources = [ "win/crashy_signal.cc" ]
|
||||
|
||||
cflags = [ "/wd4702" ] # Unreachable code.
|
||||
|
||||
@ -274,17 +263,13 @@ if (crashpad_is_win) {
|
||||
crashpad_executable("fake_handler_that_crashes_at_startup") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/fake_handler_that_crashes_at_startup.cc",
|
||||
]
|
||||
sources = [ "win/fake_handler_that_crashes_at_startup.cc" ]
|
||||
}
|
||||
|
||||
crashpad_executable("hanging_program") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/hanging_program.cc",
|
||||
]
|
||||
sources = [ "win/hanging_program.cc" ]
|
||||
|
||||
deps = [
|
||||
"../client",
|
||||
@ -295,17 +280,13 @@ if (crashpad_is_win) {
|
||||
crashpad_loadable_module("loader_lock_dll") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/loader_lock_dll.cc",
|
||||
]
|
||||
sources = [ "win/loader_lock_dll.cc" ]
|
||||
}
|
||||
|
||||
crashpad_executable("self_destroying_program") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/self_destroying_test_program.cc",
|
||||
]
|
||||
sources = [ "win/self_destroying_test_program.cc" ]
|
||||
|
||||
deps = [
|
||||
"../client",
|
||||
@ -320,9 +301,7 @@ if (crashpad_is_win) {
|
||||
crashpad_executable("crashy_z7_loader") {
|
||||
testonly = true
|
||||
|
||||
sources = [
|
||||
"win/crashy_test_z7_loader.cc",
|
||||
]
|
||||
sources = [ "win/crashy_test_z7_loader.cc" ]
|
||||
|
||||
deps = [
|
||||
"../client",
|
||||
|
@ -246,12 +246,6 @@ void CrashReportUploadThread::ProcessPendingReport(
|
||||
CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
|
||||
const CrashReportDatabase::UploadReport* report,
|
||||
std::string* response_body) {
|
||||
#if defined(OS_ANDROID)
|
||||
// TODO(jperaza): This method can be enabled on Android after HTTPTransport is
|
||||
// implemented and Crashpad takes over upload responsibilty on Android.
|
||||
NOTREACHED();
|
||||
return UploadResult::kPermanentFailure;
|
||||
#else
|
||||
std::map<std::string, std::string> parameters;
|
||||
|
||||
FileReader* reader = report->Reader();
|
||||
@ -338,7 +332,6 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
|
||||
}
|
||||
|
||||
return UploadResult::kSuccess;
|
||||
#endif // OS_ANDROID
|
||||
}
|
||||
|
||||
void CrashReportUploadThread::DoWork(const WorkerThread* thread) {
|
||||
|
@ -121,13 +121,6 @@ establish the Crashpad client environment before running a program.
|
||||
Either this option or **--mach-service**, but not both, is required. This
|
||||
option is only valid on macOS.
|
||||
|
||||
* **--no-identify-client-via-url**
|
||||
|
||||
Do not add client-identifying fields to the URL. By default, `"prod"`,
|
||||
`"ver"`, and `"guid"` annotations are added to the upload URL as name-value
|
||||
pairs `"product"`, `"version"`, and `"guid"`, respectively. Using this
|
||||
option disables that behavior.
|
||||
|
||||
* **--initial-client-data**=*HANDLE_request_crash_dump*,*HANDLE_request_non_crash_dump*,*HANDLE_non_crash_dump_completed*,*HANDLE_first_pipe_instance*,*HANDLE_client_process*,*Address_crash_exception_information*,*Address_non_crash_exception_information*,*Address_debug_critical_section*
|
||||
|
||||
Register the initial client using the inherited handles and data provided.
|
||||
@ -141,6 +134,13 @@ establish the Crashpad client environment before running a program.
|
||||
client to register, and exits when all clients have exited, after waiting for
|
||||
any uploads in progress to complete.
|
||||
|
||||
* **--initial-client-fd**=_FD_
|
||||
|
||||
Wait for client requests on _FD_. Either this option or
|
||||
**--trace-parent-with-exception**, but not both, is required. The handler
|
||||
exits when all client connections have been closed. This option is only valid
|
||||
on Linux platforms.
|
||||
|
||||
* **--mach-service**=_SERVICE_
|
||||
|
||||
Check in with the bootstrap server under the name _SERVICE_. Either this
|
||||
@ -198,6 +198,13 @@ establish the Crashpad client environment before running a program.
|
||||
To prevent excessive accumulation of handler processes, _ARGUMENT_ must not
|
||||
be `--monitor-self`.
|
||||
|
||||
* **--no-identify-client-via-url**
|
||||
|
||||
Do not add client-identifying fields to the URL. By default, `"prod"`,
|
||||
`"ver"`, and `"guid"` annotations are added to the upload URL as name-value
|
||||
pairs `"product"`, `"version"`, and `"guid"`, respectively. Using this
|
||||
option disables that behavior.
|
||||
|
||||
* **--no-periodic-tasks**
|
||||
|
||||
Do not scan for new pending crash reports or prune the crash report database.
|
||||
@ -226,6 +233,12 @@ establish the Crashpad client environment before running a program.
|
||||
for use with collection servers that don’t accept uploads compressed in this
|
||||
way.
|
||||
|
||||
* **--no-write-minidump-to-database**
|
||||
|
||||
Do not write the minidump to database. Normally, the minidump is written to
|
||||
database for upload. Use this option with **--write-minidump-to-log** to
|
||||
only write the minidump to log. This option is only available to Android.
|
||||
|
||||
* **--pipe-name**=_PIPE_
|
||||
|
||||
Listen on the given pipe name for connections from clients. _PIPE_ must be of
|
||||
@ -245,17 +258,24 @@ establish the Crashpad client environment before running a program.
|
||||
parent process. This option is only valid on macOS. Use of this option is
|
||||
discouraged. It should not be used absent extraordinary circumstances.
|
||||
|
||||
* **--sanitization-information**=_SANITIZATION-INFORMATION-ADDRESS_
|
||||
|
||||
Provides sanitization settings in a SanitizationInformation struct at
|
||||
_SANITIZATION-INFORMATION-ADDRESS_. This option requires
|
||||
**--trace-parent-with-exception** and is only valid on Linux platforms.
|
||||
|
||||
* **--shared-client-connection**
|
||||
|
||||
Indicates that the file descriptor provided by **--initial-client-fd** is
|
||||
shared among mulitple clients. Using a broker process is not supported for
|
||||
clients using this option. This option is only valid on Linux platforms.
|
||||
|
||||
* **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_
|
||||
|
||||
Causes the handler process to trace its parent process and exit. The parent
|
||||
process should have an ExceptionInformation struct at
|
||||
_EXCEPTION-INFORMATION-ADDRESS_.
|
||||
|
||||
* **--initial-client-fd**=_FD_
|
||||
|
||||
Starts the excetion handler server with an initial ExceptionHandlerClient
|
||||
connected on the socket _FD_. The server will exit when all connected client
|
||||
sockets have been closed.
|
||||
_EXCEPTION-INFORMATION-ADDRESS_. This option is only valid on Linux
|
||||
platforms.
|
||||
|
||||
* **--url**=_URL_
|
||||
|
||||
@ -265,6 +285,19 @@ establish the Crashpad client environment before running a program.
|
||||
library, typically in response to a user requesting this behavior. If this
|
||||
option is not specified, this program will behave as if uploads are disabled.
|
||||
|
||||
* **--use-cros-crash-reporter**
|
||||
|
||||
Causes crash reports to be passed via an in-memory file to
|
||||
`/sbin/crash_reporter` instead of storing them in the database. The database
|
||||
is still used for Crashpad settings. This option is only valid on Chromium
|
||||
OS.
|
||||
|
||||
* **--write-minidump-to-log**
|
||||
|
||||
Write the minidump to log. By default the minidump is only written to
|
||||
database. Use this option with **--no-write-minidump-to-database** to only
|
||||
write the minidump to log. This option is only available to Android.
|
||||
|
||||
* **--help**
|
||||
|
||||
Display help and exit.
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@ -12,15 +12,18 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "util/net/http_transport.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "handler/handler_main.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
|
||||
NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
|
||||
return std::unique_ptr<HTTPTransport>();
|
||||
extern "C" {
|
||||
|
||||
__attribute__((visibility("default"), used)) int CrashpadHandlerMain(
|
||||
int argc,
|
||||
char* argv[]) {
|
||||
return HandlerMain(argc, argv, nullptr);
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
} // namespace crashpad
|
@ -1,182 +0,0 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "handler/fuchsia/crash_report_exception_handler.h"
|
||||
|
||||
#include <zircon/syscalls/exception.h>
|
||||
|
||||
#include "base/fuchsia/fuchsia_logging.h"
|
||||
#include "client/settings.h"
|
||||
#include "minidump/minidump_file_writer.h"
|
||||
#include "minidump/minidump_user_extension_stream_data_source.h"
|
||||
#include "snapshot/fuchsia/process_snapshot_fuchsia.h"
|
||||
#include "util/fuchsia/koid_utilities.h"
|
||||
#include "util/fuchsia/scoped_task_suspend.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ScopedZxTaskResumeAfterException {
|
||||
ScopedZxTaskResumeAfterException(zx_handle_t thread) : thread_(thread) {}
|
||||
~ScopedZxTaskResumeAfterException() {
|
||||
DCHECK_NE(thread_, ZX_HANDLE_INVALID);
|
||||
// Resuming with ZX_RESUME_TRY_NEXT chains to the next handler. In normal
|
||||
// operation, there won't be another beyond this one, which will result in
|
||||
// the kernel terminating the process.
|
||||
zx_status_t status =
|
||||
zx_task_resume(thread_, ZX_RESUME_EXCEPTION | ZX_RESUME_TRY_NEXT);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status) << "zx_task_resume";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
zx_handle_t thread_; // weak
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashReportExceptionHandler::CrashReportExceptionHandler(
|
||||
CrashReportDatabase* database,
|
||||
CrashReportUploadThread* upload_thread,
|
||||
const std::map<std::string, std::string>* process_annotations,
|
||||
const std::map<std::string, base::FilePath>* process_attachments,
|
||||
const UserStreamDataSources* user_stream_data_sources)
|
||||
: database_(database),
|
||||
upload_thread_(upload_thread),
|
||||
process_annotations_(process_annotations),
|
||||
process_attachments_(process_attachments),
|
||||
user_stream_data_sources_(user_stream_data_sources) {}
|
||||
|
||||
CrashReportExceptionHandler::~CrashReportExceptionHandler() {}
|
||||
|
||||
bool CrashReportExceptionHandler::HandleException(uint64_t process_id,
|
||||
uint64_t thread_id) {
|
||||
// TODO(scottmg): This function needs to be instrumented with metrics calls,
|
||||
// https://crashpad.chromium.org/bug/230.
|
||||
|
||||
base::ScopedZxHandle process(GetProcessFromKoid(process_id));
|
||||
if (!process.is_valid()) {
|
||||
// There's no way to zx_task_resume() the thread if the process retrieval
|
||||
// fails. Assume that the process has been already killed, and bail.
|
||||
return false;
|
||||
}
|
||||
|
||||
base::ScopedZxHandle thread(GetChildHandleByKoid(process.get(), thread_id));
|
||||
if (!thread.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return HandleExceptionHandles(process.get(), thread.get());
|
||||
}
|
||||
|
||||
bool CrashReportExceptionHandler::HandleExceptionHandles(zx_handle_t process,
|
||||
zx_handle_t thread) {
|
||||
// Now that the thread has been successfully retrieved, it is possible to
|
||||
// correctly call zx_task_resume() to continue exception processing, even if
|
||||
// something else during this function fails.
|
||||
ScopedZxTaskResumeAfterException resume(thread);
|
||||
|
||||
ProcessSnapshotFuchsia process_snapshot;
|
||||
if (!process_snapshot.Initialize(process)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CrashpadInfoClientOptions client_options;
|
||||
process_snapshot.GetCrashpadOptions(&client_options);
|
||||
|
||||
if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
|
||||
zx_exception_report_t report;
|
||||
zx_status_t status = zx_object_get_info(thread,
|
||||
ZX_INFO_THREAD_EXCEPTION_REPORT,
|
||||
&report,
|
||||
sizeof(report),
|
||||
nullptr,
|
||||
nullptr);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status)
|
||||
<< "zx_object_get_info ZX_INFO_THREAD_EXCEPTION_REPORT";
|
||||
return false;
|
||||
}
|
||||
|
||||
zx_koid_t thread_id = GetKoidForHandle(thread);
|
||||
if (!process_snapshot.InitializeException(thread_id, report)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID client_id;
|
||||
Settings* const settings = database_->GetSettings();
|
||||
if (settings) {
|
||||
// If GetSettings() or GetClientID() fails, something else will log a
|
||||
// message and client_id will be left at its default value, all zeroes,
|
||||
// which is appropriate.
|
||||
settings->GetClientID(&client_id);
|
||||
}
|
||||
|
||||
process_snapshot.SetClientID(client_id);
|
||||
process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
|
||||
|
||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||
CrashReportDatabase::OperationStatus database_status =
|
||||
database_->PrepareNewCrashReport(&new_report);
|
||||
if (database_status != CrashReportDatabase::kNoError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
process_snapshot.SetReportID(new_report->ReportID());
|
||||
|
||||
MinidumpFileWriter minidump;
|
||||
minidump.InitializeFromSnapshot(&process_snapshot);
|
||||
AddUserExtensionStreams(
|
||||
user_stream_data_sources_, &process_snapshot, &minidump);
|
||||
|
||||
if (!minidump.WriteEverything(new_report->Writer())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (process_attachments_) {
|
||||
// Note that attachments are read at this point each time rather than once
|
||||
// so that if the contents of the file has changed it will be re-read for
|
||||
// each upload (e.g. in the case of a log file).
|
||||
for (const auto& it : *process_attachments_) {
|
||||
FileWriter* writer = new_report->AddAttachment(it.first);
|
||||
if (writer) {
|
||||
std::string contents;
|
||||
if (!LoggingReadEntireFile(it.second, &contents)) {
|
||||
// Not being able to read the file isn't considered fatal, and
|
||||
// should not prevent the report from being processed.
|
||||
continue;
|
||||
}
|
||||
writer->Write(contents.data(), contents.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UUID uuid;
|
||||
database_status =
|
||||
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
||||
if (database_status != CrashReportDatabase::kNoError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (upload_thread_) {
|
||||
upload_thread_->ReportPending(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
@ -1,105 +0,0 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
|
||||
#define CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <zircon/types.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/files/file_path.h"
|
||||
#include "base/macros.h"
|
||||
#include "client/crash_report_database.h"
|
||||
#include "handler/crash_report_upload_thread.h"
|
||||
#include "handler/user_stream_data_source.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief An exception handler that writes crash reports for exception messages
|
||||
//! to a CrashReportDatabase.
|
||||
class CrashReportExceptionHandler {
|
||||
public:
|
||||
//! \brief Creates a new object that will store crash reports in \a database.
|
||||
//!
|
||||
//! \param[in] database The database to store crash reports in. Weak.
|
||||
//! \param[in] upload_thread The upload thread to notify when a new crash
|
||||
//! report is written into \a database.
|
||||
//! \param[in] process_annotations A map of annotations to insert as
|
||||
//! process-level annotations into each crash report that is written. Do
|
||||
//! not confuse this with module-level annotations, which are under the
|
||||
//! control of the crashing process, and are used to implement Chrome's
|
||||
//! "crash keys." Process-level annotations are those that are beyond the
|
||||
//! control of the crashing process, which must reliably be set even if
|
||||
//! the process crashes before it’s able to establish its own annotations.
|
||||
//! To interoperate with Breakpad servers, the recommended practice is to
|
||||
//! specify values for the `"prod"` and `"ver"` keys as process
|
||||
//! annotations.
|
||||
//! \param[in] process_attachments A map of file name keys to file paths to be
|
||||
//! included in the report. Each time a report is written, the file paths
|
||||
//! will be read in their entirety and included in the report using the
|
||||
//! file name key as the name in the http upload.
|
||||
//! \param[in] user_stream_data_sources Data sources to be used to extend
|
||||
//! crash reports. For each crash report that is written, the data sources
|
||||
//! are called in turn. These data sources may contribute additional
|
||||
//! minidump streams. `nullptr` if not required.
|
||||
CrashReportExceptionHandler(
|
||||
CrashReportDatabase* database,
|
||||
CrashReportUploadThread* upload_thread,
|
||||
const std::map<std::string, std::string>* process_annotations,
|
||||
const std::map<std::string, base::FilePath>* process_attachments,
|
||||
const UserStreamDataSources* user_stream_data_sources);
|
||||
|
||||
~CrashReportExceptionHandler();
|
||||
|
||||
//! \brief Called when the exception handler server has caught an exception
|
||||
//! and wants a crash dump to be taken.
|
||||
//!
|
||||
//! This function is expected to call `zx_task_resume()` in order to complete
|
||||
//! handling of the exception.
|
||||
//!
|
||||
//! \param[in] process_id The koid of the process which sustained the
|
||||
//! exception.
|
||||
//! \param[in] thread_id The koid of the thread which sustained the exception.
|
||||
//! \return `true` on success, or `false` with an error logged.
|
||||
bool HandleException(uint64_t process_id, uint64_t thread_id);
|
||||
|
||||
//! \brief Called when the exception handler server has caught an exception
|
||||
//! and wants a crash dump to be taken.
|
||||
//!
|
||||
//! This function is expected to call `zx_task_resume()` in order to complete
|
||||
//! handling of the exception.
|
||||
//!
|
||||
//! \param[in] process The handle to the process which sustained the
|
||||
//! exception.
|
||||
//! \param[in] thread The handle to the thread of \a process which sustained
|
||||
//! the exception.
|
||||
//! \return `true` on success, or `false` with an error logged.
|
||||
bool HandleExceptionHandles(zx_handle_t process, zx_handle_t thread);
|
||||
|
||||
private:
|
||||
CrashReportDatabase* database_; // weak
|
||||
CrashReportUploadThread* upload_thread_; // weak
|
||||
const std::map<std::string, std::string>* process_annotations_; // weak
|
||||
const std::map<std::string, base::FilePath>* process_attachments_; // weak
|
||||
const UserStreamDataSources* user_stream_data_sources_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_HANDLER_FUCHSIA_CRASH_REPORT_EXCEPTION_HANDLER_H_
|
@ -1,60 +0,0 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "handler/fuchsia/exception_handler_server.h"
|
||||
|
||||
#include <zircon/syscalls/exception.h>
|
||||
#include <zircon/syscalls/port.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/fuchsia/fuchsia_logging.h"
|
||||
#include "base/logging.h"
|
||||
#include "handler/fuchsia/crash_report_exception_handler.h"
|
||||
#include "util/fuchsia/system_exception_port_key.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
ExceptionHandlerServer::ExceptionHandlerServer(
|
||||
base::ScopedZxHandle root_job,
|
||||
base::ScopedZxHandle exception_port)
|
||||
: root_job_(std::move(root_job)),
|
||||
exception_port_(std::move(exception_port)) {}
|
||||
|
||||
ExceptionHandlerServer::~ExceptionHandlerServer() = default;
|
||||
|
||||
void ExceptionHandlerServer::Run(CrashReportExceptionHandler* handler) {
|
||||
while (true) {
|
||||
zx_port_packet_t packet;
|
||||
zx_status_t status =
|
||||
zx_port_wait(exception_port_.get(), ZX_TIME_INFINITE, &packet);
|
||||
if (status != ZX_OK) {
|
||||
ZX_LOG(ERROR, status) << "zx_port_wait, aborting";
|
||||
return;
|
||||
}
|
||||
|
||||
if (packet.key != kSystemExceptionPortKey) {
|
||||
LOG(ERROR) << "unexpected packet key, ignoring";
|
||||
continue;
|
||||
}
|
||||
|
||||
bool result =
|
||||
handler->HandleException(packet.exception.pid, packet.exception.tid);
|
||||
if (!result) {
|
||||
LOG(ERROR) << "HandleException failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
@ -1,55 +0,0 @@
|
||||
// Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_
|
||||
#define CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/fuchsia/scoped_zx_handle.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class CrashReportExceptionHandler;
|
||||
|
||||
//! \brief Runs the main exception-handling server in Crashpad's handler
|
||||
//! process.
|
||||
class ExceptionHandlerServer {
|
||||
public:
|
||||
//! \brief Constructs an ExceptionHandlerServer object.
|
||||
//!
|
||||
//! \param[in] root_job The root of the tree of processes that will be handled
|
||||
//! by this server. It is assumed that \a exception_port is the exception
|
||||
//! port of this job.
|
||||
//! \param[in] exception_port The exception port that this server will
|
||||
//! monitor.
|
||||
ExceptionHandlerServer(base::ScopedZxHandle root_job,
|
||||
base::ScopedZxHandle exception_port);
|
||||
~ExceptionHandlerServer();
|
||||
|
||||
//! \brief Runs the exception-handling server.
|
||||
//!
|
||||
//! \param[in] handler The handler to which the exceptions are delegated when
|
||||
//! they are caught in Run(). Ownership is not transferred.
|
||||
void Run(CrashReportExceptionHandler* handler);
|
||||
|
||||
private:
|
||||
base::ScopedZxHandle root_job_;
|
||||
base::ScopedZxHandle exception_port_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_HANDLER_FUCHSIA_EXCEPTION_HANDLER_SERVER_H_
|
@ -28,6 +28,7 @@
|
||||
'../minidump/minidump.gyp:crashpad_minidump',
|
||||
'../snapshot/snapshot.gyp:crashpad_snapshot',
|
||||
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||
'../third_party/zlib/zlib.gyp:zlib',
|
||||
'../tools/tools.gyp:crashpad_tool_support',
|
||||
'../util/util.gyp:crashpad_util',
|
||||
],
|
||||
@ -39,6 +40,8 @@
|
||||
'crash_report_upload_thread.h',
|
||||
'handler_main.cc',
|
||||
'handler_main.h',
|
||||
'linux/capture_snapshot.cc',
|
||||
'linux/capture_snapshot.h',
|
||||
'linux/crash_report_exception_handler.cc',
|
||||
'linux/crash_report_exception_handler.h',
|
||||
'linux/exception_handler_server.cc',
|
||||
|
@ -56,6 +56,10 @@
|
||||
#include "util/string/split_string.h"
|
||||
#include "util/synchronization/semaphore.h"
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
#include "handler/linux/cros_crash_report_exception_handler.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
#include <unistd.h>
|
||||
|
||||
@ -70,8 +74,8 @@
|
||||
#include "handler/mac/crash_report_exception_handler.h"
|
||||
#include "handler/mac/exception_handler_server.h"
|
||||
#include "handler/mac/file_limit_annotation.h"
|
||||
#include "util/mach/bootstrap.h"
|
||||
#include "util/mach/child_port_handshake.h"
|
||||
#include "util/mach/mach_extensions.h"
|
||||
#include "util/posix/close_stdio.h"
|
||||
#include "util/posix/signals.h"
|
||||
#elif defined(OS_WIN)
|
||||
@ -82,12 +86,6 @@
|
||||
#include "util/win/handle.h"
|
||||
#include "util/win/initial_client_data.h"
|
||||
#include "util/win/session_end_watcher.h"
|
||||
#elif defined(OS_FUCHSIA)
|
||||
#include <zircon/process.h>
|
||||
#include <zircon/processargs.h>
|
||||
|
||||
#include "handler/fuchsia/crash_report_exception_handler.h"
|
||||
#include "handler/fuchsia/exception_handler_server.h"
|
||||
#elif defined(OS_LINUX)
|
||||
#include "handler/linux/crash_report_exception_handler.h"
|
||||
#include "handler/linux/exception_handler_server.h"
|
||||
@ -118,6 +116,9 @@ void Usage(const base::FilePath& me) {
|
||||
" Address_debug_critical_section\n"
|
||||
" use precreated data to register initial client\n"
|
||||
#endif // OS_WIN
|
||||
#if defined(OS_ANDROID) || defined(OS_LINUX)
|
||||
" --initial-client-fd=FD a socket connected to a client.\n"
|
||||
#endif // OS_ANDROID || OS_LINUX
|
||||
#if defined(OS_MACOSX)
|
||||
" --mach-service=SERVICE register SERVICE with the bootstrap server\n"
|
||||
#endif // OS_MACOSX
|
||||
@ -133,6 +134,10 @@ void Usage(const base::FilePath& me) {
|
||||
" --no-periodic-tasks don't scan for new reports or prune the database\n"
|
||||
" --no-rate-limit don't rate limit crash uploads\n"
|
||||
" --no-upload-gzip don't use gzip compression when uploading\n"
|
||||
#if defined(OS_ANDROID)
|
||||
" --no-write-minidump-to-database\n"
|
||||
" don't write minidump to database\n"
|
||||
#endif // OS_ANDROID
|
||||
#if defined(OS_WIN)
|
||||
" --pipe-name=PIPE communicate with the client over PIPE\n"
|
||||
#endif // OS_WIN
|
||||
@ -141,14 +146,31 @@ void Usage(const base::FilePath& me) {
|
||||
" reset the server's exception handler to default\n"
|
||||
#endif // OS_MACOSX
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
" --sanitization-information=SANITIZATION_INFORMATION_ADDRESS\n"
|
||||
" the address of a SanitizationInformation struct.\n"
|
||||
" --shared-client-connection the file descriptor provided by\n"
|
||||
" --initial-client-fd is shared among multiple\n"
|
||||
" clients\n"
|
||||
" --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n"
|
||||
" request a dump for the handler's parent process\n"
|
||||
" --initial-client-fd=FD a socket connected to a client.\n"
|
||||
" --sanitization_information=SANITIZATION_INFORMATION_ADDRESS\n"
|
||||
" the address of a SanitizationInformation struct.\n"
|
||||
#endif // OS_LINUX || OS_ANDROID
|
||||
" --url=URL send crash reports to this Breakpad server URL,\n"
|
||||
" only if uploads are enabled for the database\n"
|
||||
#if defined(OS_CHROMEOS)
|
||||
" --use-cros-crash-reporter\n"
|
||||
" pass crash reports to /sbin/crash_reporter\n"
|
||||
" instead of storing them in the database\n"
|
||||
" --minidump-dir-for-tests=TEST_MINIDUMP_DIR\n"
|
||||
" causes /sbin/crash_reporter to leave dumps in\n"
|
||||
" this directory instead of the normal location\n"
|
||||
" --always-allow-feedback\n"
|
||||
" pass the --always_allow_feedback flag to\n"
|
||||
" crash_reporter, thus skipping metrics consent\n"
|
||||
" checks\n"
|
||||
#endif // OS_CHROMEOS
|
||||
#if defined(OS_ANDROID)
|
||||
" --write-minidump-to-log write minidump to log\n"
|
||||
#endif // OS_ANDROID
|
||||
" --help display this help and exit\n"
|
||||
" --version output version information and exit\n",
|
||||
me.value().c_str());
|
||||
@ -168,8 +190,13 @@ struct Options {
|
||||
bool reset_own_crash_exception_port_to_system_default;
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
VMAddress exception_information_address;
|
||||
int initial_client_fd;
|
||||
VMAddress sanitization_information_address;
|
||||
int initial_client_fd;
|
||||
bool shared_client_connection;
|
||||
#if defined(OS_ANDROID)
|
||||
bool write_minidump_to_log;
|
||||
bool write_minidump_to_database;
|
||||
#endif // OS_ANDROID
|
||||
#elif defined(OS_WIN)
|
||||
std::string pipe_name;
|
||||
InitialClientData initial_client_data;
|
||||
@ -179,6 +206,11 @@ struct Options {
|
||||
bool periodic_tasks;
|
||||
bool rate_limit;
|
||||
bool upload_gzip;
|
||||
#if defined(OS_CHROMEOS)
|
||||
bool use_cros_crash_reporter = false;
|
||||
base::FilePath minidump_dir_for_tests;
|
||||
bool always_allow_feedback = false;
|
||||
#endif // OS_CHROMEOS
|
||||
};
|
||||
|
||||
// Splits |key_value| on '=' and inserts the resulting key and value into |map|.
|
||||
@ -238,8 +270,6 @@ class CallMetricsRecordNormalExit {
|
||||
|
||||
#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
|
||||
Signals::OldActions g_old_crash_signal_handlers;
|
||||
|
||||
void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||
MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
|
||||
|
||||
@ -274,9 +304,7 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||
}
|
||||
Metrics::HandlerCrashed(metrics_code);
|
||||
|
||||
struct sigaction* old_action =
|
||||
g_old_crash_signal_handlers.ActionForSignal(sig);
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action);
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||
}
|
||||
|
||||
void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||
@ -284,13 +312,13 @@ void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
|
||||
void ReinstallCrashHandler() {
|
||||
// This is used to re-enable the metrics-recording crash handler after
|
||||
// MonitorSelf() sets up a Crashpad exception handler. On macOS, the
|
||||
// metrics-recording handler uses signals and the Crashpad handler uses Mach
|
||||
// exceptions, so there’s nothing to re-enable.
|
||||
// On Linux, the signal handler installed by StartHandler() restores the
|
||||
// previously installed signal handler by default.
|
||||
}
|
||||
|
||||
void InstallCrashHandler() {
|
||||
@ -300,6 +328,8 @@ void InstallCrashHandler() {
|
||||
Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
|
||||
struct ResetSIGTERMTraits {
|
||||
static struct sigaction* InvalidValue() {
|
||||
return nullptr;
|
||||
@ -324,21 +354,6 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
|
||||
g_exception_handler_server->Stop();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void ReinstallCrashHandler() {
|
||||
// This is used to re-enable the metrics-recording crash handler after
|
||||
// MonitorSelf() sets up a Crashpad signal handler.
|
||||
Signals::InstallCrashHandlers(
|
||||
HandleCrashSignal, 0, &g_old_crash_signal_handlers);
|
||||
}
|
||||
|
||||
void InstallCrashHandler() {
|
||||
ReinstallCrashHandler();
|
||||
|
||||
Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
|
||||
}
|
||||
|
||||
#endif // OS_MACOSX
|
||||
|
||||
#elif defined(OS_WIN)
|
||||
@ -396,22 +411,6 @@ void InstallCrashHandler() {
|
||||
ALLOW_UNUSED_LOCAL(terminate_handler);
|
||||
}
|
||||
|
||||
#elif defined(OS_FUCHSIA)
|
||||
|
||||
void InstallCrashHandler() {
|
||||
// There's nothing to do here. Crashes in this process will already be caught
|
||||
// here because this handler process is in the same job that has had its
|
||||
// exception port bound.
|
||||
|
||||
// TODO(scottmg): This should collect metrics on handler crashes, at a
|
||||
// minimum. https://crashpad.chromium.org/bug/230.
|
||||
}
|
||||
|
||||
void ReinstallCrashHandler() {
|
||||
// TODO(scottmg): Fuchsia: https://crashpad.chromium.org/bug/196
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
#endif // OS_MACOSX
|
||||
|
||||
void MonitorSelf(const Options& options) {
|
||||
@ -448,7 +447,7 @@ void MonitorSelf(const Options& options) {
|
||||
// instance of crashpad_handler to be writing metrics at a time, and it should
|
||||
// be the primary instance.
|
||||
CrashpadClient crashpad_client;
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
#if defined(OS_ANDROID)
|
||||
if (!crashpad_client.StartHandlerAtCrash(executable_path,
|
||||
options.database,
|
||||
base::FilePath(),
|
||||
@ -500,6 +499,12 @@ class ScopedStoppable {
|
||||
int HandlerMain(int argc,
|
||||
char* argv[],
|
||||
const UserStreamDataSources* user_stream_sources) {
|
||||
#if defined(OS_CHROMEOS)
|
||||
if (freopen("/var/log/chrome/chrome", "a", stderr) == nullptr) {
|
||||
PLOG(ERROR) << "Failed to redirect stderr to /var/log/chrome/chrome";
|
||||
}
|
||||
#endif
|
||||
|
||||
InstallCrashHandler();
|
||||
CallMetricsRecordNormalExit metrics_record_normal_exit;
|
||||
|
||||
@ -518,6 +523,9 @@ int HandlerMain(int argc,
|
||||
#if defined(OS_WIN)
|
||||
kOptionInitialClientData,
|
||||
#endif // OS_WIN
|
||||
#if defined(OS_ANDROID) || defined(OS_LINUX)
|
||||
kOptionInitialClientFD,
|
||||
#endif // OS_ANDROID || OS_LINUX
|
||||
#if defined(OS_MACOSX)
|
||||
kOptionMachService,
|
||||
#endif // OS_MACOSX
|
||||
@ -529,6 +537,9 @@ int HandlerMain(int argc,
|
||||
kOptionNoPeriodicTasks,
|
||||
kOptionNoRateLimit,
|
||||
kOptionNoUploadGzip,
|
||||
#if defined(OS_ANDROID)
|
||||
kOptionNoWriteMinidumpToDatabase,
|
||||
#endif // OS_ANDROID
|
||||
#if defined(OS_WIN)
|
||||
kOptionPipeName,
|
||||
#endif // OS_WIN
|
||||
@ -536,11 +547,19 @@ int HandlerMain(int argc,
|
||||
kOptionResetOwnCrashExceptionPortToSystemDefault,
|
||||
#endif // OS_MACOSX
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
kOptionTraceParentWithException,
|
||||
kOptionInitialClientFD,
|
||||
kOptionSanitizationInformation,
|
||||
kOptionSharedClientConnection,
|
||||
kOptionTraceParentWithException,
|
||||
#endif
|
||||
kOptionURL,
|
||||
#if defined(OS_CHROMEOS)
|
||||
kOptionUseCrosCrashReporter,
|
||||
kOptionMinidumpDirForTests,
|
||||
kOptionAlwaysAllowFeedback,
|
||||
#endif // OS_CHROMEOS
|
||||
#if defined(OS_ANDROID)
|
||||
kOptionWriteMinidumpToLog,
|
||||
#endif // OS_ANDROID
|
||||
|
||||
// Standard options.
|
||||
kOptionHelp = -2,
|
||||
@ -559,6 +578,9 @@ int HandlerMain(int argc,
|
||||
nullptr,
|
||||
kOptionInitialClientData},
|
||||
#endif // OS_MACOSX
|
||||
#if defined(OS_ANDROID) || defined(OS_LINUX)
|
||||
{"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
|
||||
#endif // OS_ANDROID || OS_LINUX
|
||||
#if defined(OS_MACOSX)
|
||||
{"mach-service", required_argument, nullptr, kOptionMachService},
|
||||
#endif // OS_MACOSX
|
||||
@ -579,6 +601,12 @@ int HandlerMain(int argc,
|
||||
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
|
||||
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
||||
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
|
||||
#if defined(OS_ANDROID)
|
||||
{"no-write-minidump-to-database",
|
||||
no_argument,
|
||||
nullptr,
|
||||
kOptionNoWriteMinidumpToDatabase},
|
||||
#endif // OS_ANDROID
|
||||
#if defined(OS_WIN)
|
||||
{"pipe-name", required_argument, nullptr, kOptionPipeName},
|
||||
#endif // OS_WIN
|
||||
@ -589,17 +617,37 @@ int HandlerMain(int argc,
|
||||
kOptionResetOwnCrashExceptionPortToSystemDefault},
|
||||
#endif // OS_MACOSX
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
{"trace-parent-with-exception",
|
||||
required_argument,
|
||||
nullptr,
|
||||
kOptionTraceParentWithException},
|
||||
{"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
|
||||
{"sanitization-information",
|
||||
required_argument,
|
||||
nullptr,
|
||||
kOptionSanitizationInformation},
|
||||
{"shared-client-connection",
|
||||
no_argument,
|
||||
nullptr,
|
||||
kOptionSharedClientConnection},
|
||||
{"trace-parent-with-exception",
|
||||
required_argument,
|
||||
nullptr,
|
||||
kOptionTraceParentWithException},
|
||||
#endif // OS_LINUX || OS_ANDROID
|
||||
{"url", required_argument, nullptr, kOptionURL},
|
||||
#if defined(OS_CHROMEOS)
|
||||
{"use-cros-crash-reporter",
|
||||
no_argument,
|
||||
nullptr,
|
||||
kOptionUseCrosCrashReporter},
|
||||
{"minidump-dir-for-tests",
|
||||
required_argument,
|
||||
nullptr,
|
||||
kOptionMinidumpDirForTests},
|
||||
{"always-allow-feedback",
|
||||
no_argument,
|
||||
nullptr,
|
||||
kOptionAlwaysAllowFeedback},
|
||||
#endif // OS_CHROMEOS
|
||||
#if defined(OS_ANDROID)
|
||||
{"write-minidump-to-log", no_argument, nullptr, kOptionWriteMinidumpToLog},
|
||||
#endif // OS_ANDROID
|
||||
{"help", no_argument, nullptr, kOptionHelp},
|
||||
{"version", no_argument, nullptr, kOptionVersion},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
@ -610,13 +658,14 @@ int HandlerMain(int argc,
|
||||
options.handshake_fd = -1;
|
||||
#endif
|
||||
options.identify_client_via_url = true;
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
options.initial_client_fd = kInvalidFileHandle;
|
||||
#endif
|
||||
options.periodic_tasks = true;
|
||||
options.rate_limit = true;
|
||||
options.upload_gzip = true;
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
options.exception_information_address = 0;
|
||||
options.initial_client_fd = kInvalidFileHandle;
|
||||
options.sanitization_information_address = 0;
|
||||
#if defined(OS_ANDROID)
|
||||
options.write_minidump_to_database = true;
|
||||
#endif
|
||||
|
||||
int opt;
|
||||
@ -658,6 +707,15 @@ int HandlerMain(int argc,
|
||||
break;
|
||||
}
|
||||
#endif // OS_WIN
|
||||
#if defined(OS_ANDROID) || defined(OS_LINUX)
|
||||
case kOptionInitialClientFD: {
|
||||
if (!base::StringToInt(optarg, &options.initial_client_fd)) {
|
||||
ToolSupport::UsageHint(me, "failed to parse --initial-client-fd");
|
||||
return ExitFailure();
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // OS_ANDROID || OS_LINUX
|
||||
case kOptionMetrics: {
|
||||
options.metrics_dir = base::FilePath(
|
||||
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
|
||||
@ -695,6 +753,12 @@ int HandlerMain(int argc,
|
||||
options.upload_gzip = false;
|
||||
break;
|
||||
}
|
||||
#if defined(OS_ANDROID)
|
||||
case kOptionNoWriteMinidumpToDatabase: {
|
||||
options.write_minidump_to_database = false;
|
||||
break;
|
||||
}
|
||||
#endif // OS_ANDROID
|
||||
#if defined(OS_WIN)
|
||||
case kOptionPipeName: {
|
||||
options.pipe_name = optarg;
|
||||
@ -708,21 +772,6 @@ int HandlerMain(int argc,
|
||||
}
|
||||
#endif // OS_MACOSX
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
case kOptionTraceParentWithException: {
|
||||
if (!StringToNumber(optarg, &options.exception_information_address)) {
|
||||
ToolSupport::UsageHint(
|
||||
me, "failed to parse --trace-parent-with-exception");
|
||||
return ExitFailure();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kOptionInitialClientFD: {
|
||||
if (!base::StringToInt(optarg, &options.initial_client_fd)) {
|
||||
ToolSupport::UsageHint(me, "failed to parse --initial-client-fd");
|
||||
return ExitFailure();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kOptionSanitizationInformation: {
|
||||
if (!StringToNumber(optarg,
|
||||
&options.sanitization_information_address)) {
|
||||
@ -732,11 +781,44 @@ int HandlerMain(int argc,
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kOptionSharedClientConnection: {
|
||||
options.shared_client_connection = true;
|
||||
break;
|
||||
}
|
||||
case kOptionTraceParentWithException: {
|
||||
if (!StringToNumber(optarg, &options.exception_information_address)) {
|
||||
ToolSupport::UsageHint(
|
||||
me, "failed to parse --trace-parent-with-exception");
|
||||
return ExitFailure();
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif // OS_LINUX || OS_ANDROID
|
||||
case kOptionURL: {
|
||||
options.url = optarg;
|
||||
break;
|
||||
}
|
||||
#if defined(OS_CHROMEOS)
|
||||
case kOptionUseCrosCrashReporter: {
|
||||
options.use_cros_crash_reporter = true;
|
||||
break;
|
||||
}
|
||||
case kOptionMinidumpDirForTests: {
|
||||
options.minidump_dir_for_tests = base::FilePath(
|
||||
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
|
||||
break;
|
||||
}
|
||||
case kOptionAlwaysAllowFeedback: {
|
||||
options.always_allow_feedback = true;
|
||||
break;
|
||||
}
|
||||
#endif // OS_CHROMEOS
|
||||
#if defined(OS_ANDROID)
|
||||
case kOptionWriteMinidumpToLog: {
|
||||
options.write_minidump_to_log = true;
|
||||
break;
|
||||
}
|
||||
#endif // OS_ANDROID
|
||||
case kOptionHelp: {
|
||||
Usage(me);
|
||||
MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
|
||||
@ -781,7 +863,7 @@ int HandlerMain(int argc,
|
||||
if (!options.exception_information_address &&
|
||||
options.initial_client_fd == kInvalidFileHandle) {
|
||||
ToolSupport::UsageHint(
|
||||
me, "--trace-parent-with-exception or --initial_client_fd is required");
|
||||
me, "--trace-parent-with-exception or --initial-client-fd is required");
|
||||
return ExitFailure();
|
||||
}
|
||||
if (options.sanitization_information_address &&
|
||||
@ -791,6 +873,20 @@ int HandlerMain(int argc,
|
||||
"--sanitization_information requires --trace-parent-with-exception");
|
||||
return ExitFailure();
|
||||
}
|
||||
if (options.shared_client_connection &&
|
||||
options.initial_client_fd == kInvalidFileHandle) {
|
||||
ToolSupport::UsageHint(
|
||||
me, "--shared-client-connection requires --initial-client-fd");
|
||||
return ExitFailure();
|
||||
}
|
||||
#if defined(OS_ANDROID)
|
||||
if (!options.write_minidump_to_log && !options.write_minidump_to_database) {
|
||||
ToolSupport::UsageHint(me,
|
||||
"--no_write_minidump_to_database is required to use "
|
||||
"with --write_minidump_to_log.");
|
||||
ExitFailure();
|
||||
}
|
||||
#endif // OS_ANDROID
|
||||
#endif // OS_MACOSX
|
||||
|
||||
if (options.database.empty()) {
|
||||
@ -856,24 +952,62 @@ int HandlerMain(int argc,
|
||||
upload_thread.Get()->Start();
|
||||
}
|
||||
|
||||
CrashReportExceptionHandler exception_handler(
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
std::unique_ptr<ExceptionHandlerServer::Delegate> exception_handler;
|
||||
#else
|
||||
std::unique_ptr<CrashReportExceptionHandler> exception_handler;
|
||||
#endif
|
||||
|
||||
#if defined(OS_CHROMEOS)
|
||||
if (options.use_cros_crash_reporter) {
|
||||
auto cros_handler = std::make_unique<CrosCrashReportExceptionHandler>(
|
||||
database.get(),
|
||||
&options.annotations,
|
||||
user_stream_sources);
|
||||
|
||||
if (!options.minidump_dir_for_tests.empty()) {
|
||||
cros_handler->SetDumpDir(options.minidump_dir_for_tests);
|
||||
}
|
||||
|
||||
if (options.always_allow_feedback) {
|
||||
cros_handler->SetAlwaysAllowFeedback();
|
||||
}
|
||||
|
||||
exception_handler = std::move(cros_handler);
|
||||
} else {
|
||||
exception_handler = std::make_unique<CrashReportExceptionHandler>(
|
||||
database.get(),
|
||||
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
|
||||
&options.annotations,
|
||||
true,
|
||||
false,
|
||||
user_stream_sources);
|
||||
}
|
||||
#else
|
||||
exception_handler = std::make_unique<CrashReportExceptionHandler>(
|
||||
database.get(),
|
||||
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
|
||||
&options.annotations,
|
||||
#if defined(OS_FUCHSIA)
|
||||
// TODO(scottmg): Process level file attachments, and for all platforms.
|
||||
nullptr,
|
||||
#endif
|
||||
#if defined(OS_ANDROID)
|
||||
options.write_minidump_to_database,
|
||||
options.write_minidump_to_log,
|
||||
#endif // OS_ANDROID
|
||||
#if defined(OS_LINUX)
|
||||
true,
|
||||
false,
|
||||
#endif // OS_LINUX
|
||||
user_stream_sources);
|
||||
#endif // OS_CHROMEOS
|
||||
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
if (options.exception_information_address) {
|
||||
ClientInformation info;
|
||||
ExceptionHandlerProtocol::ClientInformation info;
|
||||
info.exception_information_address = options.exception_information_address;
|
||||
info.sanitization_information_address =
|
||||
options.sanitization_information_address;
|
||||
return exception_handler.HandleException(getppid(), info) ? EXIT_SUCCESS
|
||||
: ExitFailure();
|
||||
return exception_handler->HandleException(getppid(), geteuid(), info)
|
||||
? EXIT_SUCCESS
|
||||
: ExitFailure();
|
||||
}
|
||||
#endif // OS_LINUX || OS_ANDROID
|
||||
|
||||
@ -937,27 +1071,6 @@ int HandlerMain(int argc,
|
||||
if (!options.pipe_name.empty()) {
|
||||
exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
|
||||
}
|
||||
#elif defined(OS_FUCHSIA)
|
||||
// These handles are logically "moved" into these variables when retrieved by
|
||||
// zx_take_startup_handle(). Both are given to ExceptionHandlerServer which
|
||||
// owns them in this process. There is currently no "connect-later" mode on
|
||||
// Fuchsia, all the binding must be done by the client before starting
|
||||
// crashpad_handler.
|
||||
base::ScopedZxHandle root_job(zx_take_startup_handle(PA_HND(PA_USER0, 0)));
|
||||
if (!root_job.is_valid()) {
|
||||
LOG(ERROR) << "no process handle passed in startup handle 0";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
base::ScopedZxHandle exception_port(
|
||||
zx_take_startup_handle(PA_HND(PA_USER0, 1)));
|
||||
if (!exception_port.is_valid()) {
|
||||
LOG(ERROR) << "no exception port handle passed in startup handle 1";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ExceptionHandlerServer exception_handler_server(std::move(root_job),
|
||||
std::move(exception_port));
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
ExceptionHandlerServer exception_handler_server;
|
||||
#endif // OS_MACOSX
|
||||
@ -978,17 +1091,18 @@ int HandlerMain(int argc,
|
||||
#if defined(OS_WIN)
|
||||
if (options.initial_client_data.IsValid()) {
|
||||
exception_handler_server.InitializeWithInheritedDataForInitialClient(
|
||||
options.initial_client_data, &exception_handler);
|
||||
options.initial_client_data, exception_handler.get());
|
||||
}
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
if (options.initial_client_fd == kInvalidFileHandle ||
|
||||
!exception_handler_server.InitializeWithClient(
|
||||
ScopedFileHandle(options.initial_client_fd))) {
|
||||
!exception_handler_server.InitializeWithClient(
|
||||
ScopedFileHandle(options.initial_client_fd),
|
||||
options.shared_client_connection)) {
|
||||
return ExitFailure();
|
||||
}
|
||||
#endif // OS_WIN
|
||||
|
||||
exception_handler_server.Run(&exception_handler);
|
||||
exception_handler_server.Run(exception_handler.get());
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
||||
#define CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
||||
|
||||
#include "build/build_config.h"
|
||||
#include "handler/user_stream_data_source.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -34,6 +35,16 @@ int HandlerMain(int argc,
|
||||
char* argv[],
|
||||
const UserStreamDataSources* user_stream_sources);
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
//! \brief The `main()` entry point for Android libraries.
|
||||
//!
|
||||
//! This symbol is the entry point for crashpad when it is dynamically loaded
|
||||
//! using /system/bin/linker.
|
||||
//!
|
||||
//! \sa CrashpadClient::StartHandlerWithLinkerAtCrash()
|
||||
extern "C" int CrashpadHandlerMain(int argc, char* argv[]);
|
||||
#endif
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
||||
|
119
handler/linux/capture_snapshot.cc
Normal file
119
handler/linux/capture_snapshot.cc
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "handler/linux/capture_snapshot.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "snapshot/crashpad_info_client_options.h"
|
||||
#include "snapshot/sanitized/sanitization_information.h"
|
||||
#include "util/misc/metrics.h"
|
||||
#include "util/misc/tri_state.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
bool CaptureSnapshot(
|
||||
PtraceConnection* connection,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
const std::map<std::string, std::string>& process_annotations,
|
||||
uid_t client_uid,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
std::unique_ptr<ProcessSnapshotLinux>* snapshot,
|
||||
std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot) {
|
||||
std::unique_ptr<ProcessSnapshotLinux> process_snapshot(
|
||||
new ProcessSnapshotLinux());
|
||||
if (!process_snapshot->Initialize(connection)) {
|
||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
pid_t local_requesting_thread_id = -1;
|
||||
if (requesting_thread_stack_address) {
|
||||
local_requesting_thread_id = process_snapshot->FindThreadWithStackAddress(
|
||||
requesting_thread_stack_address);
|
||||
}
|
||||
|
||||
if (requesting_thread_id) {
|
||||
*requesting_thread_id = local_requesting_thread_id;
|
||||
}
|
||||
|
||||
if (!process_snapshot->InitializeException(info.exception_information_address,
|
||||
local_requesting_thread_id)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kExceptionInitializationFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
Metrics::ExceptionCode(process_snapshot->Exception()->Exception());
|
||||
|
||||
CrashpadInfoClientOptions client_options;
|
||||
process_snapshot->GetCrashpadOptions(&client_options);
|
||||
if (client_options.crashpad_handler_behavior == TriState::kDisabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& p : process_annotations) {
|
||||
process_snapshot->AddAnnotation(p.first, p.second);
|
||||
}
|
||||
|
||||
if (info.sanitization_information_address) {
|
||||
SanitizationInformation sanitization_info;
|
||||
ProcessMemoryRange range;
|
||||
if (!range.Initialize(connection->Memory(), connection->Is64Bit()) ||
|
||||
!range.Read(info.sanitization_information_address,
|
||||
sizeof(sanitization_info),
|
||||
&sanitization_info)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kSanitizationInitializationFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto annotations_whitelist = std::make_unique<std::vector<std::string>>();
|
||||
auto memory_range_whitelist =
|
||||
std::make_unique<std::vector<std::pair<VMAddress, VMAddress>>>();
|
||||
if (!ReadAnnotationsWhitelist(
|
||||
range,
|
||||
sanitization_info.annotations_whitelist_address,
|
||||
annotations_whitelist.get()) ||
|
||||
!ReadMemoryRangeWhitelist(
|
||||
range,
|
||||
sanitization_info.memory_range_whitelist_address,
|
||||
memory_range_whitelist.get())) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kSanitizationInitializationFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<ProcessSnapshotSanitized> sanitized(
|
||||
new ProcessSnapshotSanitized());
|
||||
if (!sanitized->Initialize(process_snapshot.get(),
|
||||
sanitization_info.annotations_whitelist_address
|
||||
? std::move(annotations_whitelist)
|
||||
: nullptr,
|
||||
std::move(memory_range_whitelist),
|
||||
sanitization_info.target_module_address,
|
||||
sanitization_info.sanitize_stacks)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kSkippedDueToSanitization);
|
||||
return false;
|
||||
}
|
||||
*sanitized_snapshot = std::move(sanitized);
|
||||
}
|
||||
|
||||
*snapshot = std::move(process_snapshot);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
67
handler/linux/capture_snapshot.h
Normal file
67
handler/linux/capture_snapshot.h
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
|
||||
#define CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "snapshot/linux/process_snapshot_linux.h"
|
||||
#include "snapshot/sanitized/process_snapshot_sanitized.h"
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
#include "util/linux/ptrace_connection.h"
|
||||
#include "util/misc/address_types.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief Captures a snapshot of a client over \a connection.
|
||||
//!
|
||||
//! \param[in] connection A PtraceConnection to the client to snapshot.
|
||||
//! \param[in] info Information about the client configuring the snapshot.
|
||||
//! \param[in] process_annotations A map of annotations to insert as
|
||||
//! process-level annotations into the snapshot.
|
||||
//! \param[in] client_uid The client's user ID.
|
||||
//! \param[in] requesting_thread_stack_address An address on the stack of the
|
||||
//! thread requesting the snapshot. If \a info includes an exception
|
||||
//! address, the exception will be assigned to the thread whose stack
|
||||
//! address range contains this address. If 0, \a requesting_thread_id will
|
||||
//! be -1.
|
||||
//! \param[out] requesting_thread_id The thread ID of the thread corresponding
|
||||
//! to \a requesting_thread_stack_address. Set to -1 if the thread ID could
|
||||
//! not be determined. Optional.
|
||||
//! \param[out] process_snapshot A snapshot of the client process, valid if this
|
||||
//! function returns `true`.
|
||||
//! \param[out] sanitized_snapshot A sanitized snapshot of the client process,
|
||||
//! valid if this function returns `true` and sanitization was requested in
|
||||
//! \a info.
|
||||
//! \return `true` if \a process_snapshot was successfully created. A message
|
||||
//! will be logged on failure, but not if the snapshot was skipped because
|
||||
//! handling was disabled by CrashpadInfoClientOptions.
|
||||
bool CaptureSnapshot(
|
||||
PtraceConnection* connection,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
const std::map<std::string, std::string>& process_annotations,
|
||||
uid_t client_uid,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
std::unique_ptr<ProcessSnapshotLinux>* process_snapshot,
|
||||
std::unique_ptr<ProcessSnapshotSanitized>* sanitized_snapshot);
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_
|
@ -14,38 +14,75 @@
|
||||
|
||||
#include "handler/linux/crash_report_exception_handler.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "client/settings.h"
|
||||
#include "handler/linux/capture_snapshot.h"
|
||||
#include "minidump/minidump_file_writer.h"
|
||||
#include "snapshot/crashpad_info_client_options.h"
|
||||
#include "snapshot/linux/process_snapshot_linux.h"
|
||||
#include "snapshot/sanitized/process_snapshot_sanitized.h"
|
||||
#include "snapshot/sanitized/sanitization_information.h"
|
||||
#include "util/file/file_reader.h"
|
||||
#include "util/file/output_stream_file_writer.h"
|
||||
#include "util/linux/direct_ptrace_connection.h"
|
||||
#include "util/linux/ptrace_client.h"
|
||||
#include "util/misc/implicit_cast.h"
|
||||
#include "util/misc/metrics.h"
|
||||
#include "util/misc/tri_state.h"
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/stream/base94_output_stream.h"
|
||||
#include "util/stream/log_output_stream.h"
|
||||
#include "util/stream/zlib_output_stream.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
bool WriteMinidumpLogFromFile(FileReaderInterface* file_reader) {
|
||||
ZlibOutputStream stream(ZlibOutputStream::Mode::kCompress,
|
||||
std::make_unique<Base94OutputStream>(
|
||||
Base94OutputStream::Mode::kEncode,
|
||||
std::make_unique<LogOutputStream>()));
|
||||
FileOperationResult read_result;
|
||||
do {
|
||||
uint8_t buffer[4096];
|
||||
read_result = file_reader->Read(buffer, sizeof(buffer));
|
||||
if (read_result < 0)
|
||||
return false;
|
||||
|
||||
if (read_result > 0 && (!stream.Write(buffer, read_result)))
|
||||
return false;
|
||||
} while (read_result > 0);
|
||||
return stream.Flush();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CrashReportExceptionHandler::CrashReportExceptionHandler(
|
||||
CrashReportDatabase* database,
|
||||
CrashReportUploadThread* upload_thread,
|
||||
const std::map<std::string, std::string>* process_annotations,
|
||||
bool write_minidump_to_database,
|
||||
bool write_minidump_to_log,
|
||||
const UserStreamDataSources* user_stream_data_sources)
|
||||
: database_(database),
|
||||
upload_thread_(upload_thread),
|
||||
process_annotations_(process_annotations),
|
||||
user_stream_data_sources_(user_stream_data_sources) {}
|
||||
write_minidump_to_database_(write_minidump_to_database),
|
||||
write_minidump_to_log_(write_minidump_to_log),
|
||||
user_stream_data_sources_(user_stream_data_sources) {
|
||||
DCHECK(write_minidump_to_database_ | write_minidump_to_log_);
|
||||
}
|
||||
|
||||
CrashReportExceptionHandler::~CrashReportExceptionHandler() = default;
|
||||
|
||||
bool CrashReportExceptionHandler::HandleException(
|
||||
pid_t client_process_id,
|
||||
const ClientInformation& info) {
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
UUID* local_report_id) {
|
||||
Metrics::ExceptionEncountered();
|
||||
|
||||
DirectPtraceConnection connection;
|
||||
@ -55,13 +92,20 @@ bool CrashReportExceptionHandler::HandleException(
|
||||
return false;
|
||||
}
|
||||
|
||||
return HandleExceptionWithConnection(&connection, info);
|
||||
return HandleExceptionWithConnection(&connection,
|
||||
info,
|
||||
client_uid,
|
||||
requesting_thread_stack_address,
|
||||
requesting_thread_id,
|
||||
local_report_id);
|
||||
}
|
||||
|
||||
bool CrashReportExceptionHandler::HandleExceptionWithBroker(
|
||||
pid_t client_process_id,
|
||||
const ClientInformation& info,
|
||||
int broker_sock) {
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
int broker_sock,
|
||||
UUID* local_report_id) {
|
||||
Metrics::ExceptionEncountered();
|
||||
|
||||
PtraceClient client;
|
||||
@ -71,123 +115,134 @@ bool CrashReportExceptionHandler::HandleExceptionWithBroker(
|
||||
return false;
|
||||
}
|
||||
|
||||
return HandleExceptionWithConnection(&client, info);
|
||||
return HandleExceptionWithConnection(
|
||||
&client, info, client_uid, 0, nullptr, local_report_id);
|
||||
}
|
||||
|
||||
bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
||||
PtraceConnection* connection,
|
||||
const ClientInformation& info) {
|
||||
ProcessSnapshotLinux process_snapshot;
|
||||
if (!process_snapshot.Initialize(connection)) {
|
||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
uid_t client_uid,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
UUID* local_report_id) {
|
||||
std::unique_ptr<ProcessSnapshotLinux> process_snapshot;
|
||||
std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;
|
||||
if (!CaptureSnapshot(connection,
|
||||
info,
|
||||
*process_annotations_,
|
||||
client_uid,
|
||||
requesting_thread_stack_address,
|
||||
requesting_thread_id,
|
||||
&process_snapshot,
|
||||
&sanitized_snapshot)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!process_snapshot.InitializeException(
|
||||
info.exception_information_address)) {
|
||||
UUID client_id;
|
||||
Settings* const settings = database_->GetSettings();
|
||||
if (settings) {
|
||||
// If GetSettings() or GetClientID() fails, something else will log a
|
||||
// message and client_id will be left at its default value, all zeroes,
|
||||
// which is appropriate.
|
||||
settings->GetClientID(&client_id);
|
||||
}
|
||||
process_snapshot->SetClientID(client_id);
|
||||
|
||||
return write_minidump_to_database_
|
||||
? WriteMinidumpToDatabase(process_snapshot.get(),
|
||||
sanitized_snapshot.get(),
|
||||
write_minidump_to_log_,
|
||||
local_report_id)
|
||||
: WriteMinidumpToLog(process_snapshot.get(),
|
||||
sanitized_snapshot.get());
|
||||
}
|
||||
|
||||
bool CrashReportExceptionHandler::WriteMinidumpToDatabase(
|
||||
ProcessSnapshotLinux* process_snapshot,
|
||||
ProcessSnapshotSanitized* sanitized_snapshot,
|
||||
bool write_minidump_to_log,
|
||||
UUID* local_report_id) {
|
||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||
CrashReportDatabase::OperationStatus database_status =
|
||||
database_->PrepareNewCrashReport(&new_report);
|
||||
if (database_status != CrashReportDatabase::kNoError) {
|
||||
LOG(ERROR) << "PrepareNewCrashReport failed";
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kExceptionInitializationFailed);
|
||||
Metrics::CaptureResult::kPrepareNewCrashReportFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
Metrics::ExceptionCode(process_snapshot.Exception()->Exception());
|
||||
process_snapshot->SetReportID(new_report->ReportID());
|
||||
|
||||
CrashpadInfoClientOptions client_options;
|
||||
process_snapshot.GetCrashpadOptions(&client_options);
|
||||
if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
|
||||
UUID client_id;
|
||||
Settings* const settings = database_->GetSettings();
|
||||
if (settings) {
|
||||
// If GetSettings() or GetClientID() fails, something else will log a
|
||||
// message and client_id will be left at its default value, all zeroes,
|
||||
// which is appropriate.
|
||||
settings->GetClientID(&client_id);
|
||||
ProcessSnapshot* snapshot =
|
||||
sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
|
||||
: implicit_cast<ProcessSnapshot*>(process_snapshot);
|
||||
|
||||
MinidumpFileWriter minidump;
|
||||
minidump.InitializeFromSnapshot(snapshot);
|
||||
AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
|
||||
|
||||
if (!minidump.WriteEverything(new_report->Writer())) {
|
||||
LOG(ERROR) << "WriteEverything failed";
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kMinidumpWriteFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool write_minidump_to_log_succeed = false;
|
||||
if (write_minidump_to_log) {
|
||||
if (auto* file_reader = new_report->Reader()) {
|
||||
if (WriteMinidumpLogFromFile(file_reader))
|
||||
write_minidump_to_log_succeed = true;
|
||||
else
|
||||
LOG(ERROR) << "WriteMinidumpLogFromFile failed";
|
||||
}
|
||||
}
|
||||
|
||||
process_snapshot.SetClientID(client_id);
|
||||
process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
|
||||
UUID uuid;
|
||||
database_status =
|
||||
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
||||
if (database_status != CrashReportDatabase::kNoError) {
|
||||
LOG(ERROR) << "FinishedWritingCrashReport failed";
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||
CrashReportDatabase::OperationStatus database_status =
|
||||
database_->PrepareNewCrashReport(&new_report);
|
||||
if (database_status != CrashReportDatabase::kNoError) {
|
||||
LOG(ERROR) << "PrepareNewCrashReport failed";
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kPrepareNewCrashReportFailed);
|
||||
return false;
|
||||
}
|
||||
if (upload_thread_) {
|
||||
upload_thread_->ReportPending(uuid);
|
||||
}
|
||||
|
||||
process_snapshot.SetReportID(new_report->ReportID());
|
||||
|
||||
ProcessSnapshot* snapshot = nullptr;
|
||||
ProcessSnapshotSanitized sanitized;
|
||||
std::vector<std::string> whitelist;
|
||||
if (info.sanitization_information_address) {
|
||||
SanitizationInformation sanitization_info;
|
||||
ProcessMemoryRange range;
|
||||
if (!range.Initialize(connection->Memory(), connection->Is64Bit()) ||
|
||||
!range.Read(info.sanitization_information_address,
|
||||
sizeof(sanitization_info),
|
||||
&sanitization_info)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kSanitizationInitializationFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sanitization_info.annotations_whitelist_address &&
|
||||
!ReadAnnotationsWhitelist(
|
||||
range,
|
||||
sanitization_info.annotations_whitelist_address,
|
||||
&whitelist)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kSanitizationInitializationFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sanitized.Initialize(&process_snapshot,
|
||||
sanitization_info.annotations_whitelist_address
|
||||
? &whitelist
|
||||
: nullptr,
|
||||
sanitization_info.target_module_address,
|
||||
sanitization_info.sanitize_stacks)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kSkippedDueToSanitization);
|
||||
return true;
|
||||
}
|
||||
|
||||
snapshot = &sanitized;
|
||||
} else {
|
||||
snapshot = &process_snapshot;
|
||||
}
|
||||
|
||||
MinidumpFileWriter minidump;
|
||||
minidump.InitializeFromSnapshot(snapshot);
|
||||
AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
|
||||
|
||||
if (!minidump.WriteEverything(new_report->Writer())) {
|
||||
LOG(ERROR) << "WriteEverything failed";
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kMinidumpWriteFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID uuid;
|
||||
database_status =
|
||||
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
||||
if (database_status != CrashReportDatabase::kNoError) {
|
||||
LOG(ERROR) << "FinishedWritingCrashReport failed";
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (upload_thread_) {
|
||||
upload_thread_->ReportPending(uuid);
|
||||
}
|
||||
if (local_report_id != nullptr) {
|
||||
*local_report_id = uuid;
|
||||
}
|
||||
|
||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
|
||||
return true;
|
||||
|
||||
return write_minidump_to_log ? write_minidump_to_log_succeed : true;
|
||||
}
|
||||
|
||||
bool CrashReportExceptionHandler::WriteMinidumpToLog(
|
||||
ProcessSnapshotLinux* process_snapshot,
|
||||
ProcessSnapshotSanitized* sanitized_snapshot) {
|
||||
ProcessSnapshot* snapshot =
|
||||
sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
|
||||
: implicit_cast<ProcessSnapshot*>(process_snapshot);
|
||||
MinidumpFileWriter minidump;
|
||||
minidump.InitializeFromSnapshot(snapshot);
|
||||
AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
|
||||
|
||||
OutputStreamFileWriter writer(std::make_unique<ZlibOutputStream>(
|
||||
ZlibOutputStream::Mode::kCompress,
|
||||
std::make_unique<Base94OutputStream>(
|
||||
Base94OutputStream::Mode::kEncode,
|
||||
std::make_unique<LogOutputStream>())));
|
||||
if (!minidump.WriteMinidump(&writer, false /* allow_seek */)) {
|
||||
LOG(ERROR) << "WriteMinidump failed";
|
||||
return false;
|
||||
}
|
||||
return writer.Flush();
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -26,9 +26,13 @@
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
#include "util/linux/ptrace_connection.h"
|
||||
#include "util/misc/address_types.h"
|
||||
#include "util/misc/uuid.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class ProcessSnapshotLinux;
|
||||
class ProcessSnapshotSanitized;
|
||||
|
||||
//! \brief An exception handler that writes crash reports for exceptions
|
||||
//! to a CrashReportDatabase.
|
||||
class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
||||
@ -49,6 +53,10 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
||||
//! To interoperate with Breakpad servers, the recommended practice is to
|
||||
//! specify values for the `"prod"` and `"ver"` keys as process
|
||||
//! annotations.
|
||||
//! \param[in] write_minidump_to_database Whether the minidump shall be
|
||||
//! written to database.
|
||||
//! \param[in] write_minidump_to_log Whether the minidump shall be written to
|
||||
//! log.
|
||||
//! \param[in] user_stream_data_sources Data sources to be used to extend
|
||||
//! crash reports. For each crash report that is written, the data sources
|
||||
//! are called in turn. These data sources may contribute additional
|
||||
@ -57,26 +65,49 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
||||
CrashReportDatabase* database,
|
||||
CrashReportUploadThread* upload_thread,
|
||||
const std::map<std::string, std::string>* process_annotations,
|
||||
bool write_minidump_to_database,
|
||||
bool write_minidump_to_log,
|
||||
const UserStreamDataSources* user_stream_data_sources);
|
||||
|
||||
~CrashReportExceptionHandler();
|
||||
~CrashReportExceptionHandler() override;
|
||||
|
||||
// ExceptionHandlerServer::Delegate:
|
||||
|
||||
bool HandleException(pid_t client_process_id,
|
||||
const ClientInformation& info) override;
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
VMAddress requesting_thread_stack_address = 0,
|
||||
pid_t* requesting_thread_id = nullptr,
|
||||
UUID* local_report_id = nullptr) override;
|
||||
|
||||
bool HandleExceptionWithBroker(pid_t client_process_id,
|
||||
const ClientInformation& info,
|
||||
int broker_sock) override;
|
||||
bool HandleExceptionWithBroker(
|
||||
pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
int broker_sock,
|
||||
UUID* local_report_id = nullptr) override;
|
||||
|
||||
private:
|
||||
bool HandleExceptionWithConnection(PtraceConnection* connection,
|
||||
const ClientInformation& info);
|
||||
bool HandleExceptionWithConnection(
|
||||
PtraceConnection* connection,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
uid_t client_uid,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
UUID* local_report_id = nullptr);
|
||||
|
||||
bool WriteMinidumpToDatabase(ProcessSnapshotLinux* process_snapshot,
|
||||
ProcessSnapshotSanitized* sanitized_snapshot,
|
||||
bool write_minidump_to_log,
|
||||
UUID* local_report_id);
|
||||
bool WriteMinidumpToLog(ProcessSnapshotLinux* process_snapshot,
|
||||
ProcessSnapshotSanitized* sanitized_snapshot);
|
||||
|
||||
CrashReportDatabase* database_; // weak
|
||||
CrashReportUploadThread* upload_thread_; // weak
|
||||
const std::map<std::string, std::string>* process_annotations_; // weak
|
||||
bool write_minidump_to_database_;
|
||||
bool write_minidump_to_log_;
|
||||
const UserStreamDataSources* user_stream_data_sources_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
|
||||
|
286
handler/linux/cros_crash_report_exception_handler.cc
Normal file
286
handler/linux/cros_crash_report_exception_handler.cc
Normal file
@ -0,0 +1,286 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "handler/linux/cros_crash_report_exception_handler.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "client/settings.h"
|
||||
#include "handler/linux/capture_snapshot.h"
|
||||
#include "handler/minidump_to_upload_parameters.h"
|
||||
#include "minidump/minidump_file_writer.h"
|
||||
#include "snapshot/linux/process_snapshot_linux.h"
|
||||
#include "snapshot/minidump/process_snapshot_minidump.h"
|
||||
#include "snapshot/sanitized/process_snapshot_sanitized.h"
|
||||
#include "util/file/file_writer.h"
|
||||
#include "util/linux/direct_ptrace_connection.h"
|
||||
#include "util/linux/ptrace_client.h"
|
||||
#include "util/misc/metrics.h"
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/posix/double_fork_and_exec.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace {
|
||||
|
||||
// Returns the process name for a pid.
|
||||
const std::string GetProcessNameFromPid(pid_t pid) {
|
||||
// Symlink to process binary is at /proc/###/exe.
|
||||
std::string link_path = "/proc/" + std::to_string(pid) + "/exe";
|
||||
|
||||
constexpr int kMaxSize = 4096;
|
||||
std::unique_ptr<char[]> buf(new char[kMaxSize]);
|
||||
ssize_t size = readlink(link_path.c_str(), buf.get(), kMaxSize);
|
||||
std::string result;
|
||||
if (size < 0) {
|
||||
PLOG(ERROR) << "Failed to readlink " << link_path;
|
||||
} else {
|
||||
result.assign(buf.get(), size);
|
||||
size_t last_slash_pos = result.rfind('/');
|
||||
if (last_slash_pos != std::string::npos) {
|
||||
result = result.substr(last_slash_pos + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WriteAnnotationsAndMinidump(
|
||||
const std::map<std::string, std::string>& parameters,
|
||||
MinidumpFileWriter& minidump,
|
||||
FileWriter& file_writer) {
|
||||
for (const auto& kv : parameters) {
|
||||
if (kv.first.find(':') != std::string::npos) {
|
||||
LOG(ERROR) << "Annotation key cannot have ':' in it " << kv.first;
|
||||
return false;
|
||||
}
|
||||
if (!file_writer.Write(kv.first.c_str(), strlen(kv.first.c_str()))) {
|
||||
return false;
|
||||
}
|
||||
if (!file_writer.Write(":", 1)) {
|
||||
return false;
|
||||
}
|
||||
size_t value_size = strlen(kv.second.c_str());
|
||||
std::string value_size_str = std::to_string(value_size);
|
||||
if (!file_writer.Write(value_size_str.c_str(), value_size_str.size())) {
|
||||
return false;
|
||||
}
|
||||
if (!file_writer.Write(":", 1)) {
|
||||
return false;
|
||||
}
|
||||
if (!file_writer.Write(kv.second.c_str(), strlen(kv.second.c_str()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr char kMinidumpName[] =
|
||||
"upload_file_minidump\"; filename=\"dump\":";
|
||||
if (!file_writer.Write(kMinidumpName, sizeof(kMinidumpName) - 1)) {
|
||||
return false;
|
||||
}
|
||||
crashpad::FileOffset dump_size_start_offset = file_writer.Seek(0, SEEK_CUR);
|
||||
if (dump_size_start_offset < 0) {
|
||||
LOG(ERROR) << "Failed to get minidump size start offset";
|
||||
return false;
|
||||
}
|
||||
static constexpr char kMinidumpLengthFilling[] = "00000000000000000000:";
|
||||
if (!file_writer.Write(kMinidumpLengthFilling,
|
||||
sizeof(kMinidumpLengthFilling) - 1)) {
|
||||
return false;
|
||||
}
|
||||
crashpad::FileOffset dump_start_offset = file_writer.Seek(0, SEEK_CUR);
|
||||
if (dump_start_offset < 0) {
|
||||
LOG(ERROR) << "Failed to get minidump start offset";
|
||||
return false;
|
||||
}
|
||||
if (!minidump.WriteEverything(&file_writer)) {
|
||||
return false;
|
||||
}
|
||||
crashpad::FileOffset dump_end_offset = file_writer.Seek(0, SEEK_CUR);
|
||||
if (dump_end_offset < 0) {
|
||||
LOG(ERROR) << "Failed to get minidump end offset";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t dump_data_size = dump_end_offset - dump_start_offset;
|
||||
std::string dump_data_size_str = std::to_string(dump_data_size);
|
||||
file_writer.Seek(dump_size_start_offset + strlen(kMinidumpLengthFilling) - 1 -
|
||||
dump_data_size_str.size(),
|
||||
SEEK_SET);
|
||||
if (!file_writer.Write(dump_data_size_str.c_str(),
|
||||
dump_data_size_str.size())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CrosCrashReportExceptionHandler::CrosCrashReportExceptionHandler(
|
||||
CrashReportDatabase* database,
|
||||
const std::map<std::string, std::string>* process_annotations,
|
||||
const UserStreamDataSources* user_stream_data_sources)
|
||||
: database_(database),
|
||||
process_annotations_(process_annotations),
|
||||
user_stream_data_sources_(user_stream_data_sources),
|
||||
always_allow_feedback_(false) {}
|
||||
|
||||
CrosCrashReportExceptionHandler::~CrosCrashReportExceptionHandler() = default;
|
||||
|
||||
bool CrosCrashReportExceptionHandler::HandleException(
|
||||
pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
UUID* local_report_id) {
|
||||
Metrics::ExceptionEncountered();
|
||||
|
||||
DirectPtraceConnection connection;
|
||||
if (!connection.Initialize(client_process_id)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kDirectPtraceFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
return HandleExceptionWithConnection(&connection,
|
||||
info,
|
||||
client_uid,
|
||||
requesting_thread_stack_address,
|
||||
requesting_thread_id,
|
||||
local_report_id);
|
||||
}
|
||||
|
||||
bool CrosCrashReportExceptionHandler::HandleExceptionWithBroker(
|
||||
pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
int broker_sock,
|
||||
UUID* local_report_id) {
|
||||
Metrics::ExceptionEncountered();
|
||||
|
||||
PtraceClient client;
|
||||
if (!client.Initialize(broker_sock, client_process_id)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kBrokeredPtraceFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
return HandleExceptionWithConnection(
|
||||
&client, info, client_uid, 0, nullptr, local_report_id);
|
||||
}
|
||||
|
||||
bool CrosCrashReportExceptionHandler::HandleExceptionWithConnection(
|
||||
PtraceConnection* connection,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
uid_t client_uid,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
UUID* local_report_id) {
|
||||
std::unique_ptr<ProcessSnapshotLinux> process_snapshot;
|
||||
std::unique_ptr<ProcessSnapshotSanitized> sanitized_snapshot;
|
||||
if (!CaptureSnapshot(connection,
|
||||
info,
|
||||
*process_annotations_,
|
||||
client_uid,
|
||||
requesting_thread_stack_address,
|
||||
requesting_thread_id,
|
||||
&process_snapshot,
|
||||
&sanitized_snapshot)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
UUID client_id;
|
||||
Settings* const settings = database_->GetSettings();
|
||||
if (settings) {
|
||||
// If GetSettings() or GetClientID() fails, something else will log a
|
||||
// message and client_id will be left at its default value, all zeroes,
|
||||
// which is appropriate.
|
||||
settings->GetClientID(&client_id);
|
||||
}
|
||||
process_snapshot->SetClientID(client_id);
|
||||
|
||||
UUID uuid;
|
||||
uuid.InitializeWithNew();
|
||||
process_snapshot->SetReportID(uuid);
|
||||
|
||||
ProcessSnapshot* snapshot =
|
||||
sanitized_snapshot
|
||||
? implicit_cast<ProcessSnapshot*>(sanitized_snapshot.get())
|
||||
: implicit_cast<ProcessSnapshot*>(process_snapshot.get());
|
||||
|
||||
MinidumpFileWriter minidump;
|
||||
minidump.InitializeFromSnapshot(snapshot);
|
||||
AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump);
|
||||
|
||||
FileWriter file_writer;
|
||||
if (!file_writer.OpenMemfd(base::FilePath("minidump"))) {
|
||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kOpenMemfdFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> parameters =
|
||||
BreakpadHTTPFormParametersFromMinidump(snapshot);
|
||||
// Used to differentiate between breakpad and crashpad while the switch is
|
||||
// ramping up.
|
||||
parameters.emplace("crash_library", "crashpad");
|
||||
|
||||
if (!WriteAnnotationsAndMinidump(parameters, minidump, file_writer)) {
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kMinidumpWriteFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
// CrOS uses crash_reporter instead of Crashpad to report crashes.
|
||||
// crash_reporter needs to know the pid and uid of the crashing process.
|
||||
std::vector<std::string> argv({"/sbin/crash_reporter"});
|
||||
|
||||
argv.push_back("--chrome_memfd=" + std::to_string(file_writer.fd()));
|
||||
argv.push_back("--pid=" + std::to_string(*requesting_thread_id));
|
||||
argv.push_back("--uid=" + std::to_string(client_uid));
|
||||
std::string process_name = GetProcessNameFromPid(*requesting_thread_id);
|
||||
argv.push_back("--exe=" + (process_name.empty() ? "chrome" : process_name));
|
||||
|
||||
if (info.crash_loop_before_time != 0) {
|
||||
argv.push_back("--crash_loop_before=" +
|
||||
std::to_string(info.crash_loop_before_time));
|
||||
}
|
||||
if (!dump_dir_.empty()) {
|
||||
argv.push_back("--chrome_dump_dir=" + dump_dir_.value());
|
||||
}
|
||||
if (always_allow_feedback_) {
|
||||
argv.push_back("--always_allow_feedback");
|
||||
}
|
||||
|
||||
if (!DoubleForkAndExec(argv,
|
||||
nullptr /* envp */,
|
||||
file_writer.fd() /* preserve_fd */,
|
||||
false /* use_path */,
|
||||
nullptr /* child_function */)) {
|
||||
LOG(ERROR) << "DoubleForkAndExec failed";
|
||||
Metrics::ExceptionCaptureResult(
|
||||
Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (local_report_id != nullptr) {
|
||||
*local_report_id = uuid;
|
||||
}
|
||||
LOG(INFO) << "Successfully wrote report " << uuid.ToString();
|
||||
|
||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
101
handler/linux/cros_crash_report_exception_handler.h
Normal file
101
handler/linux/cros_crash_report_exception_handler.h
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2019 The Crashpad Authors. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
|
||||
#define CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "client/crash_report_database.h"
|
||||
#include "handler/linux/exception_handler_server.h"
|
||||
#include "handler/user_stream_data_source.h"
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
#include "util/linux/ptrace_connection.h"
|
||||
#include "util/misc/address_types.h"
|
||||
#include "util/misc/uuid.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief An exception handler that writes crash reports to the ChromeOS
|
||||
//! crash_reporter.
|
||||
class CrosCrashReportExceptionHandler
|
||||
: public ExceptionHandlerServer::Delegate {
|
||||
public:
|
||||
//! \brief Creates a new object that will pass reports to
|
||||
//! `/sbin/crash_reporter`.
|
||||
//!
|
||||
//! \param[in] database The database that supplies settings for this client.
|
||||
//! This object does not write its reports to this database.
|
||||
//! \param[in] process_annotations A map of annotations to insert as
|
||||
//! process-level annotations into each crash report that is written. Do
|
||||
//! not confuse this with module-level annotations, which are under the
|
||||
//! control of the crashing process, and are used to implement Chrome’s
|
||||
//! “crash keys.” Process-level annotations are those that are beyond the
|
||||
//! control of the crashing process, which must reliably be set even if
|
||||
//! the process crashes before it’s able to establish its own annotations.
|
||||
//! To interoperate with Breakpad servers, the recommended practice is to
|
||||
//! specify values for the `"prod"` and `"ver"` keys as process
|
||||
//! annotations.
|
||||
//! \param[in] user_stream_data_sources Data sources to be used to extend
|
||||
//! crash reports. For each crash report that is written, the data sources
|
||||
//! are called in turn. These data sources may contribute additional
|
||||
//! minidump streams. `nullptr` if not required.
|
||||
CrosCrashReportExceptionHandler(
|
||||
CrashReportDatabase* database,
|
||||
const std::map<std::string, std::string>* process_annotations,
|
||||
const UserStreamDataSources* user_stream_data_sources);
|
||||
|
||||
~CrosCrashReportExceptionHandler() override;
|
||||
|
||||
// ExceptionHandlerServer::Delegate:
|
||||
|
||||
bool HandleException(pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
VMAddress requesting_thread_stack_address = 0,
|
||||
pid_t* requesting_thread_id = nullptr,
|
||||
UUID* local_report_id = nullptr) override;
|
||||
|
||||
bool HandleExceptionWithBroker(
|
||||
pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
int broker_sock,
|
||||
UUID* local_report_id = nullptr) override;
|
||||
|
||||
void SetDumpDir(const base::FilePath& dump_dir) { dump_dir_ = dump_dir; }
|
||||
void SetAlwaysAllowFeedback() { always_allow_feedback_ = true; }
|
||||
private:
|
||||
bool HandleExceptionWithConnection(
|
||||
PtraceConnection* connection,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
uid_t client_uid,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id,
|
||||
UUID* local_report_id = nullptr);
|
||||
|
||||
CrashReportDatabase* database_; // weak
|
||||
const std::map<std::string, std::string>* process_annotations_; // weak
|
||||
const UserStreamDataSources* user_stream_data_sources_; // weak
|
||||
base::FilePath dump_dir_;
|
||||
bool always_allow_feedback_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrosCrashReportExceptionHandler);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_
|
@ -15,10 +15,11 @@
|
||||
#include "handler/linux/exception_handler_server.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/capability.h>
|
||||
#include <linux/capability.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@ -31,6 +32,8 @@
|
||||
#include "build/build_config.h"
|
||||
#include "util/file/file_io.h"
|
||||
#include "util/file/filesystem.h"
|
||||
#include "util/linux/proc_task_reader.h"
|
||||
#include "util/linux/socket.h"
|
||||
#include "util/misc/as_underlying_type.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -90,54 +93,96 @@ PtraceScope GetPtraceScope() {
|
||||
}
|
||||
|
||||
bool HaveCapSysPtrace() {
|
||||
struct __user_cap_header_struct cap_header = {};
|
||||
struct __user_cap_data_struct cap_data = {};
|
||||
|
||||
__user_cap_header_struct cap_header;
|
||||
cap_header.pid = getpid();
|
||||
cap_header.version = _LINUX_CAPABILITY_VERSION_3;
|
||||
|
||||
if (capget(&cap_header, &cap_data) != 0) {
|
||||
__user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3];
|
||||
if (syscall(SYS_capget, &cap_header, &cap_data) != 0) {
|
||||
PLOG(ERROR) << "capget";
|
||||
LOG_IF(ERROR, errno == EINVAL) << "cap_header.version " << std::hex
|
||||
<< cap_header.version;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cap_header.version != _LINUX_CAPABILITY_VERSION_3) {
|
||||
LOG(ERROR) << "Unexpected capability version " << std::hex
|
||||
<< cap_header.version;
|
||||
return false;
|
||||
}
|
||||
|
||||
return (cap_data.effective & (1 << CAP_SYS_PTRACE)) != 0;
|
||||
return (cap_data[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &
|
||||
CAP_TO_MASK(CAP_SYS_PTRACE)) != 0;
|
||||
}
|
||||
|
||||
bool SendMessageToClient(int client_sock, ServerToClientMessage::Type type) {
|
||||
ServerToClientMessage message = {};
|
||||
bool SendMessageToClient(
|
||||
int client_sock,
|
||||
ExceptionHandlerProtocol::ServerToClientMessage::Type type) {
|
||||
ExceptionHandlerProtocol::ServerToClientMessage message = {};
|
||||
message.type = type;
|
||||
if (type == ServerToClientMessage::kTypeSetPtracer) {
|
||||
if (type ==
|
||||
ExceptionHandlerProtocol::ServerToClientMessage::kTypeSetPtracer) {
|
||||
message.pid = getpid();
|
||||
}
|
||||
return LoggingWriteFile(client_sock, &message, sizeof(message));
|
||||
}
|
||||
|
||||
int tgkill(pid_t pid, pid_t tid, int signo) {
|
||||
return syscall(SYS_tgkill, pid, tid, signo);
|
||||
}
|
||||
|
||||
void SendSIGCONT(pid_t pid, pid_t tid) {
|
||||
if (tid > 0) {
|
||||
if (tgkill(pid, tid, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) {
|
||||
PLOG(ERROR) << "tgkill";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<pid_t> threads;
|
||||
if (!ReadThreadIDs(pid, &threads)) {
|
||||
return;
|
||||
}
|
||||
for (const auto& thread : threads) {
|
||||
if (tgkill(pid, thread, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) {
|
||||
PLOG(ERROR) << "tgkill";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SendCredentials(int client_sock) {
|
||||
ExceptionHandlerProtocol::ServerToClientMessage message = {};
|
||||
message.type =
|
||||
ExceptionHandlerProtocol::ServerToClientMessage::kTypeCredentials;
|
||||
return UnixCredentialSocket::SendMsg(
|
||||
client_sock, &message, sizeof(message)) == 0;
|
||||
}
|
||||
|
||||
class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
|
||||
public:
|
||||
PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {}
|
||||
~PtraceStrategyDeciderImpl() = default;
|
||||
|
||||
Strategy ChooseStrategy(int sock, const ucred& client_credentials) override {
|
||||
Strategy ChooseStrategy(int sock,
|
||||
bool multiple_clients,
|
||||
const ucred& client_credentials) override {
|
||||
if (client_credentials.pid <= 0) {
|
||||
LOG(ERROR) << "invalid credentials";
|
||||
return Strategy::kNoPtrace;
|
||||
}
|
||||
|
||||
switch (GetPtraceScope()) {
|
||||
case PtraceScope::kClassic:
|
||||
if (getuid() == client_credentials.uid) {
|
||||
if (getuid() == client_credentials.uid || HaveCapSysPtrace()) {
|
||||
return Strategy::kDirectPtrace;
|
||||
}
|
||||
return TryForkingBroker(sock);
|
||||
return multiple_clients ? Strategy::kNoPtrace : TryForkingBroker(sock);
|
||||
|
||||
case PtraceScope::kRestricted:
|
||||
if (multiple_clients) {
|
||||
return Strategy::kDirectPtrace;
|
||||
}
|
||||
if (!SendMessageToClient(sock,
|
||||
ServerToClientMessage::kTypeSetPtracer)) {
|
||||
ExceptionHandlerProtocol::
|
||||
ServerToClientMessage::kTypeSetPtracer)) {
|
||||
return Strategy::kError;
|
||||
}
|
||||
|
||||
Errno status;
|
||||
ExceptionHandlerProtocol::Errno status;
|
||||
if (!LoggingReadFileExactly(sock, &status, sizeof(status))) {
|
||||
return Strategy::kError;
|
||||
}
|
||||
@ -169,12 +214,13 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
|
||||
|
||||
private:
|
||||
static Strategy TryForkingBroker(int client_sock) {
|
||||
if (!SendMessageToClient(client_sock,
|
||||
ServerToClientMessage::kTypeForkBroker)) {
|
||||
if (!SendMessageToClient(
|
||||
client_sock,
|
||||
ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker)) {
|
||||
return Strategy::kError;
|
||||
}
|
||||
|
||||
Errno status;
|
||||
ExceptionHandlerProtocol::Errno status;
|
||||
if (!LoggingReadFileExactly(client_sock, &status, sizeof(status))) {
|
||||
return Strategy::kError;
|
||||
}
|
||||
@ -190,12 +236,6 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
|
||||
|
||||
} // namespace
|
||||
|
||||
struct ExceptionHandlerServer::Event {
|
||||
enum class Type { kShutdown, kClientMessage } type;
|
||||
|
||||
ScopedFileHandle fd;
|
||||
};
|
||||
|
||||
ExceptionHandlerServer::ExceptionHandlerServer()
|
||||
: clients_(),
|
||||
shutdown_event_(),
|
||||
@ -211,7 +251,8 @@ void ExceptionHandlerServer::SetPtraceStrategyDecider(
|
||||
strategy_decider_ = std::move(decider);
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock) {
|
||||
bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock,
|
||||
bool multiple_clients) {
|
||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||
|
||||
pollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
|
||||
@ -239,7 +280,9 @@ bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InstallClientSocket(std::move(sock))) {
|
||||
if (!InstallClientSocket(std::move(sock),
|
||||
multiple_clients ? Event::Type::kSharedSocketMessage
|
||||
: Event::Type::kClientMessage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -281,8 +324,8 @@ void ExceptionHandlerServer::Stop() {
|
||||
}
|
||||
|
||||
void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {
|
||||
DCHECK_EQ(AsUnderlyingType(event->type),
|
||||
AsUnderlyingType(Event::Type::kClientMessage));
|
||||
DCHECK_NE(AsUnderlyingType(event->type),
|
||||
AsUnderlyingType(Event::Type::kShutdown));
|
||||
|
||||
if (event_type & EPOLLERR) {
|
||||
LogSocketError(event->fd.get());
|
||||
@ -306,16 +349,30 @@ void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket) {
|
||||
int optval = 1;
|
||||
bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket,
|
||||
Event::Type type) {
|
||||
// The handler may not have permission to set SO_PASSCRED on the socket, but
|
||||
// it doesn't need to if the client has already set it.
|
||||
// https://bugs.chromium.org/p/crashpad/issues/detail?id=252
|
||||
int optval;
|
||||
socklen_t optlen = sizeof(optval);
|
||||
if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) != 0) {
|
||||
PLOG(ERROR) << "setsockopt";
|
||||
if (getsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, &optlen) !=
|
||||
0) {
|
||||
PLOG(ERROR) << "getsockopt";
|
||||
return false;
|
||||
}
|
||||
if (!optval) {
|
||||
optval = 1;
|
||||
optlen = sizeof(optval);
|
||||
if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) !=
|
||||
0) {
|
||||
PLOG(ERROR) << "setsockopt";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
auto event = std::make_unique<Event>();
|
||||
event->type = Event::Type::kClientMessage;
|
||||
event->type = type;
|
||||
event->fd.reset(socket.release());
|
||||
|
||||
Event* eventp = event.get();
|
||||
@ -355,53 +412,24 @@ bool ExceptionHandlerServer::UninstallClientSocket(Event* event) {
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {
|
||||
ClientToServerMessage message;
|
||||
iovec iov;
|
||||
iov.iov_base = &message;
|
||||
iov.iov_len = sizeof(message);
|
||||
|
||||
msghdr msg;
|
||||
msg.msg_name = nullptr;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
char cmsg_buf[CMSG_SPACE(sizeof(ucred))];
|
||||
msg.msg_control = cmsg_buf;
|
||||
msg.msg_controllen = sizeof(cmsg_buf);
|
||||
msg.msg_flags = 0;
|
||||
|
||||
int res = HANDLE_EINTR(recvmsg(event->fd.get(), &msg, 0));
|
||||
if (res < 0) {
|
||||
PLOG(ERROR) << "recvmsg";
|
||||
return false;
|
||||
}
|
||||
if (res == 0) {
|
||||
// The client had an orderly shutdown.
|
||||
ExceptionHandlerProtocol::ClientToServerMessage message;
|
||||
ucred creds;
|
||||
if (!UnixCredentialSocket::RecvMsg(
|
||||
event->fd.get(), &message, sizeof(message), &creds)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.msg_name != nullptr || msg.msg_namelen != 0) {
|
||||
LOG(ERROR) << "unexpected msg name";
|
||||
return false;
|
||||
}
|
||||
switch (message.type) {
|
||||
case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCheckCredentials:
|
||||
return SendCredentials(event->fd.get());
|
||||
|
||||
if (msg.msg_iovlen != 1) {
|
||||
LOG(ERROR) << "unexpected iovlen";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (msg.msg_iov[0].iov_len != sizeof(ClientToServerMessage)) {
|
||||
LOG(ERROR) << "unexpected message size " << msg.msg_iov[0].iov_len;
|
||||
return false;
|
||||
}
|
||||
auto client_msg =
|
||||
reinterpret_cast<ClientToServerMessage*>(msg.msg_iov[0].iov_base);
|
||||
|
||||
switch (client_msg->type) {
|
||||
case ClientToServerMessage::kCrashDumpRequest:
|
||||
case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCrashDumpRequest:
|
||||
return HandleCrashDumpRequest(
|
||||
msg, client_msg->client_info, event->fd.get());
|
||||
creds,
|
||||
message.client_info,
|
||||
message.requesting_thread_stack_address,
|
||||
event->fd.get(),
|
||||
event->type == Event::Type::kSharedSocketMessage);
|
||||
}
|
||||
|
||||
DCHECK(false);
|
||||
@ -410,53 +438,56 @@ bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {
|
||||
}
|
||||
|
||||
bool ExceptionHandlerServer::HandleCrashDumpRequest(
|
||||
const msghdr& msg,
|
||||
const ClientInformation& client_info,
|
||||
int client_sock) {
|
||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg == nullptr) {
|
||||
LOG(ERROR) << "missing credentials";
|
||||
return false;
|
||||
}
|
||||
const ucred& creds,
|
||||
const ExceptionHandlerProtocol::ClientInformation& client_info,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
int client_sock,
|
||||
bool multiple_clients) {
|
||||
pid_t client_process_id = creds.pid;
|
||||
pid_t requesting_thread_id = -1;
|
||||
uid_t client_uid = creds.uid;
|
||||
|
||||
if (cmsg->cmsg_level != SOL_SOCKET) {
|
||||
LOG(ERROR) << "unexpected cmsg_level " << cmsg->cmsg_level;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_type != SCM_CREDENTIALS) {
|
||||
LOG(ERROR) << "unexpected cmsg_type " << cmsg->cmsg_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cmsg->cmsg_len != CMSG_LEN(sizeof(ucred))) {
|
||||
LOG(ERROR) << "unexpected cmsg_len " << cmsg->cmsg_len;
|
||||
return false;
|
||||
}
|
||||
|
||||
ucred* client_credentials = reinterpret_cast<ucred*>(CMSG_DATA(cmsg));
|
||||
pid_t client_process_id = client_credentials->pid;
|
||||
|
||||
switch (strategy_decider_->ChooseStrategy(client_sock, *client_credentials)) {
|
||||
switch (
|
||||
strategy_decider_->ChooseStrategy(client_sock, multiple_clients, creds)) {
|
||||
case PtraceStrategyDecider::Strategy::kError:
|
||||
if (multiple_clients) {
|
||||
SendSIGCONT(client_process_id, requesting_thread_id);
|
||||
}
|
||||
return false;
|
||||
|
||||
case PtraceStrategyDecider::Strategy::kNoPtrace:
|
||||
return SendMessageToClient(client_sock,
|
||||
ServerToClientMessage::kTypeCrashDumpFailed);
|
||||
if (multiple_clients) {
|
||||
SendSIGCONT(client_process_id, requesting_thread_id);
|
||||
return true;
|
||||
}
|
||||
return SendMessageToClient(
|
||||
client_sock,
|
||||
ExceptionHandlerProtocol::ServerToClientMessage::
|
||||
kTypeCrashDumpFailed);
|
||||
|
||||
case PtraceStrategyDecider::Strategy::kDirectPtrace:
|
||||
delegate_->HandleException(client_process_id, client_info);
|
||||
case PtraceStrategyDecider::Strategy::kDirectPtrace: {
|
||||
delegate_->HandleException(client_process_id,
|
||||
client_uid,
|
||||
client_info,
|
||||
requesting_thread_stack_address,
|
||||
&requesting_thread_id);
|
||||
if (multiple_clients) {
|
||||
SendSIGCONT(client_process_id, requesting_thread_id);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PtraceStrategyDecider::Strategy::kUseBroker:
|
||||
DCHECK(!multiple_clients);
|
||||
delegate_->HandleExceptionWithBroker(
|
||||
client_process_id, client_info, client_sock);
|
||||
client_process_id, client_uid, client_info, client_sock);
|
||||
break;
|
||||
}
|
||||
|
||||
return SendMessageToClient(client_sock,
|
||||
ServerToClientMessage::kTypeCrashDumpComplete);
|
||||
return SendMessageToClient(
|
||||
client_sock,
|
||||
ExceptionHandlerProtocol::ServerToClientMessage::kTypeCrashDumpComplete);
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
@ -26,6 +27,7 @@
|
||||
#include "util/linux/exception_handler_protocol.h"
|
||||
#include "util/misc/address_types.h"
|
||||
#include "util/misc/initialization_state_dcheck.h"
|
||||
#include "util/misc/uuid.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -53,9 +55,12 @@ class PtraceStrategyDecider {
|
||||
//! \brief Chooses an appropriate `ptrace` strategy.
|
||||
//!
|
||||
//! \param[in] sock A socket conncted to a ExceptionHandlerClient.
|
||||
//! \param[in] multiple_clients `true` if the socket is connected to multiple
|
||||
//! clients. The broker is not supported in this configuration.
|
||||
//! \param[in] client_credentials The credentials for the connected client.
|
||||
//! \return the chosen #Strategy.
|
||||
virtual Strategy ChooseStrategy(int sock,
|
||||
bool multiple_clients,
|
||||
const ucred& client_credentials) = 0;
|
||||
|
||||
protected:
|
||||
@ -71,24 +76,43 @@ class ExceptionHandlerServer {
|
||||
//! \brief Called on receipt of a crash dump request from a client.
|
||||
//!
|
||||
//! \param[in] client_process_id The process ID of the crashing client.
|
||||
//! \param[in] client_uid The user ID of the crashing client.
|
||||
//! \param[in] info Information on the client.
|
||||
//! \param[in] requesting_thread_stack_address Any address within the stack
|
||||
//! range for the the thread that sent the crash dump request. Optional.
|
||||
//! If unspecified or 0, \a requesting_thread_id will be -1.
|
||||
//! \param[out] requesting_thread_id The thread ID of the thread which
|
||||
//! requested the crash dump if not `nullptr`. Set to -1 if the thread
|
||||
//! ID could not be determined. Optional.
|
||||
//! \param[out] local_report_id The unique identifier for the report created
|
||||
//! in the local report database. Optional.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
virtual bool HandleException(pid_t client_process_id,
|
||||
const ClientInformation& info) = 0;
|
||||
virtual bool HandleException(
|
||||
pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
VMAddress requesting_thread_stack_address = 0,
|
||||
pid_t* requesting_thread_id = nullptr,
|
||||
UUID* local_report_id = nullptr) = 0;
|
||||
|
||||
//! \brief Called on the receipt of a crash dump request from a client for a
|
||||
//! crash that should be mediated by a PtraceBroker.
|
||||
//!
|
||||
//! \param[in] client_process_id The process ID of the crashing client.
|
||||
//! \param[in] client_uid The uid of the crashing client.
|
||||
//! \param[in] info Information on the client.
|
||||
//! \param[in] broker_sock A socket connected to the PtraceBroker.
|
||||
//! \param[out] local_report_id The unique identifier for the report created
|
||||
//! in the local report database. Optional.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
virtual bool HandleExceptionWithBroker(pid_t client_process_id,
|
||||
const ClientInformation& info,
|
||||
int broker_sock) = 0;
|
||||
virtual bool HandleExceptionWithBroker(
|
||||
pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
int broker_sock,
|
||||
UUID* local_report_id = nullptr) = 0;
|
||||
|
||||
protected:
|
||||
~Delegate() {}
|
||||
virtual ~Delegate() {}
|
||||
};
|
||||
|
||||
ExceptionHandlerServer();
|
||||
@ -105,8 +129,11 @@ class ExceptionHandlerServer {
|
||||
//! This method must be successfully called before Run().
|
||||
//!
|
||||
//! \param[in] sock A socket on which to receive client requests.
|
||||
//! \param[in] multiple_clients `true` if this socket is used by multiple
|
||||
//! clients. Using a broker process is not supported in this
|
||||
//! configuration.
|
||||
//! \return `true` on success. `false` on failure with a message logged.
|
||||
bool InitializeWithClient(ScopedFileHandle sock);
|
||||
bool InitializeWithClient(ScopedFileHandle sock, bool multiple_clients);
|
||||
|
||||
//! \brief Runs the exception-handling server.
|
||||
//!
|
||||
@ -126,22 +153,39 @@ class ExceptionHandlerServer {
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
struct Event;
|
||||
struct Event {
|
||||
enum class Type {
|
||||
// Used by Stop() to shutdown the server.
|
||||
kShutdown,
|
||||
|
||||
// A message from a client on a private socket connection.
|
||||
kClientMessage,
|
||||
|
||||
// A message from a client on a shared socket connection.
|
||||
kSharedSocketMessage
|
||||
};
|
||||
|
||||
Type type;
|
||||
ScopedFileHandle fd;
|
||||
};
|
||||
|
||||
void HandleEvent(Event* event, uint32_t event_type);
|
||||
bool InstallClientSocket(ScopedFileHandle socket);
|
||||
bool InstallClientSocket(ScopedFileHandle socket, Event::Type type);
|
||||
bool UninstallClientSocket(Event* event);
|
||||
bool ReceiveClientMessage(Event* event);
|
||||
bool HandleCrashDumpRequest(const msghdr& msg,
|
||||
const ClientInformation& client_info,
|
||||
int client_sock);
|
||||
bool HandleCrashDumpRequest(
|
||||
const ucred& creds,
|
||||
const ExceptionHandlerProtocol::ClientInformation& client_info,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
int client_sock,
|
||||
bool multiple_clients);
|
||||
|
||||
std::unordered_map<int, std::unique_ptr<Event>> clients_;
|
||||
std::unique_ptr<Event> shutdown_event_;
|
||||
std::unique_ptr<PtraceStrategyDecider> strategy_decider_;
|
||||
Delegate* delegate_;
|
||||
ScopedFileHandle pollfd_;
|
||||
bool keep_running_;
|
||||
std::atomic<bool> keep_running_;
|
||||
InitializationStateDcheck initialized_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
|
||||
|
@ -18,16 +18,23 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/linux/process_snapshot_linux.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "util/linux/direct_ptrace_connection.h"
|
||||
#include "util/linux/exception_handler_client.h"
|
||||
#include "util/linux/ptrace_client.h"
|
||||
#include "util/linux/scoped_pr_set_ptracer.h"
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/synchronization/semaphore.h"
|
||||
#include "util/thread/thread.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include <android/api-level.h>
|
||||
#endif
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
@ -101,7 +108,11 @@ class TestDelegate : public ExceptionHandlerServer::Delegate {
|
||||
}
|
||||
|
||||
bool HandleException(pid_t client_process_id,
|
||||
const ClientInformation& info) override {
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
VMAddress requesting_thread_stack_address,
|
||||
pid_t* requesting_thread_id = nullptr,
|
||||
UUID* local_report_id = nullptr) override {
|
||||
DirectPtraceConnection connection;
|
||||
bool connected = connection.Initialize(client_process_id);
|
||||
EXPECT_TRUE(connected);
|
||||
@ -109,12 +120,32 @@ class TestDelegate : public ExceptionHandlerServer::Delegate {
|
||||
last_exception_address_ = info.exception_information_address;
|
||||
last_client_ = client_process_id;
|
||||
sem_.Signal();
|
||||
return connected;
|
||||
if (!connected) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requesting_thread_id) {
|
||||
if (requesting_thread_stack_address) {
|
||||
ProcessSnapshotLinux process_snapshot;
|
||||
if (!process_snapshot.Initialize(&connection)) {
|
||||
ADD_FAILURE();
|
||||
return false;
|
||||
}
|
||||
*requesting_thread_id = process_snapshot.FindThreadWithStackAddress(
|
||||
requesting_thread_stack_address);
|
||||
} else {
|
||||
*requesting_thread_id = -1;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleExceptionWithBroker(pid_t client_process_id,
|
||||
const ClientInformation& info,
|
||||
int broker_sock) override {
|
||||
bool HandleExceptionWithBroker(
|
||||
pid_t client_process_id,
|
||||
uid_t client_uid,
|
||||
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||
int broker_sock,
|
||||
UUID* local_report_id = nullptr) override {
|
||||
PtraceClient client;
|
||||
bool connected = client.Initialize(broker_sock, client_process_id);
|
||||
EXPECT_TRUE(connected);
|
||||
@ -140,12 +171,15 @@ class MockPtraceStrategyDecider : public PtraceStrategyDecider {
|
||||
|
||||
~MockPtraceStrategyDecider() {}
|
||||
|
||||
Strategy ChooseStrategy(int sock, const ucred& client_credentials) override {
|
||||
Strategy ChooseStrategy(int sock,
|
||||
bool multiple_clients,
|
||||
const ucred& client_credentials) override {
|
||||
if (strategy_ == Strategy::kUseBroker) {
|
||||
ServerToClientMessage message = {};
|
||||
message.type = ServerToClientMessage::kTypeForkBroker;
|
||||
ExceptionHandlerProtocol::ServerToClientMessage message = {};
|
||||
message.type =
|
||||
ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker;
|
||||
|
||||
Errno status;
|
||||
ExceptionHandlerProtocol::Errno status;
|
||||
bool result = LoggingWriteFile(sock, &message, sizeof(message)) &&
|
||||
LoggingReadFileExactly(sock, &status, sizeof(status));
|
||||
EXPECT_TRUE(result);
|
||||
@ -169,13 +203,14 @@ class MockPtraceStrategyDecider : public PtraceStrategyDecider {
|
||||
DISALLOW_COPY_AND_ASSIGN(MockPtraceStrategyDecider);
|
||||
};
|
||||
|
||||
class ExceptionHandlerServerTest : public testing::Test {
|
||||
class ExceptionHandlerServerTest : public testing::TestWithParam<bool> {
|
||||
public:
|
||||
ExceptionHandlerServerTest()
|
||||
: server_(),
|
||||
delegate_(),
|
||||
server_thread_(&server_, &delegate_),
|
||||
sock_to_handler_() {}
|
||||
sock_to_handler_(),
|
||||
use_multi_client_socket_(GetParam()) {}
|
||||
|
||||
~ExceptionHandlerServerTest() = default;
|
||||
|
||||
@ -197,7 +232,7 @@ class ExceptionHandlerServerTest : public testing::Test {
|
||||
~CrashDumpTest() = default;
|
||||
|
||||
void MultiprocessParent() override {
|
||||
ClientInformation info;
|
||||
ExceptionHandlerProtocol::ClientInformation info;
|
||||
ASSERT_TRUE(
|
||||
LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info)));
|
||||
|
||||
@ -216,9 +251,8 @@ class ExceptionHandlerServerTest : public testing::Test {
|
||||
void MultiprocessChild() override {
|
||||
ASSERT_EQ(close(server_test_->sock_to_client_), 0);
|
||||
|
||||
ClientInformation info;
|
||||
ExceptionHandlerProtocol::ClientInformation info;
|
||||
info.exception_information_address = 42;
|
||||
|
||||
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info)));
|
||||
|
||||
// If the current ptrace_scope is restricted, the broker needs to be set
|
||||
@ -226,7 +260,8 @@ class ExceptionHandlerServerTest : public testing::Test {
|
||||
// ptracer allows the broker to inherit this condition.
|
||||
ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ true);
|
||||
|
||||
ExceptionHandlerClient client(server_test_->SockToHandler());
|
||||
ExceptionHandlerClient client(server_test_->SockToHandler(),
|
||||
server_test_->use_multi_client_socket_);
|
||||
ASSERT_EQ(client.RequestCrashDump(info), 0);
|
||||
}
|
||||
|
||||
@ -239,16 +274,18 @@ class ExceptionHandlerServerTest : public testing::Test {
|
||||
|
||||
void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,
|
||||
bool succeeds) {
|
||||
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
||||
ServerThread()->Start();
|
||||
|
||||
Server()->SetPtraceStrategyDecider(
|
||||
std::make_unique<MockPtraceStrategyDecider>(strategy));
|
||||
|
||||
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
||||
ServerThread()->Start();
|
||||
|
||||
CrashDumpTest test(this, succeeds);
|
||||
test.Run();
|
||||
}
|
||||
|
||||
bool UsingMultiClientSocket() const { return use_multi_client_socket_; }
|
||||
|
||||
protected:
|
||||
void SetUp() override {
|
||||
int socks[2];
|
||||
@ -256,7 +293,8 @@ class ExceptionHandlerServerTest : public testing::Test {
|
||||
sock_to_handler_.reset(socks[0]);
|
||||
sock_to_client_ = socks[1];
|
||||
|
||||
ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1])));
|
||||
ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1]),
|
||||
use_multi_client_socket_));
|
||||
}
|
||||
|
||||
private:
|
||||
@ -265,36 +303,37 @@ class ExceptionHandlerServerTest : public testing::Test {
|
||||
RunServerThread server_thread_;
|
||||
ScopedFileHandle sock_to_handler_;
|
||||
int sock_to_client_;
|
||||
bool use_multi_client_socket_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest);
|
||||
};
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, ShutdownWithNoClients) {
|
||||
TEST_P(ExceptionHandlerServerTest, ShutdownWithNoClients) {
|
||||
ServerThread()->Start();
|
||||
Hangup();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, StopWithClients) {
|
||||
TEST_P(ExceptionHandlerServerTest, StopWithClients) {
|
||||
ServerThread()->Start();
|
||||
Server()->Stop();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, StopBeforeRun) {
|
||||
TEST_P(ExceptionHandlerServerTest, StopBeforeRun) {
|
||||
Server()->Stop();
|
||||
ServerThread()->Start();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, MultipleStops) {
|
||||
TEST_P(ExceptionHandlerServerTest, MultipleStops) {
|
||||
ServerThread()->Start();
|
||||
Server()->Stop();
|
||||
Server()->Stop();
|
||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
|
||||
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
|
||||
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
||||
ServerThread()->Start();
|
||||
|
||||
@ -302,25 +341,35 @@ TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
|
||||
test.Run();
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
|
||||
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace,
|
||||
false);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) {
|
||||
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) {
|
||||
if (UsingMultiClientSocket()) {
|
||||
// The broker is not supported with multiple clients connected on a single
|
||||
// socket.
|
||||
return;
|
||||
}
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kUseBroker,
|
||||
true);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
|
||||
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace,
|
||||
true);
|
||||
}
|
||||
|
||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpError) {
|
||||
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpError) {
|
||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ExceptionHandlerServerTestSuite,
|
||||
ExceptionHandlerServerTest,
|
||||
testing::Bool()
|
||||
);
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user