mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-19 18:03: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
|
*.Makefile
|
||||||
*.ninja
|
*.ninja
|
||||||
*.pyc
|
*.pyc
|
||||||
@ -10,6 +24,7 @@
|
|||||||
.gdbinit
|
.gdbinit
|
||||||
/Makefile
|
/Makefile
|
||||||
/out
|
/out
|
||||||
|
/third_party/edo/edo
|
||||||
/third_party/fuchsia/.cipd
|
/third_party/fuchsia/.cipd
|
||||||
/third_party/fuchsia/clang
|
/third_party/fuchsia/clang
|
||||||
/third_party/fuchsia/qemu
|
/third_party/fuchsia/qemu
|
||||||
@ -19,6 +34,7 @@
|
|||||||
/third_party/linux/.cipd
|
/third_party/linux/.cipd
|
||||||
/third_party/linux/clang
|
/third_party/linux/clang
|
||||||
/third_party/linux/sysroot
|
/third_party/linux/sysroot
|
||||||
|
/third_party/lss/lss
|
||||||
/third_party/gyp/gyp
|
/third_party/gyp/gyp
|
||||||
/third_party/mini_chromium/mini_chromium
|
/third_party/mini_chromium/mini_chromium
|
||||||
/third_party/zlib/zlib
|
/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/crashpad_buildconfig.gni")
|
||||||
import("build/test.gni")
|
import("build/test.gni")
|
||||||
|
import("util/net/tls.gni")
|
||||||
|
|
||||||
config("crashpad_config") {
|
config("crashpad_config") {
|
||||||
include_dirs = [ "." ]
|
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) {
|
if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
||||||
test("crashpad_tests") {
|
test("crashpad_tests") {
|
||||||
deps = [
|
deps = [
|
||||||
"client:client_test",
|
"client:client_test",
|
||||||
"handler:handler_test",
|
|
||||||
"minidump:minidump_test",
|
"minidump:minidump_test",
|
||||||
"snapshot:snapshot_test",
|
|
||||||
"test:gmock_main",
|
"test:gmock_main",
|
||||||
"test:test_test",
|
"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) {
|
if (crashpad_is_in_fuchsia) {
|
||||||
import("//build/package.gni")
|
import("//build/package.gni")
|
||||||
package("crashpad_test") {
|
package("crashpad_test") {
|
||||||
testonly = true
|
testonly = true
|
||||||
deprecated_system_image = true
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
":crashpad_tests",
|
":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_large",
|
||||||
"snapshot:crashpad_snapshot_test_module_small",
|
"snapshot:crashpad_snapshot_test_module_small",
|
||||||
"test:crashpad_test_test_multiprocess_exec_test_child",
|
"test:crashpad_test_test_multiprocess_exec_test_child",
|
||||||
"util:generate_test_server_key",
|
|
||||||
"util:http_transport_test_server",
|
"util:http_transport_test_server",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -53,24 +94,23 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
|||||||
{
|
{
|
||||||
name = "crashpad_tests"
|
name = "crashpad_tests"
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
meta = [
|
||||||
|
{
|
||||||
|
path = "test/fuchsia_crashpad_tests.cmx"
|
||||||
|
dest = "crashpad_tests.cmx"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
binaries = [
|
||||||
{
|
{
|
||||||
name = "crashpad_test_test_multiprocess_exec_test_child"
|
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"
|
name = "http_transport_test_server"
|
||||||
dest = "crashpad_test_data/http_transport_test_server"
|
dest = "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"
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -92,43 +132,39 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
|||||||
resources = [
|
resources = [
|
||||||
{
|
{
|
||||||
path = "util/net/testdata/ascii_http_body.txt"
|
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"
|
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"
|
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"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
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_handler") {
|
|
||||||
deprecated_system_image = true
|
|
||||||
|
|
||||||
deps = [
|
|
||||||
"handler:crashpad_handler",
|
|
||||||
]
|
|
||||||
|
|
||||||
binaries = [
|
|
||||||
{
|
|
||||||
name = "crashpad_handler"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
package("crashpad_database_util") {
|
package("crashpad_database_util") {
|
||||||
deprecated_system_image = true
|
deps = [ "tools:crashpad_database_util" ]
|
||||||
|
|
||||||
deps = [
|
|
||||||
"tools:crashpad_database_util",
|
|
||||||
]
|
|
||||||
|
|
||||||
binaries = [
|
binaries = [
|
||||||
{
|
{
|
||||||
name = "crashpad_database_util"
|
name = "crashpad_database_util"
|
||||||
|
shell = true
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -146,6 +182,9 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
|||||||
"handler:handler_test",
|
"handler:handler_test",
|
||||||
"test:gtest_main",
|
"test:gtest_main",
|
||||||
]
|
]
|
||||||
|
if (crashpad_is_ios || crashpad_is_fuchsia) {
|
||||||
|
deps -= [ "handler:handler_test" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("crashpad_minidump_test") {
|
test("crashpad_minidump_test") {
|
||||||
@ -160,6 +199,9 @@ if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
|
|||||||
"snapshot:snapshot_test",
|
"snapshot:snapshot_test",
|
||||||
"test:gtest_main",
|
"test:gtest_main",
|
||||||
]
|
]
|
||||||
|
if (crashpad_is_ios) {
|
||||||
|
deps -= [ "snapshot:snapshot_test" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("crashpad_test_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>
|
Mark Mentovai <mark@chromium.org>
|
||||||
Robert Sesek <rsesek@chromium.org>
|
Robert Sesek <rsesek@chromium.org>
|
||||||
Scott Graham <scottmg@chromium.org>
|
Scott Graham <scottmg@chromium.org>
|
||||||
|
Joshua Peraza <jperaza@chromium.org>
|
||||||
|
99
DEPS
99
DEPS
@ -15,22 +15,34 @@
|
|||||||
vars = {
|
vars = {
|
||||||
'chromium_git': 'https://chromium.googlesource.com',
|
'chromium_git': 'https://chromium.googlesource.com',
|
||||||
'pull_linux_clang': False,
|
'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 = {
|
deps = {
|
||||||
'buildtools':
|
'buildtools':
|
||||||
Var('chromium_git') + '/chromium/buildtools.git@' +
|
Var('chromium_git') + '/chromium/src/buildtools.git@' +
|
||||||
'6fe4a3251488f7af86d64fc25cf442e817cf6133',
|
'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':
|
'crashpad/third_party/gtest/gtest':
|
||||||
Var('chromium_git') + '/external/github.com/google/googletest@' +
|
Var('chromium_git') + '/external/github.com/google/googletest@' +
|
||||||
'c091b0469ab4c04ee9411ef770f32360945f4c53',
|
'e3f0319d89f4cbf32993de595d984183b1a9fc57',
|
||||||
'crashpad/third_party/gyp/gyp':
|
'crashpad/third_party/gyp/gyp':
|
||||||
Var('chromium_git') + '/external/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':
|
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||||
'793e94e2c652831af2d25bb5288b04e59048c62d',
|
'8ca5ea356cdb97913d62d379d503567a80d90726',
|
||||||
'crashpad/third_party/libfuzzer/src':
|
'crashpad/third_party/libfuzzer/src':
|
||||||
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
|
Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' +
|
||||||
'fda403cf93ecb8792cb1d061564d89a6553ca020',
|
'fda403cf93ecb8792cb1d061564d89a6553ca020',
|
||||||
@ -49,7 +61,7 @@ deps = {
|
|||||||
'condition': 'checkout_linux and pull_linux_clang',
|
'condition': 'checkout_linux and pull_linux_clang',
|
||||||
'dep_type': 'cipd'
|
'dep_type': 'cipd'
|
||||||
},
|
},
|
||||||
'crashpad/third_party/linux/clang/mac-amd64': {
|
'crashpad/third_party/fuchsia/clang/mac-amd64': {
|
||||||
'packages': [
|
'packages': [
|
||||||
{
|
{
|
||||||
'package': 'fuchsia/clang/mac-amd64',
|
'package': 'fuchsia/clang/mac-amd64',
|
||||||
@ -89,22 +101,24 @@ deps = {
|
|||||||
'condition': 'checkout_fuchsia and host_os == "linux"',
|
'condition': 'checkout_fuchsia and host_os == "linux"',
|
||||||
'dep_type': 'cipd'
|
'dep_type': 'cipd'
|
||||||
},
|
},
|
||||||
'crashpad/third_party/fuchsia/sdk/linux-amd64': {
|
'crashpad/third_party/fuchsia/sdk/mac-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).
|
|
||||||
'packages': [
|
'packages': [
|
||||||
{
|
{
|
||||||
'package': 'fuchsia/sdk/linux-amd64',
|
'package': 'fuchsia/sdk/gn/mac-amd64',
|
||||||
'version': 'latest'
|
'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'
|
'dep_type': 'cipd'
|
||||||
},
|
},
|
||||||
'crashpad/third_party/win/toolchain': {
|
'crashpad/third_party/win/toolchain': {
|
||||||
@ -163,45 +177,6 @@ hooks = [
|
|||||||
'buildtools/win/clang-format.exe.sha1',
|
'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
|
# If using a local clang ("pull_linux_clang" above), also pull down a
|
||||||
# sysroot.
|
# sysroot.
|
||||||
@ -213,9 +188,13 @@ hooks = [
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'name': 'gyp',
|
'name': 'setup_gn_ios',
|
||||||
'pattern': '\.gypi?$',
|
'pattern': '.',
|
||||||
'action': ['python', 'crashpad/build/gyp_crashpad.py'],
|
'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
|
* Bugs can be reported at the [Crashpad issue
|
||||||
tracker](https://crashpad.chromium.org/bug/).
|
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.
|
perform automated builds and tests.
|
||||||
* [crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev)
|
* [crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev)
|
||||||
is the Crashpad developers’ mailing list.
|
is the Crashpad developers’ mailing list.
|
||||||
|
@ -32,9 +32,7 @@ config("crashpad_is_in_fuchsia") {
|
|||||||
|
|
||||||
group("default_exe_manifest_win") {
|
group("default_exe_manifest_win") {
|
||||||
if (crashpad_is_in_chromium) {
|
if (crashpad_is_in_chromium) {
|
||||||
deps = [
|
deps = [ "//build/win:default_exe_manifest" ]
|
||||||
"//build/win:default_exe_manifest",
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,7 +43,26 @@ config("crashpad_fuzzer_flags") {
|
|||||||
"-fsanitize=fuzzer",
|
"-fsanitize=fuzzer",
|
||||||
]
|
]
|
||||||
|
|
||||||
ldflags = [
|
ldflags = [ "-fsanitize=address" ]
|
||||||
"-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 = [
|
_default_configs = [
|
||||||
"//third_party/mini_chromium/mini_chromium/build:default",
|
"//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:Wexit_time_destructors",
|
||||||
|
"//third_party/mini_chromium/mini_chromium/build:Wimplicit_fallthrough",
|
||||||
]
|
]
|
||||||
|
|
||||||
if (crashpad_use_libfuzzer) {
|
if (crashpad_use_libfuzzer) {
|
||||||
|
@ -35,6 +35,11 @@
|
|||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
|
['OS=="android"', {
|
||||||
|
'ldflags': [
|
||||||
|
'-static-libstdc++',
|
||||||
|
],
|
||||||
|
}],
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -15,28 +15,28 @@
|
|||||||
declare_args() {
|
declare_args() {
|
||||||
# Determines various flavors of build configuration, and which concrete
|
# Determines various flavors of build configuration, and which concrete
|
||||||
# targets to use for dependencies. Valid values are "standalone", "chromium",
|
# targets to use for dependencies. Valid values are "standalone", "chromium",
|
||||||
# and "fuchsia".
|
# "fuchsia", "dart" or "external".
|
||||||
crashpad_dependencies = "standalone"
|
crashpad_dependencies = "standalone"
|
||||||
|
|
||||||
if (defined(is_fuchsia_tree) && is_fuchsia_tree) {
|
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"
|
crashpad_dependencies = "fuchsia"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" ||
|
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_chromium = crashpad_dependencies == "chromium"
|
||||||
crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia"
|
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"
|
crashpad_is_standalone = crashpad_dependencies == "standalone"
|
||||||
|
|
||||||
if (crashpad_is_in_chromium) {
|
if (crashpad_is_in_chromium) {
|
||||||
crashpad_is_mac = is_mac
|
crashpad_is_mac = is_mac
|
||||||
|
crashpad_is_ios = is_ios
|
||||||
crashpad_is_win = is_win
|
crashpad_is_win = is_win
|
||||||
crashpad_is_linux = is_linux
|
crashpad_is_linux = is_linux
|
||||||
crashpad_is_android = is_android
|
crashpad_is_android = is_android
|
||||||
@ -46,11 +46,18 @@ if (crashpad_is_in_chromium) {
|
|||||||
|
|
||||||
crashpad_is_clang = is_clang
|
crashpad_is_clang = is_clang
|
||||||
} else {
|
} else {
|
||||||
|
# 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
|
# Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into
|
||||||
# the same location in both cases.
|
# the same location in both cases.
|
||||||
import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
|
import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
|
||||||
import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
|
import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
|
||||||
|
}
|
||||||
crashpad_is_mac = mini_chromium_is_mac
|
crashpad_is_mac = mini_chromium_is_mac
|
||||||
|
crashpad_is_ios = mini_chromium_is_ios
|
||||||
crashpad_is_win = mini_chromium_is_win
|
crashpad_is_win = mini_chromium_is_win
|
||||||
crashpad_is_linux = mini_chromium_is_linux
|
crashpad_is_linux = mini_chromium_is_linux
|
||||||
crashpad_is_android = mini_chromium_is_android
|
crashpad_is_android = mini_chromium_is_android
|
||||||
@ -63,7 +70,12 @@ if (crashpad_is_in_chromium) {
|
|||||||
|
|
||||||
template("crashpad_executable") {
|
template("crashpad_executable") {
|
||||||
executable(target_name) {
|
executable(target_name) {
|
||||||
forward_variables_from(invoker, "*", [ "configs", "remove_configs" ])
|
forward_variables_from(invoker,
|
||||||
|
"*",
|
||||||
|
[
|
||||||
|
"configs",
|
||||||
|
"remove_configs",
|
||||||
|
])
|
||||||
if (defined(invoker.remove_configs)) {
|
if (defined(invoker.remove_configs)) {
|
||||||
configs -= invoker.remove_configs
|
configs -= invoker.remove_configs
|
||||||
}
|
}
|
||||||
@ -72,21 +84,23 @@ template("crashpad_executable") {
|
|||||||
configs += invoker.configs
|
configs += invoker.configs
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!defined(deps)) {
|
if (crashpad_is_in_fuchsia) {
|
||||||
deps = []
|
fdio_config = [ "//build/config/fuchsia:fdio_config" ]
|
||||||
|
if (configs + fdio_config - fdio_config == configs) {
|
||||||
|
configs += fdio_config
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crashpad_is_in_chromium) {
|
|
||||||
deps += [ "//build/config:exe_and_shlib_deps" ]
|
|
||||||
} else if (crashpad_is_in_fuchsia) {
|
|
||||||
configs += [ "//build/config/fuchsia:fdio_config" ]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template("crashpad_loadable_module") {
|
template("crashpad_loadable_module") {
|
||||||
loadable_module(target_name) {
|
loadable_module(target_name) {
|
||||||
forward_variables_from(invoker, "*", [ "configs", "remove_configs" ])
|
forward_variables_from(invoker,
|
||||||
|
"*",
|
||||||
|
[
|
||||||
|
"configs",
|
||||||
|
"remove_configs",
|
||||||
|
])
|
||||||
if (defined(invoker.remove_configs)) {
|
if (defined(invoker.remove_configs)) {
|
||||||
configs -= invoker.remove_configs
|
configs -= invoker.remove_configs
|
||||||
}
|
}
|
||||||
@ -95,13 +109,7 @@ template("crashpad_loadable_module") {
|
|||||||
configs += invoker.configs
|
configs += invoker.configs
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!defined(deps)) {
|
if (crashpad_is_in_fuchsia) {
|
||||||
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" ]
|
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("crashpad_buildconfig.gni")
|
||||||
import("test.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) {
|
if (crashpad_is_standalone && crashpad_use_libfuzzer) {
|
||||||
test(target_name) {
|
test(target_name) {
|
||||||
forward_variables_from(invoker,
|
forward_variables_from(invoker,
|
||||||
@ -38,6 +41,15 @@ template("fuzzer_test") {
|
|||||||
}
|
}
|
||||||
cflags += [ "-fsanitize=fuzzer" ]
|
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 {
|
} else {
|
||||||
not_needed(invoker, "*")
|
not_needed(invoker, "*")
|
||||||
group(target_name) {
|
group(target_name) {
|
||||||
|
@ -21,10 +21,10 @@ import sys
|
|||||||
def ChooseDependencyPath(local_path, external_path):
|
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
|
The local path, used in standalone builds, is preferred. If it is not
|
||||||
but the external path is, the external path will be used. If neither path is
|
present but the external path is, the external path will be used. If neither
|
||||||
present, the local path will be used, so that error messages uniformly refer
|
path is present, the local path will be used, so that error messages
|
||||||
to the local path.
|
uniformly refer to the local path.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
local_path: The preferred local path to use for a standalone build.
|
local_path: The preferred local path to use for a standalone build.
|
||||||
@ -41,14 +41,14 @@ def ChooseDependencyPath(local_path, external_path):
|
|||||||
|
|
||||||
|
|
||||||
script_dir = os.path.dirname(__file__)
|
script_dir = os.path.dirname(__file__)
|
||||||
crashpad_dir = (os.path.dirname(script_dir) if script_dir not in ('', os.curdir)
|
crashpad_dir = (os.path.dirname(script_dir)
|
||||||
else os.pardir)
|
if script_dir not in ('', os.curdir) else os.pardir)
|
||||||
|
|
||||||
sys.path.insert(0,
|
sys.path.insert(
|
||||||
ChooseDependencyPath(os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp',
|
0,
|
||||||
'pylib'),
|
ChooseDependencyPath(
|
||||||
os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp',
|
os.path.join(crashpad_dir, 'third_party', 'gyp', 'gyp', 'pylib'),
|
||||||
'pylib'))[1])
|
os.path.join(crashpad_dir, os.pardir, os.pardir, 'gyp', 'pylib'))[1])
|
||||||
|
|
||||||
import gyp
|
import gyp
|
||||||
|
|
||||||
@ -75,8 +75,8 @@ def main(args):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
if sys.platform == 'win32':
|
if sys.platform == 'win32':
|
||||||
# Check to make sure that no target_arch was specified. target_arch may be
|
# Check to make sure that no target_arch was specified. target_arch may
|
||||||
# set during a cross build, such as a cross build for Android.
|
# be set during a cross build, such as a cross build for Android.
|
||||||
has_target_arch = False
|
has_target_arch = False
|
||||||
for arg_index in range(0, len(args)):
|
for arg_index in range(0, len(args)):
|
||||||
arg = args[arg_index]
|
arg = args[arg_index]
|
||||||
@ -88,7 +88,8 @@ def main(args):
|
|||||||
|
|
||||||
if not has_target_arch:
|
if not has_target_arch:
|
||||||
# Also generate the x86 build.
|
# Also generate the x86 build.
|
||||||
result = gyp.main(args + ['-D', 'target_arch=ia32', '-G', 'config=Debug'])
|
result = gyp.main(args +
|
||||||
|
['-D', 'target_arch=ia32', '-G', 'config=Debug'])
|
||||||
if result != 0:
|
if result != 0:
|
||||||
return result
|
return result
|
||||||
result = gyp.main(
|
result = gyp.main(
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
# Copyright 2017 The Crashpad Authors. All rights reserved.
|
# Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||||
#
|
#
|
||||||
@ -28,85 +27,42 @@ def main(args):
|
|||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Set up an Android cross build',
|
description='Set up an Android cross build',
|
||||||
epilog='Additional arguments will be passed to gyp_crashpad.py.')
|
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')
|
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)
|
(parsed, extra_command_line_args) = parser.parse_known_args(args)
|
||||||
|
|
||||||
NDK_ERROR=(
|
ndk_bin_dir = os.path.join(parsed.ndk, 'toolchains', 'llvm', 'prebuilt',
|
||||||
'NDK must be a valid standalone NDK toolchain.\n' +
|
'linux-x86_64', 'bin')
|
||||||
'See https://developer.android.com/ndk/guides/standalone_toolchain.html')
|
if not os.path.exists(ndk_bin_dir):
|
||||||
arch_dirs = glob.glob(os.path.join(parsed.ndk, '*-linux-android*'))
|
parser.error("missing toolchain")
|
||||||
if len(arch_dirs) != 1:
|
|
||||||
parser.error(NDK_ERROR)
|
|
||||||
|
|
||||||
arch_triplet = os.path.basename(arch_dirs[0])
|
ARCH_TO_ARCH_TRIPLET = {
|
||||||
ARCH_TRIPLET_TO_ARCH = {
|
'arm': 'armv7a-linux-androideabi',
|
||||||
'arm-linux-androideabi': 'arm',
|
'arm64': 'aarch64-linux-android',
|
||||||
'aarch64-linux-android': 'arm64',
|
'ia32': 'i686-linux-android',
|
||||||
'i686-linux-android': 'ia32',
|
'x64': 'x86_64-linux-android',
|
||||||
'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]
|
|
||||||
|
|
||||||
ndk_bin_dir = os.path.join(parsed.ndk, 'bin')
|
clang_prefix = ARCH_TO_ARCH_TRIPLET[parsed.arch] + parsed.api_level
|
||||||
|
os.environ['CC_target'] = os.path.join(ndk_bin_dir, clang_prefix + '-clang')
|
||||||
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)
|
|
||||||
os.environ['CXX_target'] = os.path.join(ndk_bin_dir,
|
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__
|
extra_args = ['-D', 'android_api_level=' + parsed.api_level]
|
||||||
# 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
|
# ARM only includes 'v7a' in the tool prefix for clang
|
||||||
# Clang wrapper won’t mention __ANDROID_API__, but the standalone toolchain’s
|
tool_prefix = ('arm-linux-androideabi' if parsed.arch == 'arm' else
|
||||||
# <android/api-level.h> will #define it for both Clang and GCC.
|
ARCH_TO_ARCH_TRIPLET[parsed.arch])
|
||||||
#
|
|
||||||
# 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')
|
|
||||||
|
|
||||||
for tool in ('ar', 'nm', 'readelf'):
|
for tool in ('ar', 'nm', 'readelf'):
|
||||||
os.environ['%s_target' % tool.upper()] = (
|
os.environ['%s_target' % tool.upper()] = (os.path.join(
|
||||||
os.path.join(ndk_bin_dir, '%s-%s' % (arch_triplet, tool)))
|
ndk_bin_dir, '%s-%s' % (tool_prefix, tool)))
|
||||||
|
|
||||||
return gyp_crashpad.main(
|
return gyp_crashpad.main([
|
||||||
['-D', 'OS=android',
|
'-D', 'OS=android', '-D',
|
||||||
'-D', 'target_arch=%s' % arch,
|
'target_arch=%s' % parsed.arch, '-D', 'clang=1', '-f', 'ninja-android'
|
||||||
'-D', 'clang=%d' % (1 if parsed.compiler == 'clang' else 0),
|
] + extra_args + extra_command_line_args)
|
||||||
'-f', 'ninja-android'] +
|
|
||||||
extra_args +
|
|
||||||
extra_command_line_args)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -23,7 +23,6 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
|
|
||||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
# Sysroot revision from:
|
# Sysroot revision from:
|
||||||
@ -33,11 +32,12 @@ PATH = 'chrome-linux-sysroot/toolchain'
|
|||||||
REVISION = '3c248ba4290a5ad07085b7af07e6785bf1ae5b66'
|
REVISION = '3c248ba4290a5ad07085b7af07e6785bf1ae5b66'
|
||||||
FILENAME = 'debian_stretch_amd64_sysroot.tar.xz'
|
FILENAME = 'debian_stretch_amd64_sysroot.tar.xz'
|
||||||
|
|
||||||
|
|
||||||
def main():
|
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,
|
sysroot = os.path.join(SCRIPT_DIR, os.pardir, 'third_party', 'linux',
|
||||||
'third_party', 'linux', 'sysroot')
|
'sysroot')
|
||||||
|
|
||||||
stamp = os.path.join(sysroot, '.stamp')
|
stamp = os.path.join(sysroot, '.stamp')
|
||||||
if os.path.exists(stamp):
|
if os.path.exists(stamp):
|
||||||
|
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.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
"""Helper script to [re]start or stop a helper Fuchsia QEMU instance to be used
|
"""Helper script to [re]start or stop a helper Fuchsia QEMU instance to be used
|
||||||
for running tests without a device.
|
for running tests without a device.
|
||||||
"""
|
"""
|
||||||
@ -53,12 +52,15 @@ def _CheckForTun():
|
|||||||
"""Check for networking. TODO(scottmg): Currently, this is Linux-specific.
|
"""Check for networking. TODO(scottmg): Currently, this is Linux-specific.
|
||||||
"""
|
"""
|
||||||
returncode = subprocess.call(
|
returncode = subprocess.call(
|
||||||
['tunctl', '-b', '-u', getpass.getuser(), '-t', 'qemu'],
|
['tunctl', '-b', '-u',
|
||||||
stdout=DEVNULL, stderr=DEVNULL)
|
getpass.getuser(), '-t', 'qemu'],
|
||||||
|
stdout=DEVNULL,
|
||||||
|
stderr=DEVNULL)
|
||||||
if returncode != 0:
|
if returncode != 0:
|
||||||
print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
|
print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
print(' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only',
|
print(
|
||||||
|
' https://fuchsia.googlesource.com/zircon/+/HEAD/docs/qemu.md#enabling-networking-under-qemu-x86_64-only',
|
||||||
file=sys.stderr)
|
file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
return 0
|
return 0
|
||||||
@ -78,10 +80,13 @@ def _Start(pid_file):
|
|||||||
initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
|
initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
|
||||||
|
|
||||||
mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
|
mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
|
||||||
instance_name = 'crashpad_qemu_' + \
|
instance_name = (
|
||||||
''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8))
|
'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.
|
# These arguments are from the Fuchsia repo in zircon/scripts/run-zircon.
|
||||||
|
|
||||||
|
# yapf: disable
|
||||||
popen = subprocess.Popen([
|
popen = subprocess.Popen([
|
||||||
qemu_path,
|
qemu_path,
|
||||||
'-m', '2048',
|
'-m', '2048',
|
||||||
@ -97,15 +102,21 @@ def _Start(pid_file):
|
|||||||
'-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
|
'-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
|
||||||
'-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
|
'-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
|
||||||
'-append', 'TERM=dumb zircon.nodename=' + instance_name,
|
'-append', 'TERM=dumb zircon.nodename=' + instance_name,
|
||||||
], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL)
|
],
|
||||||
|
stdin=DEVNULL,
|
||||||
|
stdout=DEVNULL,
|
||||||
|
stderr=DEVNULL)
|
||||||
|
# yapf: enable
|
||||||
|
|
||||||
with open(pid_file, 'wb') as f:
|
with open(pid_file, 'wb') as f:
|
||||||
f.write('%d\n' % popen.pid)
|
f.write('%d\n' % popen.pid)
|
||||||
|
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools', 'netaddr')
|
netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools',
|
||||||
|
'netaddr')
|
||||||
if subprocess.call([netaddr_path, '--nowait', instance_name],
|
if subprocess.call([netaddr_path, '--nowait', instance_name],
|
||||||
stdout=open(os.devnull), stderr=open(os.devnull)) == 0:
|
stdout=open(os.devnull),
|
||||||
|
stderr=open(os.devnull)) == 0:
|
||||||
break
|
break
|
||||||
time.sleep(.5)
|
time.sleep(.5)
|
||||||
else:
|
else:
|
||||||
|
@ -24,6 +24,7 @@ import posixpath
|
|||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
@ -33,8 +34,8 @@ IS_WINDOWS_HOST = sys.platform.startswith('win')
|
|||||||
|
|
||||||
def _FindGNFromBinaryDir(binary_dir):
|
def _FindGNFromBinaryDir(binary_dir):
|
||||||
"""Attempts to determine the path to a GN binary used to generate the build
|
"""Attempts to determine the path to a GN binary used to generate the build
|
||||||
files in the given binary_dir. This is necessary because `gn` might not be in
|
files in the given binary_dir. This is necessary because `gn` might not be
|
||||||
the path or might be in a non-standard location, particularly on build
|
in the path or might be in a non-standard location, particularly on build
|
||||||
machines."""
|
machines."""
|
||||||
|
|
||||||
build_ninja = os.path.join(binary_dir, 'build.ninja')
|
build_ninja = os.path.join(binary_dir, 'build.ninja')
|
||||||
@ -57,24 +58,27 @@ def _FindGNFromBinaryDir(binary_dir):
|
|||||||
if line.startswith(' command = '):
|
if line.startswith(' command = '):
|
||||||
gn_command_line_parts = line.strip().split(' ')
|
gn_command_line_parts = line.strip().split(' ')
|
||||||
if len(gn_command_line_parts) > 2:
|
if len(gn_command_line_parts) > 2:
|
||||||
return os.path.join(binary_dir, gn_command_line_parts[2])
|
return os.path.join(binary_dir,
|
||||||
|
gn_command_line_parts[2])
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _BinaryDirTargetOS(binary_dir):
|
def _BinaryDirTargetOS(binary_dir):
|
||||||
"""Returns the apparent target OS of binary_dir, or None if none appear to be
|
"""Returns the apparent target OS of binary_dir, or None if none appear to
|
||||||
explicitly specified."""
|
be explicitly specified."""
|
||||||
|
|
||||||
gn_path = _FindGNFromBinaryDir(binary_dir)
|
gn_path = _FindGNFromBinaryDir(binary_dir)
|
||||||
|
|
||||||
if gn_path:
|
if gn_path:
|
||||||
# Look for a GN “target_os”.
|
# Look for a GN “target_os”.
|
||||||
popen = subprocess.Popen([gn_path, '--root=' + CRASHPAD_DIR,
|
popen = subprocess.Popen([
|
||||||
'args', binary_dir,
|
gn_path, '--root=' + CRASHPAD_DIR, 'args', binary_dir,
|
||||||
'--list=target_os', '--short'],
|
'--list=target_os', '--short'
|
||||||
|
],
|
||||||
shell=IS_WINDOWS_HOST,
|
shell=IS_WINDOWS_HOST,
|
||||||
stdout=subprocess.PIPE, stderr=open(os.devnull))
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=open(os.devnull))
|
||||||
value = popen.communicate()[0]
|
value = popen.communicate()[0]
|
||||||
if popen.returncode == 0:
|
if popen.returncode == 0:
|
||||||
match = re.match('target_os = "(.*)"$', value.decode('utf-8'))
|
match = re.match('target_os = "(.*)"$', value.decode('utf-8'))
|
||||||
@ -87,8 +91,7 @@ def _BinaryDirTargetOS(binary_dir):
|
|||||||
if os.path.exists(build_ninja_path):
|
if os.path.exists(build_ninja_path):
|
||||||
with open(build_ninja_path) as build_ninja_file:
|
with open(build_ninja_path) as build_ninja_file:
|
||||||
build_ninja_content = build_ninja_file.read()
|
build_ninja_content = build_ninja_file.read()
|
||||||
match = re.search('^ar = .+-linux-android(eabi)?-ar$',
|
match = re.search('-linux-android(eabi)?-ar$', build_ninja_content,
|
||||||
build_ninja_content,
|
|
||||||
re.MULTILINE)
|
re.MULTILINE)
|
||||||
if match:
|
if match:
|
||||||
return 'android'
|
return 'android'
|
||||||
@ -118,8 +121,8 @@ def _EnableVTProcessingOnWindowsConsole():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# From <wincon.h>. This would be
|
# From <wincon.h>. This would be
|
||||||
# win32console.ENABLE_VIRTUAL_TERMINAL_PROCESSING, but it’s too new to be
|
# win32console.ENABLE_VIRTUAL_TERMINAL_PROCESSING, but it’s too new to
|
||||||
# defined there.
|
# be defined there.
|
||||||
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
||||||
|
|
||||||
stdout_console.SetConsoleMode(console_mode |
|
stdout_console.SetConsoleMode(console_mode |
|
||||||
@ -147,8 +150,8 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
return
|
return
|
||||||
|
|
||||||
def _adb(*args):
|
def _adb(*args):
|
||||||
# Flush all of this script’s own buffered stdout output before running adb,
|
# Flush all of this script’s own buffered stdout output before running
|
||||||
# which will likely produce its own output on stdout.
|
# adb, which will likely produce its own output on stdout.
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
|
|
||||||
adb_command = ['adb', '-s', android_device]
|
adb_command = ['adb', '-s', android_device]
|
||||||
@ -161,33 +164,34 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
_adb('push', *args)
|
_adb('push', *args)
|
||||||
|
|
||||||
def _adb_shell(command_args, env={}):
|
def _adb_shell(command_args, env={}):
|
||||||
# Build a command to execute via “sh -c” instead of invoking it directly.
|
# Build a command to execute via “sh -c” instead of invoking it
|
||||||
# Here’s why:
|
# directly. Here’s why:
|
||||||
#
|
#
|
||||||
# /system/bin/env isn’t normally present prior to Android 6.0 (M), where
|
# /system/bin/env isn’t normally present prior to Android 6.0 (M), where
|
||||||
# toybox was introduced (Android platform/manifest 9a2c01e8450b). Instead,
|
# toybox was introduced (Android platform/manifest 9a2c01e8450b).
|
||||||
# set environment variables by using the shell’s internal “export” command.
|
# Instead, set environment variables by using the shell’s internal
|
||||||
|
# “export” command.
|
||||||
#
|
#
|
||||||
# adbd prior to Android 7.0 (N), and the adb client prior to SDK
|
# adbd prior to Android 7.0 (N), and the adb client prior to SDK
|
||||||
# platform-tools version 24, don’t know how to communicate a shell command’s
|
# platform-tools version 24, don’t know how to communicate a shell
|
||||||
# exit status. This was added in Android platform/system/core 606835ae5c4b).
|
# command’s exit status. This was added in Android platform/system/core
|
||||||
# With older adb servers and clients, adb will “exit 0” indicating success
|
# 606835ae5c4b). With older adb servers and clients, adb will “exit 0”
|
||||||
# even if the command failed on the device. This makes
|
# indicating success even if the command failed on the device. This
|
||||||
# subprocess.check_call() semantics difficult to implement directly. As a
|
# makes subprocess.check_call() semantics difficult to implement
|
||||||
# workaround, have the device send the command’s exit status over stdout and
|
# directly. As a workaround, have the device send the command’s exit
|
||||||
# pick it back up in this function.
|
# status over stdout and pick it back up in this function.
|
||||||
#
|
#
|
||||||
# Both workarounds are implemented by giving the device a simple script,
|
# Both workarounds are implemented by giving the device a simple script,
|
||||||
# which adbd will run as an “sh -c” argument.
|
# which adbd will run as an “sh -c” argument.
|
||||||
adb_command = ['adb', '-s', android_device, 'shell']
|
adb_command = ['adb', '-s', android_device, 'shell']
|
||||||
script_commands = []
|
script_commands = []
|
||||||
for k, v in env.items():
|
for k, v in env.items():
|
||||||
script_commands.append('export %s=%s' % (pipes.quote(k), pipes.quote(v)))
|
script_commands.append('export %s=%s' %
|
||||||
|
(pipes.quote(k), pipes.quote(v)))
|
||||||
script_commands.extend([
|
script_commands.extend([
|
||||||
' '.join(pipes.quote(x) for x in command_args),
|
' '.join(pipes.quote(x) for x in command_args), 'status=${?}',
|
||||||
'status=${?}',
|
'echo "status=${status}"', 'exit ${status}'
|
||||||
'echo "status=${status}"',
|
])
|
||||||
'exit ${status}'])
|
|
||||||
adb_command.append('; '.join(script_commands))
|
adb_command.append('; '.join(script_commands))
|
||||||
child = subprocess.Popen(adb_command,
|
child = subprocess.Popen(adb_command,
|
||||||
shell=IS_WINDOWS_HOST,
|
shell=IS_WINDOWS_HOST,
|
||||||
@ -211,9 +215,9 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
print(data, end='')
|
print(data, end='')
|
||||||
|
|
||||||
if final_line is None:
|
if final_line is None:
|
||||||
# Maybe there was some stderr output after the end of stdout. Old versions
|
# Maybe there was some stderr output after the end of stdout. Old
|
||||||
# of adb, prior to when the exit status could be communicated, smush the
|
# versions of adb, prior to when the exit status could be
|
||||||
# two together.
|
# communicated, smush the two together.
|
||||||
raise subprocess.CalledProcessError(-1, adb_command)
|
raise subprocess.CalledProcessError(-1, adb_command)
|
||||||
status = int(FINAL_LINE_RE.match(final_line.rstrip()).group(1))
|
status = int(FINAL_LINE_RE.match(final_line.rstrip()).group(1))
|
||||||
if status != 0:
|
if status != 0:
|
||||||
@ -221,23 +225,24 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
|
|
||||||
child.wait()
|
child.wait()
|
||||||
if child.returncode != 0:
|
if child.returncode != 0:
|
||||||
raise subprocess.CalledProcessError(subprocess.returncode, adb_command)
|
raise subprocess.CalledProcessError(subprocess.returncode,
|
||||||
|
adb_command)
|
||||||
|
|
||||||
# /system/bin/mktemp isn’t normally present prior to Android 6.0 (M), where
|
# /system/bin/mktemp isn’t normally present prior to Android 6.0 (M), where
|
||||||
# toybox was introduced (Android platform/manifest 9a2c01e8450b). Fake it with
|
# toybox was introduced (Android platform/manifest 9a2c01e8450b). Fake it
|
||||||
# a host-generated name. This won’t retry if the name is in use, but with 122
|
# with a host-generated name. This won’t retry if the name is in use, but
|
||||||
# bits of randomness, it should be OK. This uses “mkdir” instead of “mkdir -p”
|
# with 122 bits of randomness, it should be OK. This uses “mkdir” instead of
|
||||||
# because the latter will not indicate failure if the directory already
|
# “mkdir -p”because the latter will not indicate failure if the directory
|
||||||
# exists.
|
# already exists.
|
||||||
device_temp_dir = '/data/local/tmp/%s.%s' % (test, uuid.uuid4().hex)
|
device_temp_dir = '/data/local/tmp/%s.%s' % (test, uuid.uuid4().hex)
|
||||||
_adb_shell(['mkdir', device_temp_dir])
|
_adb_shell(['mkdir', device_temp_dir])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Specify test dependencies that must be pushed to the device. This could be
|
# Specify test dependencies that must be pushed to the device. This
|
||||||
# determined automatically in a GN build, following the example used for
|
# could be determined automatically in a GN build, following the example
|
||||||
# Fuchsia. Since nothing like that exists for GYP, hard-code it for
|
# used for Fuchsia. Since nothing like that exists for GYP, hard-code it
|
||||||
# supported tests.
|
# for supported tests.
|
||||||
test_build_artifacts = [test]
|
test_build_artifacts = [test, 'crashpad_handler']
|
||||||
test_data = ['test/test_paths_test_data_root.txt']
|
test_data = ['test/test_paths_test_data_root.txt']
|
||||||
|
|
||||||
if test == 'crashpad_test_test':
|
if test == 'crashpad_test_test':
|
||||||
@ -250,12 +255,13 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
device_out_dir = posixpath.join(device_temp_dir, 'out')
|
device_out_dir = posixpath.join(device_temp_dir, 'out')
|
||||||
device_mkdirs = [device_out_dir]
|
device_mkdirs = [device_out_dir]
|
||||||
for source_path in test_data:
|
for source_path in test_data:
|
||||||
# A trailing slash could reasonably mean to copy an entire directory, but
|
# A trailing slash could reasonably mean to copy an entire
|
||||||
# will interfere with what’s needed from the path split. All parent
|
# directory, but will interfere with what’s needed from the path
|
||||||
# directories of any source_path need to be be represented in
|
# split. All parent directories of any source_path need to be be
|
||||||
# device_mkdirs, but it’s important that no source_path itself wind up in
|
# represented in device_mkdirs, but it’s important that no
|
||||||
# device_mkdirs, even if source_path names a directory, because that would
|
# source_path itself wind up in device_mkdirs, even if source_path
|
||||||
# cause the “adb push” of the directory below to behave incorrectly.
|
# names a directory, because that would cause the “adb push” of the
|
||||||
|
# directory below to behave incorrectly.
|
||||||
if source_path.endswith(posixpath.sep):
|
if source_path.endswith(posixpath.sep):
|
||||||
source_path = source_path[:-1]
|
source_path = source_path[:-1]
|
||||||
|
|
||||||
@ -270,7 +276,8 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
# Push the test binary and any other build output to the device.
|
# Push the test binary and any other build output to the device.
|
||||||
local_test_build_artifacts = []
|
local_test_build_artifacts = []
|
||||||
for artifact in test_build_artifacts:
|
for artifact in test_build_artifacts:
|
||||||
local_test_build_artifacts.append(os.path.join(binary_dir, artifact))
|
local_test_build_artifacts.append(os.path.join(
|
||||||
|
binary_dir, artifact))
|
||||||
_adb_push(local_test_build_artifacts, device_out_dir)
|
_adb_push(local_test_build_artifacts, device_out_dir)
|
||||||
|
|
||||||
# Push test data to the device.
|
# Push test data to the device.
|
||||||
@ -278,20 +285,21 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
_adb_push([os.path.join(CRASHPAD_DIR, source_path)],
|
_adb_push([os.path.join(CRASHPAD_DIR, source_path)],
|
||||||
posixpath.join(device_temp_dir, source_path))
|
posixpath.join(device_temp_dir, source_path))
|
||||||
|
|
||||||
# Run the test on the device. Pass the test data root in the environment.
|
# Run the test on the device. Pass the test data root in the
|
||||||
|
# environment.
|
||||||
#
|
#
|
||||||
# Because the test will not run with its standard output attached to a
|
# Because the test will not run with its standard output attached to a
|
||||||
# pseudo-terminal device, gtest will not normally enable colored output, so
|
# pseudo-terminal device, gtest will not normally enable colored output,
|
||||||
# mimic gtest’s own logic for deciding whether to enable color by checking
|
# so mimic gtest’s own logic for deciding whether to enable color by
|
||||||
# this script’s own standard output connection. The whitelist of TERM values
|
# checking this script’s own standard output connection. The whitelist
|
||||||
# comes from gtest googletest/src/gtest.cc
|
# of TERM values comes from gtest googletest/src/gtest.cc
|
||||||
# testing::internal::ShouldUseColor().
|
# testing::internal::ShouldUseColor().
|
||||||
env = {'CRASHPAD_TEST_DATA_ROOT': device_temp_dir}
|
env = {'CRASHPAD_TEST_DATA_ROOT': device_temp_dir}
|
||||||
gtest_color = os.environ.get('GTEST_COLOR')
|
gtest_color = os.environ.get('GTEST_COLOR')
|
||||||
if gtest_color in ('auto', None):
|
if gtest_color in ('auto', None):
|
||||||
if (sys.stdout.isatty() and
|
if (sys.stdout.isatty() and
|
||||||
(os.environ.get('TERM') in
|
(os.environ.get('TERM')
|
||||||
('xterm', 'xterm-color', 'xterm-256color', 'screen',
|
in ('xterm', 'xterm-color', 'xterm-256color', 'screen',
|
||||||
'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode',
|
'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode',
|
||||||
'rxvt-unicode-256color', 'linux', 'cygwin') or
|
'rxvt-unicode-256color', 'linux', 'cygwin') or
|
||||||
(IS_WINDOWS_HOST and _EnableVTProcessingOnWindowsConsole()))):
|
(IS_WINDOWS_HOST and _EnableVTProcessingOnWindowsConsole()))):
|
||||||
@ -299,7 +307,8 @@ def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
|
|||||||
else:
|
else:
|
||||||
gtest_color = 'no'
|
gtest_color = 'no'
|
||||||
env['GTEST_COLOR'] = gtest_color
|
env['GTEST_COLOR'] = gtest_color
|
||||||
_adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line, env)
|
_adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line,
|
||||||
|
env)
|
||||||
finally:
|
finally:
|
||||||
_adb_shell(['rm', '-rf', device_temp_dir])
|
_adb_shell(['rm', '-rf', device_temp_dir])
|
||||||
|
|
||||||
@ -315,9 +324,10 @@ def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests):
|
|||||||
with open(targets_file, 'wb') as f:
|
with open(targets_file, 'wb') as f:
|
||||||
f.write('//:' + '\n//:'.join(tests) + '\n')
|
f.write('//:' + '\n//:'.join(tests) + '\n')
|
||||||
gn_path = _FindGNFromBinaryDir(binary_dir)
|
gn_path = _FindGNFromBinaryDir(binary_dir)
|
||||||
subprocess.check_call(
|
subprocess.check_call([
|
||||||
[gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir,
|
gn_path, '--root=' + CRASHPAD_DIR, 'gen', binary_dir,
|
||||||
'--runtime-deps-list-file=' + targets_file])
|
'--runtime-deps-list-file=' + targets_file
|
||||||
|
])
|
||||||
|
|
||||||
# Run again so that --runtime-deps-list-file isn't in the regen rule. See
|
# Run again so that --runtime-deps-list-file isn't in the regen rule. See
|
||||||
# https://crbug.com/814816.
|
# https://crbug.com/814816.
|
||||||
@ -357,7 +367,9 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line):
|
|||||||
# Run loglistener and filter the output to know when the test is done.
|
# Run loglistener and filter the output to know when the test is done.
|
||||||
loglistener_process = subprocess.Popen(
|
loglistener_process = subprocess.Popen(
|
||||||
[os.path.join(sdk_root, 'tools', 'loglistener'), device_name],
|
[os.path.join(sdk_root, 'tools', 'loglistener'), device_name],
|
||||||
stdout=subprocess.PIPE, stdin=open(os.devnull), stderr=open(os.devnull))
|
stdout=subprocess.PIPE,
|
||||||
|
stdin=open(os.devnull),
|
||||||
|
stderr=open(os.devnull))
|
||||||
|
|
||||||
runtime_deps_file = os.path.join(binary_dir, test + '.runtime_deps')
|
runtime_deps_file = os.path.join(binary_dir, test + '.runtime_deps')
|
||||||
with open(runtime_deps_file, 'rb') as f:
|
with open(runtime_deps_file, 'rb') as f:
|
||||||
@ -368,8 +380,8 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line):
|
|||||||
by using pipes.quote(), and then each command is chained by shell ';'.
|
by using pipes.quote(), and then each command is chained by shell ';'.
|
||||||
"""
|
"""
|
||||||
netruncmd_path = os.path.join(sdk_root, 'tools', 'netruncmd')
|
netruncmd_path = os.path.join(sdk_root, 'tools', 'netruncmd')
|
||||||
final_args = ' ; '.join(' '.join(pipes.quote(x) for x in command)
|
final_args = ' ; '.join(
|
||||||
for command in args)
|
' '.join(pipes.quote(x) for x in command) for command in args)
|
||||||
subprocess.check_call([netruncmd_path, device_name, final_args])
|
subprocess.check_call([netruncmd_path, device_name, final_args])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -379,35 +391,38 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line):
|
|||||||
staging_root = test_root + '/pkg'
|
staging_root = test_root + '/pkg'
|
||||||
|
|
||||||
# Make a staging directory tree on the target.
|
# Make a staging directory tree on the target.
|
||||||
directories_to_create = [tmp_root,
|
directories_to_create = [
|
||||||
|
tmp_root,
|
||||||
'%s/bin' % staging_root,
|
'%s/bin' % staging_root,
|
||||||
'%s/assets' % staging_root]
|
'%s/assets' % staging_root
|
||||||
|
]
|
||||||
netruncmd(['mkdir', '-p'] + directories_to_create)
|
netruncmd(['mkdir', '-p'] + directories_to_create)
|
||||||
|
|
||||||
def netcp(local_path):
|
def netcp(local_path):
|
||||||
"""Uses `netcp` to copy a file or directory to the device. Files located
|
"""Uses `netcp` to copy a file or directory to the device. Files
|
||||||
inside the build dir are stored to /pkg/bin, otherwise to /pkg/assets.
|
located inside the build dir are stored to /pkg/bin, otherwise to
|
||||||
.so files are stored somewhere completely different, into /boot/lib (!).
|
/pkg/assets. .so files are stored somewhere completely different,
|
||||||
This is because the loader service does not yet correctly handle the
|
into /boot/lib (!). This is because the loader service does not yet
|
||||||
namespace in which the caller is being run, and so can only load .so files
|
correctly handle the namespace in which the caller is being run, and
|
||||||
from a couple hardcoded locations, the only writable one of which is
|
so can only load .so files from a couple hardcoded locations, the
|
||||||
/boot/lib, so we copy all .so files there. This bug is filed upstream as
|
only writable one of which is /boot/lib, so we copy all .so files
|
||||||
ZX-1619.
|
there. This bug is filed upstream as ZX-1619.
|
||||||
"""
|
"""
|
||||||
in_binary_dir = local_path.startswith(binary_dir + '/')
|
in_binary_dir = local_path.startswith(binary_dir + '/')
|
||||||
if in_binary_dir:
|
if in_binary_dir:
|
||||||
if local_path.endswith('.so'):
|
if local_path.endswith('.so'):
|
||||||
target_path = os.path.join(
|
target_path = os.path.join('/boot/lib',
|
||||||
'/boot/lib', local_path[len(binary_dir)+1:])
|
local_path[len(binary_dir) + 1:])
|
||||||
else:
|
else:
|
||||||
target_path = os.path.join(
|
target_path = os.path.join(staging_root, 'bin',
|
||||||
staging_root, 'bin', local_path[len(binary_dir)+1:])
|
local_path[len(binary_dir) + 1:])
|
||||||
else:
|
else:
|
||||||
relative_path = os.path.relpath(local_path, CRASHPAD_DIR)
|
relative_path = os.path.relpath(local_path, CRASHPAD_DIR)
|
||||||
target_path = os.path.join(staging_root, 'assets', relative_path)
|
target_path = os.path.join(staging_root, 'assets',
|
||||||
|
relative_path)
|
||||||
netcp_path = os.path.join(sdk_root, 'tools', 'netcp')
|
netcp_path = os.path.join(sdk_root, 'tools', 'netcp')
|
||||||
subprocess.check_call([netcp_path, local_path,
|
subprocess.check_call(
|
||||||
device_name + ':' + target_path],
|
[netcp_path, local_path, device_name + ':' + target_path],
|
||||||
stderr=open(os.devnull))
|
stderr=open(os.devnull))
|
||||||
|
|
||||||
# Copy runtime deps into the staging tree.
|
# Copy runtime deps into the staging tree.
|
||||||
@ -422,19 +437,87 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line):
|
|||||||
|
|
||||||
done_message = 'TERMINATED: ' + unique_id
|
done_message = 'TERMINATED: ' + unique_id
|
||||||
namespace_command = [
|
namespace_command = [
|
||||||
'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '/svc=/svc',
|
'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root,
|
||||||
'--replace-child-argv0=/pkg/bin/' + test, '--',
|
'/svc=/svc', '--replace-child-argv0=/pkg/bin/' + test, '--',
|
||||||
staging_root + '/bin/' + test] + extra_command_line
|
staging_root + '/bin/' + test
|
||||||
|
] + extra_command_line
|
||||||
netruncmd(namespace_command, ['echo', done_message])
|
netruncmd(namespace_command, ['echo', done_message])
|
||||||
|
|
||||||
success = _HandleOutputFromFuchsiaLogListener(
|
success = _HandleOutputFromFuchsiaLogListener(loglistener_process,
|
||||||
loglistener_process, done_message)
|
done_message)
|
||||||
if not success:
|
if not success:
|
||||||
raise subprocess.CalledProcessError(1, test)
|
raise subprocess.CalledProcessError(1, test)
|
||||||
finally:
|
finally:
|
||||||
netruncmd(['rm', '-rf', test_root])
|
netruncmd(['rm', '-rf', test_root])
|
||||||
|
|
||||||
|
|
||||||
|
def _RunOnIOSTarget(binary_dir, test, is_xcuitest=False):
|
||||||
|
"""Runs the given iOS |test| app on iPhone 8 with the default OS version."""
|
||||||
|
|
||||||
|
def xctest(binary_dir, test):
|
||||||
|
"""Returns a dict containing the xctestrun data needed to run an
|
||||||
|
XCTest-based test app."""
|
||||||
|
test_path = os.path.join(CRASHPAD_DIR, binary_dir)
|
||||||
|
module_data = {
|
||||||
|
'TestBundlePath': os.path.join(test_path, test + '_module.xctest'),
|
||||||
|
'TestHostPath': os.path.join(test_path, test + '.app'),
|
||||||
|
'TestingEnvironmentVariables': {
|
||||||
|
'DYLD_FRAMEWORK_PATH': '__TESTROOT__/Debug-iphonesimulator:',
|
||||||
|
'DYLD_INSERT_LIBRARIES':
|
||||||
|
('__PLATFORMS__/iPhoneSimulator.platform/Developer/'
|
||||||
|
'usr/lib/libXCTestBundleInject.dylib'),
|
||||||
|
'DYLD_LIBRARY_PATH': '__TESTROOT__/Debug-iphonesimulator',
|
||||||
|
'IDEiPhoneInternalTestBundleName': test + '.app',
|
||||||
|
'XCInjectBundleInto': '__TESTHOST__/' + test,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {test: module_data}
|
||||||
|
|
||||||
|
def xcuitest(binary_dir, test):
|
||||||
|
"""Returns a dict containing the xctestrun data needed to run an
|
||||||
|
XCUITest-based test app."""
|
||||||
|
|
||||||
|
test_path = os.path.join(CRASHPAD_DIR, binary_dir)
|
||||||
|
runner_path = os.path.join(test_path, test + '_module-Runner.app')
|
||||||
|
bundle_path = os.path.join(runner_path, 'PlugIns',
|
||||||
|
test + '_module.xctest')
|
||||||
|
target_app_path = os.path.join(test_path, test + '.app')
|
||||||
|
module_data = {
|
||||||
|
'IsUITestBundle': True,
|
||||||
|
'IsXCTRunnerHostedTestBundle': True,
|
||||||
|
'TestBundlePath': bundle_path,
|
||||||
|
'TestHostPath': runner_path,
|
||||||
|
'UITargetAppPath': target_app_path,
|
||||||
|
'DependentProductPaths': [
|
||||||
|
bundle_path, runner_path, target_app_path
|
||||||
|
],
|
||||||
|
'TestingEnvironmentVariables': {
|
||||||
|
'DYLD_FRAMEWORK_PATH': '__TESTROOT__/Debug-iphonesimulator:',
|
||||||
|
'DYLD_INSERT_LIBRARIES':
|
||||||
|
('__PLATFORMS__/iPhoneSimulator.platform/Developer/'
|
||||||
|
'usr/lib/libXCTestBundleInject.dylib'),
|
||||||
|
'DYLD_LIBRARY_PATH': '__TESTROOT__/Debug-iphonesimulator',
|
||||||
|
'XCInjectBundleInto': '__TESTHOST__/' + test + '_module-Runner',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return {test: module_data}
|
||||||
|
|
||||||
|
with tempfile.NamedTemporaryFile() as f:
|
||||||
|
import plistlib
|
||||||
|
|
||||||
|
xctestrun_path = f.name
|
||||||
|
print(xctestrun_path)
|
||||||
|
if is_xcuitest:
|
||||||
|
plistlib.writePlist(xcuitest(binary_dir, test), xctestrun_path)
|
||||||
|
else:
|
||||||
|
plistlib.writePlist(xctest(binary_dir, test), xctestrun_path)
|
||||||
|
|
||||||
|
subprocess.check_call([
|
||||||
|
'xcodebuild', 'test-without-building', '-xctestrun', xctestrun_path,
|
||||||
|
'-destination', 'platform=iOS Simulator,name=iPhone 8'
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
# This script is primarily used from the waterfall so that the list of tests
|
# This script is primarily used from the waterfall so that the list of tests
|
||||||
# that are run is maintained in-tree, rather than in a separate infrastructure
|
# that are run is maintained in-tree, rather than in a separate infrastructure
|
||||||
# location in the recipe.
|
# location in the recipe.
|
||||||
@ -447,11 +530,11 @@ def main(args):
|
|||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Tell 64-bit Windows tests where to find 32-bit test executables, for
|
# Tell 64-bit Windows tests where to find 32-bit test executables, for
|
||||||
# cross-bitted testing. This relies on the fact that the GYP build by default
|
# cross-bitted testing. This relies on the fact that the GYP build by
|
||||||
# uses {Debug,Release} for the 32-bit build and {Debug,Release}_x64 for the
|
# default uses {Debug,Release} for the 32-bit build and {Debug,Release}_x64
|
||||||
# 64-bit build. This is not a universally valid assumption, and if it’s not
|
# for the 64-bit build. This is not a universally valid assumption, and if
|
||||||
# met, 64-bit tests that require 32-bit build output will disable themselves
|
# it’s not met, 64-bit tests that require 32-bit build output will disable
|
||||||
# dynamically.
|
# themselves dynamically.
|
||||||
if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and
|
if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and
|
||||||
'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ):
|
'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ):
|
||||||
binary_dir_32 = args.binary_dir[:-4]
|
binary_dir_32 = args.binary_dir[:-4]
|
||||||
@ -461,6 +544,7 @@ def main(args):
|
|||||||
target_os = _BinaryDirTargetOS(args.binary_dir)
|
target_os = _BinaryDirTargetOS(args.binary_dir)
|
||||||
is_android = target_os == 'android'
|
is_android = target_os == 'android'
|
||||||
is_fuchsia = target_os == 'fuchsia'
|
is_fuchsia = target_os == 'fuchsia'
|
||||||
|
is_ios = target_os == 'ios'
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
'crashpad_client_test',
|
'crashpad_client_test',
|
||||||
@ -480,13 +564,13 @@ def main(args):
|
|||||||
for line in adb_devices.splitlines():
|
for line in adb_devices.splitlines():
|
||||||
line = line.decode('utf-8')
|
line = line.decode('utf-8')
|
||||||
if (line == 'List of devices attached' or
|
if (line == 'List of devices attached' or
|
||||||
re.match('^\* daemon .+ \*$', line) or
|
re.match('^\* daemon .+ \*$', line) or line == ''):
|
||||||
line == ''):
|
|
||||||
continue
|
continue
|
||||||
(device, ignore) = line.split('\t')
|
(device, ignore) = line.split('\t')
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
if len(devices) != 1:
|
if len(devices) != 1:
|
||||||
print("Please set ANDROID_DEVICE to your device's id", file=sys.stderr)
|
print("Please set ANDROID_DEVICE to your device's id",
|
||||||
|
file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
android_device = devices[0]
|
android_device = devices[0]
|
||||||
print('Using autodetected Android device:', android_device)
|
print('Using autodetected Android device:', android_device)
|
||||||
@ -494,7 +578,8 @@ def main(args):
|
|||||||
zircon_nodename = os.environ.get('ZIRCON_NODENAME')
|
zircon_nodename = os.environ.get('ZIRCON_NODENAME')
|
||||||
if not zircon_nodename:
|
if not zircon_nodename:
|
||||||
netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls')
|
netls = os.path.join(_GetFuchsiaSDKRoot(), 'tools', 'netls')
|
||||||
popen = subprocess.Popen([netls, '--nowait'], stdout=subprocess.PIPE)
|
popen = subprocess.Popen([netls, '--nowait'],
|
||||||
|
stdout=subprocess.PIPE)
|
||||||
devices = popen.communicate()[0].splitlines()
|
devices = popen.communicate()[0].splitlines()
|
||||||
if popen.returncode != 0 or len(devices) != 1:
|
if popen.returncode != 0 or len(devices) != 1:
|
||||||
print("Please set ZIRCON_NODENAME to your device's hostname",
|
print("Please set ZIRCON_NODENAME to your device's hostname",
|
||||||
@ -504,6 +589,8 @@ def main(args):
|
|||||||
print('Using autodetected Fuchsia device:', zircon_nodename)
|
print('Using autodetected Fuchsia device:', zircon_nodename)
|
||||||
_GenerateFuchsiaRuntimeDepsFiles(
|
_GenerateFuchsiaRuntimeDepsFiles(
|
||||||
args.binary_dir, [t for t in tests if not t.endswith('.py')])
|
args.binary_dir, [t for t in tests if not t.endswith('.py')])
|
||||||
|
elif is_ios:
|
||||||
|
tests.append('ios_crash_xcuitests')
|
||||||
elif IS_WINDOWS_HOST:
|
elif IS_WINDOWS_HOST:
|
||||||
tests.append('snapshot/win/end_to_end_test.py')
|
tests.append('snapshot/win/end_to_end_test.py')
|
||||||
|
|
||||||
@ -519,8 +606,10 @@ def main(args):
|
|||||||
print(test)
|
print(test)
|
||||||
print('-' * 80)
|
print('-' * 80)
|
||||||
if test.endswith('.py'):
|
if test.endswith('.py'):
|
||||||
subprocess.check_call(
|
subprocess.check_call([
|
||||||
[sys.executable, os.path.join(CRASHPAD_DIR, test), args.binary_dir])
|
sys.executable,
|
||||||
|
os.path.join(CRASHPAD_DIR, test), args.binary_dir
|
||||||
|
])
|
||||||
else:
|
else:
|
||||||
extra_command_line = []
|
extra_command_line = []
|
||||||
if args.gtest_filter:
|
if args.gtest_filter:
|
||||||
@ -531,6 +620,10 @@ def main(args):
|
|||||||
elif is_fuchsia:
|
elif is_fuchsia:
|
||||||
_RunOnFuchsiaTarget(args.binary_dir, test, zircon_nodename,
|
_RunOnFuchsiaTarget(args.binary_dir, test, zircon_nodename,
|
||||||
extra_command_line)
|
extra_command_line)
|
||||||
|
elif is_ios:
|
||||||
|
_RunOnIOSTarget(args.binary_dir,
|
||||||
|
test,
|
||||||
|
is_xcuitest=test.startswith('ios'))
|
||||||
else:
|
else:
|
||||||
subprocess.check_call([os.path.join(args.binary_dir, test)] +
|
subprocess.check_call([os.path.join(args.binary_dir, test)] +
|
||||||
extra_command_line)
|
extra_command_line)
|
||||||
|
@ -18,9 +18,32 @@ if (crashpad_is_in_chromium) {
|
|||||||
import("//testing/test.gni")
|
import("//testing/test.gni")
|
||||||
} else {
|
} else {
|
||||||
template("test") {
|
template("test") {
|
||||||
|
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) {
|
executable(target_name) {
|
||||||
testonly = true
|
testonly = true
|
||||||
forward_variables_from(invoker, "*")
|
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) {
|
if (crashpad_is_linux || crashpad_is_android) {
|
||||||
set_sources_assignment_filter([])
|
set_sources_assignment_filter([])
|
||||||
sources += [
|
sources += [
|
||||||
@ -77,22 +84,36 @@ static_library("client") {
|
|||||||
|
|
||||||
public_configs = [ "..:crashpad_config" ]
|
public_configs = [ "..:crashpad_config" ]
|
||||||
|
|
||||||
deps = [
|
public_deps = [
|
||||||
"../compat",
|
|
||||||
"../third_party/mini_chromium:base",
|
"../third_party/mini_chromium:base",
|
||||||
"../util",
|
"../util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
deps = []
|
||||||
|
|
||||||
if (crashpad_is_win) {
|
if (crashpad_is_win) {
|
||||||
libs = [ "rpcrt4.lib" ]
|
libs = [ "rpcrt4.lib" ]
|
||||||
cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
|
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 += [
|
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") {
|
source_set("client_test") {
|
||||||
@ -116,6 +137,17 @@ source_set("client_test") {
|
|||||||
sources += [ "crashpad_client_win_test.cc" ]
|
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) {
|
if (crashpad_is_linux || crashpad_is_android) {
|
||||||
sources += [ "crashpad_client_linux_test.cc" ]
|
sources += [ "crashpad_client_linux_test.cc" ]
|
||||||
}
|
}
|
||||||
@ -131,9 +163,9 @@ source_set("client_test") {
|
|||||||
"../util",
|
"../util",
|
||||||
]
|
]
|
||||||
|
|
||||||
data_deps = [
|
if (!crashpad_is_ios && !crashpad_is_fuchsia) {
|
||||||
"../handler:crashpad_handler",
|
data_deps = [ "../handler:crashpad_handler" ]
|
||||||
]
|
}
|
||||||
|
|
||||||
if (crashpad_is_win) {
|
if (crashpad_is_win) {
|
||||||
data_deps += [ "../handler:crashpad_handler_console" ]
|
data_deps += [ "../handler:crashpad_handler_console" ]
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
'dependencies': [
|
'dependencies': [
|
||||||
'../compat/compat.gyp:crashpad_compat',
|
'../compat/compat.gyp:crashpad_compat',
|
||||||
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||||
|
'../third_party/lss/lss.gyp:lss',
|
||||||
'../util/util.gyp:crashpad_util',
|
'../util/util.gyp:crashpad_util',
|
||||||
],
|
],
|
||||||
'include_dirs': [
|
'include_dirs': [
|
||||||
|
@ -61,14 +61,14 @@ std::vector<std::string> BuildHandlerArgvStrings(
|
|||||||
return argv_strings;
|
return argv_strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConvertArgvStrings(const std::vector<std::string>& argv_strings,
|
void StringVectorToCStringVector(const std::vector<std::string>& strings,
|
||||||
std::vector<const char*>* argv) {
|
std::vector<const char*>* c_strings) {
|
||||||
argv->clear();
|
c_strings->clear();
|
||||||
argv->reserve(argv_strings.size() + 1);
|
c_strings->reserve(strings.size() + 1);
|
||||||
for (const auto& arg : argv_strings) {
|
for (const auto& str : strings) {
|
||||||
argv->push_back(arg.c_str());
|
c_strings->push_back(str.c_str());
|
||||||
}
|
}
|
||||||
argv->push_back(nullptr);
|
c_strings->push_back(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -40,11 +40,11 @@ std::vector<std::string> BuildHandlerArgvStrings(
|
|||||||
//! \brief Flattens a string vector into a const char* vector suitable for use
|
//! \brief Flattens a string vector into a const char* vector suitable for use
|
||||||
//! in an exec() call.
|
//! in an exec() call.
|
||||||
//!
|
//!
|
||||||
//! \param[in] argv_strings Arguments to be passed to child process, typically
|
//! \param[in] strings A vector of string data. This vector must remain valid
|
||||||
//! created by BuildHandlerArgvStrings().
|
//! for the lifetime of \a c_strings.
|
||||||
//! \param[out] argv argv suitable for starting the child process.
|
//! \param[out] c_strings A vector of pointers to the string data in \a strings.
|
||||||
void ConvertArgvStrings(const std::vector<std::string>& argv_strings,
|
void StringVectorToCStringVector(const std::vector<std::string>& strings,
|
||||||
std::vector<const char*>* argv);
|
std::vector<const char*>* c_strings);
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ CrashReportDatabase::Report::Report()
|
|||||||
uploaded(false),
|
uploaded(false),
|
||||||
last_upload_attempt_time(0),
|
last_upload_attempt_time(0),
|
||||||
upload_attempts(0),
|
upload_attempts(0),
|
||||||
upload_explicitly_requested(false) {}
|
upload_explicitly_requested(false),
|
||||||
|
total_size(0u) {}
|
||||||
|
|
||||||
CrashReportDatabase::NewReport::NewReport()
|
CrashReportDatabase::NewReport::NewReport()
|
||||||
: writer_(std::make_unique<FileWriter>()),
|
: writer_(std::make_unique<FileWriter>()),
|
||||||
@ -64,6 +65,15 @@ bool CrashReportDatabase::NewReport::Initialize(
|
|||||||
return true;
|
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()
|
CrashReportDatabase::UploadReport::UploadReport()
|
||||||
: Report(),
|
: Report(),
|
||||||
reader_(std::make_unique<FileReader>()),
|
reader_(std::make_unique<FileReader>()),
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
|
#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
|
||||||
#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
|
#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -98,6 +99,10 @@ class CrashReportDatabase {
|
|||||||
//! Whether this crash report was explicitly requested by user to be
|
//! Whether this crash report was explicitly requested by user to be
|
||||||
//! uploaded. This can be true only if report is in the 'pending' state.
|
//! uploaded. This can be true only if report is in the 'pending' state.
|
||||||
bool upload_explicitly_requested;
|
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.
|
//! \brief A crash report that is in the process of being written.
|
||||||
@ -108,9 +113,13 @@ class CrashReportDatabase {
|
|||||||
NewReport();
|
NewReport();
|
||||||
~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(); }
|
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
|
//! A unique identifier by which this report will always be known to the
|
||||||
//! database.
|
//! database.
|
||||||
const UUID& ReportID() const { return uuid_; }
|
const UUID& ReportID() const { return uuid_; }
|
||||||
@ -137,6 +146,7 @@ class CrashReportDatabase {
|
|||||||
const base::FilePath::StringType& extension);
|
const base::FilePath::StringType& extension);
|
||||||
|
|
||||||
std::unique_ptr<FileWriter> writer_;
|
std::unique_ptr<FileWriter> writer_;
|
||||||
|
std::unique_ptr<FileReader> reader_;
|
||||||
ScopedRemoveFile file_remover_;
|
ScopedRemoveFile file_remover_;
|
||||||
std::vector<std::unique_ptr<FileWriter>> attachment_writers_;
|
std::vector<std::unique_ptr<FileWriter>> attachment_writers_;
|
||||||
std::vector<ScopedRemoveFile> attachment_removers_;
|
std::vector<ScopedRemoveFile> attachment_removers_;
|
||||||
@ -163,7 +173,7 @@ class CrashReportDatabase {
|
|||||||
//! This is not implemented on macOS or Windows.
|
//! This is not implemented on macOS or Windows.
|
||||||
std::map<std::string, FileReader*> GetAttachments() const {
|
std::map<std::string, FileReader*> GetAttachments() const {
|
||||||
return attachment_map_;
|
return attachment_map_;
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class CrashReportDatabase;
|
friend class CrashReportDatabase;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "client/crash_report_database.h"
|
#include "client/crash_report_database.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@ -25,6 +26,7 @@
|
|||||||
#include "util/file/directory_reader.h"
|
#include "util/file/directory_reader.h"
|
||||||
#include "util/file/filesystem.h"
|
#include "util/file/filesystem.h"
|
||||||
#include "util/misc/initialization_state_dcheck.h"
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
|
#include "util/misc/memory_sanitizer.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
@ -160,6 +162,34 @@ class ScopedLockFile {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(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
|
} // namespace
|
||||||
|
|
||||||
class CrashReportDatabaseGeneric : public CrashReportDatabase {
|
class CrashReportDatabaseGeneric : public CrashReportDatabase {
|
||||||
@ -253,7 +283,7 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase {
|
|||||||
void RemoveAttachmentsByUUID(const UUID& uuid);
|
void RemoveAttachmentsByUUID(const UUID& uuid);
|
||||||
|
|
||||||
// Reads the metadata for a report from path and returns it in report.
|
// 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.
|
// Wraps ReadMetadata and removes the report from the database on failure.
|
||||||
bool CleaningReadMetadata(const base::FilePath& path, Report* report);
|
bool CleaningReadMetadata(const base::FilePath& path, Report* report);
|
||||||
@ -303,6 +333,9 @@ void CrashReportDatabase::UploadReport::InitializeAttachments() {
|
|||||||
base::FilePath attachments_dir =
|
base::FilePath attachments_dir =
|
||||||
static_cast<CrashReportDatabaseGeneric*>(database_)->AttachmentsPath(
|
static_cast<CrashReportDatabaseGeneric*>(database_)->AttachmentsPath(
|
||||||
uuid);
|
uuid);
|
||||||
|
if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
DirectoryReader reader;
|
DirectoryReader reader;
|
||||||
if (!reader.Open(attachments_dir)) {
|
if (!reader.Open(attachments_dir)) {
|
||||||
return;
|
return;
|
||||||
@ -842,7 +875,6 @@ void CrashReportDatabaseGeneric::CleanOrphanedAttachments() {
|
|||||||
base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory));
|
base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory));
|
||||||
DirectoryReader reader;
|
DirectoryReader reader;
|
||||||
if (!reader.Open(root_attachments_dir)) {
|
if (!reader.Open(root_attachments_dir)) {
|
||||||
LOG(ERROR) << "no attachments dir";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -883,6 +915,9 @@ void CrashReportDatabaseGeneric::CleanOrphanedAttachments() {
|
|||||||
|
|
||||||
void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) {
|
void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) {
|
||||||
base::FilePath attachments_dir = AttachmentsPath(uuid);
|
base::FilePath attachments_dir = AttachmentsPath(uuid);
|
||||||
|
if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
DirectoryReader reader;
|
DirectoryReader reader;
|
||||||
if (!reader.Open(attachments_dir)) {
|
if (!reader.Open(attachments_dir)) {
|
||||||
return;
|
return;
|
||||||
@ -899,7 +934,6 @@ void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) {
|
|||||||
LoggingRemoveDirectory(attachments_dir);
|
LoggingRemoveDirectory(attachments_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
||||||
Report* report) {
|
Report* report) {
|
||||||
const base::FilePath metadata_path(
|
const base::FilePath metadata_path(
|
||||||
@ -910,7 +944,8 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!report->uuid.InitializeFromString(
|
UUID uuid;
|
||||||
|
if (!uuid.InitializeFromString(
|
||||||
path.BaseName().RemoveFinalExtension().value())) {
|
path.BaseName().RemoveFinalExtension().value())) {
|
||||||
LOG(ERROR) << "Couldn't interpret report uuid";
|
LOG(ERROR) << "Couldn't interpret report uuid";
|
||||||
return false;
|
return false;
|
||||||
@ -930,6 +965,12 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
|||||||
return false;
|
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->upload_attempts = metadata.upload_attempts;
|
||||||
report->last_upload_attempt_time = metadata.last_upload_attempt_time;
|
report->last_upload_attempt_time = metadata.last_upload_attempt_time;
|
||||||
report->creation_time = metadata.creation_time;
|
report->creation_time = metadata.creation_time;
|
||||||
@ -937,6 +978,7 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
|
|||||||
report->upload_explicitly_requested =
|
report->upload_explicitly_requested =
|
||||||
(metadata.attributes & kAttributeUploadExplicitlyRequested) != 0;
|
(metadata.attributes & kAttributeUploadExplicitlyRequested) != 0;
|
||||||
report->file_path = path;
|
report->file_path = path;
|
||||||
|
report->total_size = total_size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -966,6 +1008,11 @@ bool CrashReportDatabaseGeneric::WriteNewMetadata(const base::FilePath& path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReportMetadata metadata;
|
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);
|
metadata.creation_time = time(nullptr);
|
||||||
|
|
||||||
return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata));
|
return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata));
|
||||||
@ -986,6 +1033,11 @@ bool CrashReportDatabaseGeneric::WriteMetadata(const base::FilePath& path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
ReportMetadata metadata;
|
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.creation_time = report.creation_time;
|
||||||
metadata.last_upload_attempt_time = report.last_upload_attempt_time;
|
metadata.last_upload_attempt_time = report.last_upload_attempt_time;
|
||||||
metadata.upload_attempts = report.upload_attempts;
|
metadata.upload_attempts = report.upload_attempts;
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||||
#include "base/posix/eintr_wrapper.h"
|
#include "base/posix/eintr_wrapper.h"
|
||||||
#include "base/scoped_generic.h"
|
#include "base/scoped_generic.h"
|
||||||
|
#include "base/stl_util.h"
|
||||||
#include "base/strings/string_piece.h"
|
#include "base/strings/string_piece.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "base/strings/sys_string_conversions.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.
|
// 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])))
|
if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i])))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -681,6 +682,14 @@ bool CrashReportDatabaseMac::ReadReportMetadataLocked(
|
|||||||
return false;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@
|
|||||||
#include "test/errors.h"
|
#include "test/errors.h"
|
||||||
#include "test/file.h"
|
#include "test/file.h"
|
||||||
#include "test/filesystem.h"
|
#include "test/filesystem.h"
|
||||||
#include "test/gtest_disabled.h"
|
|
||||||
#include "test/scoped_temp_dir.h"
|
#include "test/scoped_temp_dir.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
#include "util/file/filesystem.h"
|
#include "util/file/filesystem.h"
|
||||||
@ -31,8 +30,7 @@ namespace {
|
|||||||
|
|
||||||
class CrashReportDatabaseTest : public testing::Test {
|
class CrashReportDatabaseTest : public testing::Test {
|
||||||
public:
|
public:
|
||||||
CrashReportDatabaseTest() {
|
CrashReportDatabaseTest() {}
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// testing::Test:
|
// testing::Test:
|
||||||
@ -41,9 +39,7 @@ class CrashReportDatabaseTest : public testing::Test {
|
|||||||
ASSERT_TRUE(db_);
|
ASSERT_TRUE(db_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ResetDatabase() {
|
void ResetDatabase() { db_.reset(); }
|
||||||
db_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
CrashReportDatabase* db() { return db_.get(); }
|
CrashReportDatabase* db() { return db_.get(); }
|
||||||
base::FilePath path() const {
|
base::FilePath path() const {
|
||||||
@ -57,6 +53,12 @@ class CrashReportDatabaseTest : public testing::Test {
|
|||||||
static constexpr char kTest[] = "test";
|
static constexpr char kTest[] = "test";
|
||||||
ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest)));
|
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;
|
UUID uuid;
|
||||||
EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid),
|
EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid),
|
||||||
CrashReportDatabase::kNoError);
|
CrashReportDatabase::kNoError);
|
||||||
@ -101,6 +103,7 @@ class CrashReportDatabaseTest : public testing::Test {
|
|||||||
EXPECT_EQ(report.last_upload_attempt_time, 0);
|
EXPECT_EQ(report.last_upload_attempt_time, 0);
|
||||||
EXPECT_EQ(report.upload_attempts, 0);
|
EXPECT_EQ(report.upload_attempts, 0);
|
||||||
EXPECT_FALSE(report.upload_explicitly_requested);
|
EXPECT_FALSE(report.upload_explicitly_requested);
|
||||||
|
EXPECT_GE(report.total_size, 0u);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RelocateDatabase() {
|
void RelocateDatabase() {
|
||||||
@ -673,7 +676,7 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) {
|
|||||||
TEST_F(CrashReportDatabaseTest, Attachments) {
|
TEST_F(CrashReportDatabaseTest, Attachments) {
|
||||||
#if defined(OS_MACOSX) || defined(OS_WIN)
|
#if defined(OS_MACOSX) || defined(OS_WIN)
|
||||||
// Attachments aren't supported on Mac and Windows yet.
|
// Attachments aren't supported on Mac and Windows yet.
|
||||||
DISABLED_TEST();
|
GTEST_SKIP();
|
||||||
#else
|
#else
|
||||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||||
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
|
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
|
||||||
@ -719,7 +722,7 @@ TEST_F(CrashReportDatabaseTest, Attachments) {
|
|||||||
TEST_F(CrashReportDatabaseTest, OrphanedAttachments) {
|
TEST_F(CrashReportDatabaseTest, OrphanedAttachments) {
|
||||||
#if defined(OS_MACOSX) || defined(OS_WIN)
|
#if defined(OS_MACOSX) || defined(OS_WIN)
|
||||||
// Attachments aren't supported on Mac and Windows yet.
|
// Attachments aren't supported on Mac and Windows yet.
|
||||||
DISABLED_TEST();
|
GTEST_SKIP();
|
||||||
#else
|
#else
|
||||||
// TODO: This is using paths that are specific to the generic implementation
|
// TODO: This is using paths that are specific to the generic implementation
|
||||||
// and will need to be generalized for Mac and Windows.
|
// and will need to be generalized for Mac and Windows.
|
||||||
@ -835,6 +838,66 @@ TEST_F(CrashReportDatabaseTest, CleanBrokenDatabase) {
|
|||||||
}
|
}
|
||||||
#endif // !OS_MACOSX && !OS_WIN
|
#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
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -458,7 +458,18 @@ void Metadata::Read() {
|
|||||||
LOG(ERROR) << "invalid string table index";
|
LOG(ERROR) << "invalid string table index";
|
||||||
return;
|
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);
|
reports_.swap(reports);
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
|
#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@ -24,6 +25,7 @@
|
|||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
#include "util/misc/capture_context.h"
|
#include "util/misc/capture_context.h"
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
@ -78,6 +80,10 @@ class CrashpadClient {
|
|||||||
//! On Fuchsia, this method binds to the exception port of the current default
|
//! On Fuchsia, this method binds to the exception port of the current default
|
||||||
//! job, and starts a Crashpad handler to monitor that port.
|
//! 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] handler The path to a Crashpad handler executable.
|
||||||
//! \param[in] database The path to a Crashpad database. The handler will be
|
//! \param[in] database The path to a Crashpad database. The handler will be
|
||||||
//! started with this path as its `--database` argument.
|
//! started with this path as its `--database` argument.
|
||||||
@ -112,6 +118,199 @@ class CrashpadClient {
|
|||||||
bool restartable,
|
bool restartable,
|
||||||
bool asynchronous_start);
|
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
|
#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
|
||||||
//! \brief Installs a signal handler to launch a handler process in reponse to
|
//! \brief Installs a signal handler to launch a handler process in reponse to
|
||||||
//! a crash.
|
//! a crash.
|
||||||
@ -135,7 +334,7 @@ class CrashpadClient {
|
|||||||
//! specified in this parameter.
|
//! specified in this parameter.
|
||||||
//!
|
//!
|
||||||
//! \return `true` on success, `false` on failure with a message logged.
|
//! \return `true` on success, `false` on failure with a message logged.
|
||||||
static bool StartHandlerAtCrash(
|
bool StartHandlerAtCrash(
|
||||||
const base::FilePath& handler,
|
const base::FilePath& handler,
|
||||||
const base::FilePath& database,
|
const base::FilePath& database,
|
||||||
const base::FilePath& metrics_dir,
|
const base::FilePath& metrics_dir,
|
||||||
@ -188,6 +387,12 @@ class CrashpadClient {
|
|||||||
//! CaptureContext() or similar.
|
//! CaptureContext() or similar.
|
||||||
static void DumpWithoutCrash(NativeCPUContext* context);
|
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.
|
//! \brief The type for custom handlers installed by clients.
|
||||||
using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);
|
using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*);
|
||||||
|
|
||||||
@ -209,8 +414,38 @@ class CrashpadClient {
|
|||||||
//! \param[in] handler The custom crash signal handler to install.
|
//! \param[in] handler The custom crash signal handler to install.
|
||||||
static void SetFirstChanceExceptionHandler(FirstChanceHandler handler);
|
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
|
#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
|
#if defined(OS_MACOSX) || DOXYGEN
|
||||||
//! \brief Sets the process’ crash handler to a Mach service registered with
|
//! \brief Sets the process’ crash handler to a Mach service registered with
|
||||||
//! the bootstrap server.
|
//! the bootstrap server.
|
||||||
@ -384,12 +619,24 @@ class CrashpadClient {
|
|||||||
static void UseSystemDefaultHandler();
|
static void UseSystemDefaultHandler();
|
||||||
#endif
|
#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:
|
private:
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
base::mac::ScopedMachSendRight exception_port_;
|
base::mac::ScopedMachSendRight exception_port_;
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
std::wstring ipc_pipe_;
|
std::wstring ipc_pipe_;
|
||||||
ScopedKernelHANDLE handler_start_thread_;
|
ScopedKernelHANDLE handler_start_thread_;
|
||||||
|
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
|
std::set<int> unhandled_signals_;
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
|
DISALLOW_COPY_AND_ASSIGN(CrashpadClient);
|
||||||
|
@ -15,15 +15,15 @@
|
|||||||
#include "client/crashpad_client.h"
|
#include "client/crashpad_client.h"
|
||||||
|
|
||||||
#include <lib/fdio/spawn.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 <zircon/processargs.h>
|
||||||
|
|
||||||
#include "base/fuchsia/fuchsia_logging.h"
|
#include "base/fuchsia/fuchsia_logging.h"
|
||||||
#include "base/fuchsia/scoped_zx_handle.h"
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "client/client_argv_handling.h"
|
#include "client/client_argv_handling.h"
|
||||||
#include "util/fuchsia/system_exception_port_key.h"
|
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
@ -43,52 +43,44 @@ bool CrashpadClient::StartHandler(
|
|||||||
DCHECK_EQ(restartable, false); // Not used on Fuchsia.
|
DCHECK_EQ(restartable, false); // Not used on Fuchsia.
|
||||||
DCHECK_EQ(asynchronous_start, 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(
|
std::vector<std::string> argv_strings = BuildHandlerArgvStrings(
|
||||||
handler, database, metrics_dir, url, annotations, arguments);
|
handler, database, metrics_dir, url, annotations, arguments);
|
||||||
|
|
||||||
std::vector<const char*> argv;
|
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,
|
// Set up handles to send to the spawned process:
|
||||||
// process handle as handle 0, with type USER0, exception port handle as
|
// 0. PA_USER0 job
|
||||||
// handle 1, also with type PA_USER0) so that it's trivial to replace
|
// 1. PA_USER0 exception channel
|
||||||
// crashlogger with crashpad_handler. The exception port is passed on, so
|
//
|
||||||
// released here. Currently it is assumed that this process's default job
|
// Currently it is assumed that this process's default job handle is the
|
||||||
// handle is the exception port that should be monitored. In the future, it
|
// exception channel that should be monitored. In the future, it might be
|
||||||
// might be useful for this to be configurable by the client.
|
// useful for this to be configurable by the client.
|
||||||
constexpr size_t kActionCount = 2;
|
zx::job job;
|
||||||
fdio_spawn_action_t actions[] = {
|
zx_status_t status =
|
||||||
{.action = FDIO_SPAWN_ACTION_ADD_HANDLE,
|
zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &job);
|
||||||
.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);
|
|
||||||
if (status != ZX_OK) {
|
if (status != ZX_OK) {
|
||||||
ZX_LOG(ERROR, status) << "zx_handle_duplicate";
|
ZX_LOG(ERROR, status) << "zx_handle_duplicate";
|
||||||
return false;
|
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];
|
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
|
// TODO(scottmg): https://crashpad.chromium.org/bug/196, FDIO_SPAWN_CLONE_ALL
|
||||||
// is useful during bringup, but should probably be made minimal for real
|
// is useful during bringup, but should probably be made minimal for real
|
||||||
// usage.
|
// usage.
|
||||||
@ -99,9 +91,8 @@ bool CrashpadClient::StartHandler(
|
|||||||
nullptr,
|
nullptr,
|
||||||
kActionCount,
|
kActionCount,
|
||||||
actions,
|
actions,
|
||||||
&child_raw,
|
child.reset_and_get_address(),
|
||||||
error_message);
|
error_message);
|
||||||
base::ScopedZxHandle child(child_raw);
|
|
||||||
if (status != ZX_OK) {
|
if (status != ZX_OK) {
|
||||||
ZX_LOG(ERROR, status) << "fdio_spawn_etc: " << error_message;
|
ZX_LOG(ERROR, status) << "fdio_spawn_etc: " << error_message;
|
||||||
return false;
|
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 <fcntl.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -25,10 +26,14 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "client/client_argv_handling.h"
|
#include "client/client_argv_handling.h"
|
||||||
|
#include "third_party/lss/lss.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
#include "util/linux/exception_handler_client.h"
|
#include "util/linux/exception_handler_client.h"
|
||||||
#include "util/linux/exception_information.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/scoped_pr_set_ptracer.h"
|
||||||
|
#include "util/linux/socket.h"
|
||||||
#include "util/misc/from_pointer_cast.h"
|
#include "util/misc/from_pointer_cast.h"
|
||||||
#include "util/posix/double_fork_and_exec.h"
|
#include "util/posix/double_fork_and_exec.h"
|
||||||
#include "util/posix/signals.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);
|
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);
|
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 {
|
class SignalHandler {
|
||||||
public:
|
public:
|
||||||
virtual void HandleCrashFatal(int signo,
|
// Returns the currently installed signal hander. May be `nullptr` if no
|
||||||
siginfo_t* siginfo,
|
// handler has been installed.
|
||||||
void* context) = 0;
|
static SignalHandler* Get() { return handler_; }
|
||||||
virtual bool HandleCrashNonFatal(int signo,
|
|
||||||
siginfo_t* siginfo,
|
// Disables any installed Crashpad signal handler for the calling thread. If a
|
||||||
void* context) = 0;
|
// 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) {
|
void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) {
|
||||||
first_chance_handler_ = handler;
|
first_chance_handler_ = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
// The base implementation for all signal handlers, suitable for calling
|
||||||
SignalHandler() = default;
|
// directly to simulate signal delivery.
|
||||||
~SignalHandler() = default;
|
bool HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
||||||
|
if (disabled_for_thread_) {
|
||||||
CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr;
|
return 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) {
|
|
||||||
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_ &&
|
if (first_chance_handler_ &&
|
||||||
first_chance_handler_(
|
first_chance_handler_(
|
||||||
signo, siginfo, static_cast<ucontext_t*>(context))) {
|
signo, siginfo, static_cast<ucontext_t*>(context))) {
|
||||||
@ -98,29 +155,100 @@ class LaunchAtCrashHandler : public SignalHandler {
|
|||||||
exception_information_.context_address =
|
exception_information_.context_address =
|
||||||
FromPointerCast<decltype(exception_information_.context_address)>(
|
FromPointerCast<decltype(exception_information_.context_address)>(
|
||||||
context);
|
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();
|
pid_t pid = fork();
|
||||||
if (pid < 0) {
|
if (pid < 0) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
|
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()));
|
execv(argv_[0], const_cast<char* const*>(argv_.data()));
|
||||||
|
}
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
waitpid(pid, &status, 0);
|
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:
|
private:
|
||||||
@ -128,22 +256,107 @@ class LaunchAtCrashHandler : public SignalHandler {
|
|||||||
|
|
||||||
~LaunchAtCrashHandler() = delete;
|
~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<std::string> argv_strings_;
|
||||||
std::vector<const char*> argv_;
|
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);
|
DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
// A pointer to the currently installed crash signal handler. This allows
|
class RequestCrashDumpHandler : public SignalHandler {
|
||||||
// the static method CrashpadClient::DumpWithoutCrashing to simulate a crash
|
public:
|
||||||
// using the currently configured crash handling strategy.
|
static RequestCrashDumpHandler* Get() {
|
||||||
static SignalHandler* g_crash_handler;
|
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
|
} // namespace
|
||||||
|
|
||||||
@ -160,14 +373,134 @@ bool CrashpadClient::StartHandler(
|
|||||||
const std::vector<std::string>& arguments,
|
const std::vector<std::string>& arguments,
|
||||||
bool restartable,
|
bool restartable,
|
||||||
bool asynchronous_start) {
|
bool asynchronous_start) {
|
||||||
// TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever
|
DCHECK(!asynchronous_start);
|
||||||
// supports accepting new connections.
|
|
||||||
// https://crashpad.chromium.org/bug/30
|
ScopedFileHandle client_sock, handler_sock;
|
||||||
NOTREACHED();
|
if (!UnixCredentialSocket::CreateCredentialSocketpair(&client_sock,
|
||||||
|
&handler_sock)) {
|
||||||
return false;
|
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
|
// 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(
|
bool CrashpadClient::StartHandlerAtCrash(
|
||||||
const base::FilePath& handler,
|
const base::FilePath& handler,
|
||||||
const base::FilePath& database,
|
const base::FilePath& database,
|
||||||
@ -179,12 +512,7 @@ bool CrashpadClient::StartHandlerAtCrash(
|
|||||||
handler, database, metrics_dir, url, annotations, arguments);
|
handler, database, metrics_dir, url, annotations, arguments);
|
||||||
|
|
||||||
auto signal_handler = LaunchAtCrashHandler::Get();
|
auto signal_handler = LaunchAtCrashHandler::Get();
|
||||||
if (signal_handler->Initialize(&argv)) {
|
return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_);
|
||||||
DCHECK(!g_crash_handler);
|
|
||||||
g_crash_handler = signal_handler;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
@ -199,14 +527,17 @@ bool CrashpadClient::StartHandlerForClient(
|
|||||||
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
||||||
handler, database, metrics_dir, url, annotations, arguments);
|
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
|
// static
|
||||||
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) {
|
||||||
DCHECK(g_crash_handler);
|
if (!SignalHandler::Get()) {
|
||||||
|
DLOG(ERROR) << "Crashpad isn't enabled";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(ARCH_CPU_ARMEL)
|
#if defined(ARCH_CPU_ARMEL)
|
||||||
memset(context->uc_regspace, 0, sizeof(context->uc_regspace));
|
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_signo = Signals::kSimulatedSigno;
|
||||||
siginfo.si_errno = 0;
|
siginfo.si_errno = 0;
|
||||||
siginfo.si_code = 0;
|
siginfo.si_code = 0;
|
||||||
g_crash_handler->HandleCrashNonFatal(
|
SignalHandler::Get()->HandleCrash(
|
||||||
siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
|
siginfo.si_signo, &siginfo, reinterpret_cast<void*>(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void CrashpadClient::CrashWithoutDump(const std::string& message) {
|
||||||
|
SignalHandler::DisableForThread();
|
||||||
|
LOG(FATAL) << message;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void CrashpadClient::SetFirstChanceExceptionHandler(
|
void CrashpadClient::SetFirstChanceExceptionHandler(
|
||||||
FirstChanceHandler handler) {
|
FirstChanceHandler handler) {
|
||||||
DCHECK(g_crash_handler);
|
DCHECK(SignalHandler::Get());
|
||||||
g_crash_handler->SetFirstChanceHandler(handler);
|
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
|
} // namespace crashpad
|
||||||
|
@ -14,8 +14,8 @@
|
|||||||
|
|
||||||
#include "client/crashpad_client.h"
|
#include "client/crashpad_client.h"
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -37,76 +37,98 @@
|
|||||||
#include "util/file/filesystem.h"
|
#include "util/file/filesystem.h"
|
||||||
#include "util/linux/exception_handler_client.h"
|
#include "util/linux/exception_handler_client.h"
|
||||||
#include "util/linux/exception_information.h"
|
#include "util/linux/exception_information.h"
|
||||||
|
#include "util/linux/socket.h"
|
||||||
#include "util/misc/address_types.h"
|
#include "util/misc/address_types.h"
|
||||||
#include "util/misc/from_pointer_cast.h"
|
#include "util/misc/from_pointer_cast.h"
|
||||||
#include "util/posix/signals.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 crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
namespace {
|
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*) {
|
bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CrashpadClient, SimulateCrash) {
|
bool InstallHandler(CrashpadClient* client,
|
||||||
ScopedTempDir temp_dir;
|
bool start_at_crash,
|
||||||
|
const base::FilePath& handler_path,
|
||||||
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
|
const base::FilePath& database_path) {
|
||||||
FILE_PATH_LITERAL("crashpad_handler"));
|
return start_at_crash
|
||||||
|
? client->StartHandlerAtCrash(handler_path,
|
||||||
crashpad::CrashpadClient client;
|
database_path,
|
||||||
ASSERT_TRUE(client.StartHandlerAtCrash(handler_path,
|
|
||||||
base::FilePath(temp_dir.path()),
|
|
||||||
base::FilePath(),
|
base::FilePath(),
|
||||||
"",
|
"",
|
||||||
std::map<std::string, std::string>(),
|
std::map<std::string, std::string>(),
|
||||||
std::vector<std::string>()));
|
std::vector<std::string>())
|
||||||
|
: client->StartHandler(handler_path,
|
||||||
auto database =
|
database_path,
|
||||||
CrashReportDatabase::InitializeWithoutCreating(temp_dir.path());
|
base::FilePath(),
|
||||||
ASSERT_TRUE(database);
|
"",
|
||||||
|
std::map<std::string, std::string>(),
|
||||||
{
|
std::vector<std::string>(),
|
||||||
CrashpadClient::SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
|
false,
|
||||||
|
false);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char kTestAnnotationName[] = "name_of_annotation";
|
constexpr char kTestAnnotationName[] = "name_of_annotation";
|
||||||
constexpr char kTestAnnotationValue[] = "value_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) {
|
void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
||||||
ProcessSnapshotMinidump minidump_snapshot;
|
ProcessSnapshotMinidump minidump_snapshot;
|
||||||
ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));
|
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 ModuleSnapshot* module : minidump_snapshot.Modules()) {
|
||||||
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
|
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
|
||||||
if (static_cast<Annotation::Type>(annotation.type) !=
|
if (static_cast<Annotation::Type>(annotation.type) !=
|
||||||
@ -126,7 +148,7 @@ void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
|||||||
ADD_FAILURE();
|
ADD_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
|
CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
|
||||||
FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
|
FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
|
||||||
|
|
||||||
VMSize temp_dir_length;
|
VMSize temp_dir_length;
|
||||||
@ -135,6 +157,9 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
|
|||||||
std::string temp_dir(temp_dir_length, '\0');
|
std::string temp_dir(temp_dir_length, '\0');
|
||||||
CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length);
|
CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length);
|
||||||
|
|
||||||
|
StartHandlerForSelfTestOptions options;
|
||||||
|
CheckedReadFileExactly(in, &options, sizeof(options));
|
||||||
|
|
||||||
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
|
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
|
||||||
FILE_PATH_LITERAL("crashpad_handler"));
|
FILE_PATH_LITERAL("crashpad_handler"));
|
||||||
|
|
||||||
@ -144,27 +169,42 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
|
|||||||
test_annotation.Set(kTestAnnotationValue);
|
test_annotation.Set(kTestAnnotationValue);
|
||||||
|
|
||||||
crashpad::CrashpadClient client;
|
crashpad::CrashpadClient client;
|
||||||
if (!client.StartHandlerAtCrash(handler_path,
|
if (!InstallHandler(&client,
|
||||||
base::FilePath(temp_dir),
|
options.start_handler_at_crash,
|
||||||
base::FilePath(),
|
handler_path,
|
||||||
"",
|
base::FilePath(temp_dir))) {
|
||||||
std::map<std::string, std::string>(),
|
|
||||||
std::vector<std::string>())) {
|
|
||||||
return EXIT_FAILURE;
|
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();
|
__builtin_trap();
|
||||||
|
|
||||||
NOTREACHED();
|
NOTREACHED();
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
class StartHandlerAtCrashTest : public MultiprocessExec {
|
class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
||||||
public:
|
public:
|
||||||
StartHandlerAtCrashTest() : MultiprocessExec() {
|
StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options)
|
||||||
SetChildTestMainFunction("StartHandlerAtCrashChild");
|
: MultiprocessExec(), options_(options) {
|
||||||
|
SetChildTestMainFunction("StartHandlerForSelfTestChild");
|
||||||
|
if (!options.simulate_crash) {
|
||||||
SetExpectedChildTerminationBuiltinTrap();
|
SetExpectedChildTerminationBuiltinTrap();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void MultiprocessParent() override {
|
void MultiprocessParent() override {
|
||||||
@ -174,6 +214,8 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
|
|||||||
WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length)));
|
WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length)));
|
||||||
ASSERT_TRUE(LoggingWriteFile(
|
ASSERT_TRUE(LoggingWriteFile(
|
||||||
WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length));
|
WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length));
|
||||||
|
ASSERT_TRUE(
|
||||||
|
LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_)));
|
||||||
|
|
||||||
// Wait for child to finish.
|
// Wait for child to finish.
|
||||||
CheckedReadFileAtEOF(ReadPipeHandle());
|
CheckedReadFileAtEOF(ReadPipeHandle());
|
||||||
@ -189,7 +231,11 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
|
|||||||
reports.clear();
|
reports.clear();
|
||||||
ASSERT_EQ(database->GetPendingReports(&reports),
|
ASSERT_EQ(database->GetPendingReports(&reports),
|
||||||
CrashReportDatabase::kNoError);
|
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;
|
std::unique_ptr<const CrashReportDatabase::UploadReport> report;
|
||||||
ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report),
|
ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report),
|
||||||
@ -197,14 +243,26 @@ class StartHandlerAtCrashTest : public MultiprocessExec {
|
|||||||
ValidateDump(report.get());
|
ValidateDump(report.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(StartHandlerAtCrashTest);
|
StartHandlerForSelfTestOptions options_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(StartHandlerForSelfInChildTest);
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(CrashpadClient, StartHandlerAtCrash) {
|
TEST_P(StartHandlerForSelfTest, StartHandlerInChild) {
|
||||||
StartHandlerAtCrashTest test;
|
if (Options().set_first_chance_handler && !Options().simulate_crash) {
|
||||||
|
// TODO(jperaza): test first chance handlers with real crashes.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
StartHandlerForSelfInChildTest test(Options());
|
||||||
test.Run();
|
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.
|
// Test state for starting the handler for another process.
|
||||||
class StartHandlerForClientTest {
|
class StartHandlerForClientTest {
|
||||||
public:
|
public:
|
||||||
@ -213,16 +271,8 @@ class StartHandlerForClientTest {
|
|||||||
|
|
||||||
bool Initialize(bool sanitize) {
|
bool Initialize(bool sanitize) {
|
||||||
sanitize_ = sanitize;
|
sanitize_ = sanitize;
|
||||||
|
return UnixCredentialSocket::CreateCredentialSocketpair(&client_sock_,
|
||||||
int socks[2];
|
&server_sock_);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StartHandlerOnDemand() {
|
bool StartHandlerOnDemand() {
|
||||||
@ -298,7 +348,7 @@ class StartHandlerForClientTest {
|
|||||||
static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
|
||||||
auto state = Get();
|
auto state = Get();
|
||||||
|
|
||||||
char c;
|
char c = 0;
|
||||||
CHECK(LoggingWriteFile(state->client_sock_, &c, sizeof(c)));
|
CHECK(LoggingWriteFile(state->client_sock_, &c, sizeof(c)));
|
||||||
|
|
||||||
ExceptionInformation exception_information;
|
ExceptionInformation exception_information;
|
||||||
@ -310,7 +360,7 @@ class StartHandlerForClientTest {
|
|||||||
context);
|
context);
|
||||||
exception_information.thread_id = syscall(SYS_gettid);
|
exception_information.thread_id = syscall(SYS_gettid);
|
||||||
|
|
||||||
ClientInformation info;
|
ExceptionHandlerProtocol::ClientInformation info;
|
||||||
info.exception_information_address =
|
info.exception_information_address =
|
||||||
FromPointerCast<decltype(info.exception_information_address)>(
|
FromPointerCast<decltype(info.exception_information_address)>(
|
||||||
&exception_information);
|
&exception_information);
|
||||||
@ -324,7 +374,7 @@ class StartHandlerForClientTest {
|
|||||||
FromPointerCast<VMAddress>(&sanitization_info);
|
FromPointerCast<VMAddress>(&sanitization_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionHandlerClient handler_client(state->client_sock_);
|
ExceptionHandlerClient handler_client(state->client_sock_, false);
|
||||||
CHECK_EQ(handler_client.RequestCrashDump(info), 0);
|
CHECK_EQ(handler_client.RequestCrashDump(info), 0);
|
||||||
|
|
||||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "base/mac/mach_logging.h"
|
#include "base/mac/mach_logging.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "util/mac/mac_util.h"
|
#include "util/mac/mac_util.h"
|
||||||
|
#include "util/mach/bootstrap.h"
|
||||||
#include "util/mach/child_port_handshake.h"
|
#include "util/mach/child_port_handshake.h"
|
||||||
#include "util/mach/exception_ports.h"
|
#include "util/mach/exception_ports.h"
|
||||||
#include "util/mach/mach_extensions.h"
|
#include "util/mach/mach_extensions.h"
|
||||||
@ -338,6 +339,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface {
|
|||||||
// this interface.
|
// this interface.
|
||||||
if (!DoubleForkAndExec(
|
if (!DoubleForkAndExec(
|
||||||
argv,
|
argv,
|
||||||
|
nullptr,
|
||||||
server_write_fd.get(),
|
server_write_fd.get(),
|
||||||
true,
|
true,
|
||||||
restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) {
|
restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) {
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "client/crashpad_client.h"
|
#include "client/crashpad_client.h"
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -35,10 +36,12 @@
|
|||||||
#include "util/misc/random_string.h"
|
#include "util/misc/random_string.h"
|
||||||
#include "util/win/address_types.h"
|
#include "util/win/address_types.h"
|
||||||
#include "util/win/command_line.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/critical_section_with_debug_info.h"
|
||||||
#include "util/win/get_function.h"
|
#include "util/win/get_function.h"
|
||||||
#include "util/win/handle.h"
|
#include "util/win/handle.h"
|
||||||
#include "util/win/initial_client_data.h"
|
#include "util/win/initial_client_data.h"
|
||||||
|
#include "util/win/loader_lock.h"
|
||||||
#include "util/win/nt_internals.h"
|
#include "util/win/nt_internals.h"
|
||||||
#include "util/win/ntstatus_logging.h"
|
#include "util/win/ntstatus_logging.h"
|
||||||
#include "util/win/process_info.h"
|
#include "util/win/process_info.h"
|
||||||
@ -93,9 +96,8 @@ base::subtle::AtomicWord g_handler_startup_state;
|
|||||||
CRITICAL_SECTION g_critical_section_with_debug_info;
|
CRITICAL_SECTION g_critical_section_with_debug_info;
|
||||||
|
|
||||||
void SetHandlerStartupState(StartupState state) {
|
void SetHandlerStartupState(StartupState state) {
|
||||||
DCHECK(state == StartupState::kSucceeded ||
|
DCHECK(state == StartupState::kSucceeded || state == StartupState::kFailed);
|
||||||
state == StartupState::kFailed);
|
base::subtle::Release_Store(&g_handler_startup_state,
|
||||||
base::subtle::Acquire_Store(&g_handler_startup_state,
|
|
||||||
static_cast<base::subtle::AtomicWord>(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.
|
// Wait until we know the handler has either succeeded or failed to start.
|
||||||
base::subtle::AtomicWord startup_state;
|
base::subtle::AtomicWord startup_state;
|
||||||
while (
|
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)) {
|
static_cast<int>(StartupState::kNotReady)) {
|
||||||
Sleep(1);
|
Sleep(1);
|
||||||
}
|
}
|
||||||
@ -187,11 +189,7 @@ void HandleAbortSignal(int signum) {
|
|||||||
EXCEPTION_RECORD record = {};
|
EXCEPTION_RECORD record = {};
|
||||||
record.ExceptionCode = STATUS_FATAL_APP_EXIT;
|
record.ExceptionCode = STATUS_FATAL_APP_EXIT;
|
||||||
record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
record.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
|
||||||
#if defined(ARCH_CPU_64_BITS)
|
record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);
|
||||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
|
|
||||||
#else
|
|
||||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
|
|
||||||
#endif // ARCH_CPU_64_BITS
|
|
||||||
|
|
||||||
EXCEPTION_POINTERS exception_pointers;
|
EXCEPTION_POINTERS exception_pointers;
|
||||||
exception_pointers.ContextRecord = &context;
|
exception_pointers.ContextRecord = &context;
|
||||||
@ -349,6 +347,8 @@ class ScopedCallSetHandlerStartupState {
|
|||||||
|
|
||||||
bool StartHandlerProcess(
|
bool StartHandlerProcess(
|
||||||
std::unique_ptr<BackgroundHandlerStartThreadData> data) {
|
std::unique_ptr<BackgroundHandlerStartThreadData> data) {
|
||||||
|
CHECK(!IsThreadInLoaderLock());
|
||||||
|
|
||||||
ScopedCallSetHandlerStartupState scoped_startup_state_caller;
|
ScopedCallSetHandlerStartupState scoped_startup_state_caller;
|
||||||
|
|
||||||
std::wstring command_line;
|
std::wstring command_line;
|
||||||
@ -476,8 +476,25 @@ 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;
|
PROCESS_INFORMATION process_info;
|
||||||
rv = CreateProcess(data->handler.value().c_str(),
|
rv = CreateProcess(
|
||||||
|
is_embedded_in_dll ? nullptr : data->handler.value().c_str(),
|
||||||
&command_line[0],
|
&command_line[0],
|
||||||
nullptr,
|
nullptr,
|
||||||
nullptr,
|
nullptr,
|
||||||
@ -756,11 +773,7 @@ void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) {
|
|||||||
constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed;
|
constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed;
|
||||||
EXCEPTION_RECORD record = {};
|
EXCEPTION_RECORD record = {};
|
||||||
record.ExceptionCode = kSimulatedExceptionCode;
|
record.ExceptionCode = kSimulatedExceptionCode;
|
||||||
#if defined(ARCH_CPU_64_BITS)
|
record.ExceptionAddress = ProgramCounterFromCONTEXT(&context);
|
||||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Rip);
|
|
||||||
#else
|
|
||||||
record.ExceptionAddress = reinterpret_cast<void*>(context.Eip);
|
|
||||||
#endif // ARCH_CPU_64_BITS
|
|
||||||
|
|
||||||
exception_pointers.ExceptionRecord = &record;
|
exception_pointers.ExceptionRecord = &record;
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
#include "base/macros.h"
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "test/test_paths.h"
|
#include "test/test_paths.h"
|
||||||
|
@ -141,15 +141,15 @@ struct CrashpadInfo {
|
|||||||
//!
|
//!
|
||||||
//! When handling an exception, the Crashpad handler will scan all modules in
|
//! 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 process. The first one that has a CrashpadInfo structure populated with
|
||||||
//! a value other than #kUnset for this field will dictate whether the handler
|
//! a value other than TriState::kUnset for this field will dictate whether
|
||||||
//! is functional or not. If all modules with a CrashpadInfo structure specify
|
//! the handler is functional or not. If all modules with a CrashpadInfo
|
||||||
//! #kUnset, the handler will be enabled. If disabled, the Crashpad handler
|
//! structure specify TriState::kUnset, the handler will be enabled. If
|
||||||
//! will still run and receive exceptions, but will not take any action on an
|
//! disabled, the Crashpad handler will still run and receive exceptions, but
|
||||||
//! exception on its own behalf, except for the action necessary to determine
|
//! will not take any action on an exception on its own behalf, except for the
|
||||||
//! that it has been disabled.
|
//! action necessary to determine that it has been disabled.
|
||||||
//!
|
//!
|
||||||
//! The Crashpad handler should not normally be disabled. More commonly, it
|
//! The Crashpad handler should not normally be disabled. More commonly, it is
|
||||||
//! is appropriate to disable crash report upload by calling
|
//! appropriate to disable crash report upload by calling
|
||||||
//! Settings::SetUploadsEnabled().
|
//! Settings::SetUploadsEnabled().
|
||||||
void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {
|
void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {
|
||||||
crashpad_handler_behavior_ = 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
|
//! 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 process. The first one that has a CrashpadInfo structure populated with
|
||||||
//! a value other than #kUnset for this field will dictate whether the
|
//! a value other than TriState::kUnset for this field will dictate whether
|
||||||
//! exception is forwarded to the system’s crash reporter. If all modules with
|
//! the exception is forwarded to the system’s crash reporter. If all modules
|
||||||
//! a CrashpadInfo structure specify #kUnset, forwarding will be enabled.
|
//! with a CrashpadInfo structure specify TriState::kUnset, forwarding will be
|
||||||
//! Unless disabled, forwarding may still occur if the Crashpad handler is
|
//! enabled. Unless disabled, forwarding may still occur if the Crashpad
|
||||||
//! disabled by SetCrashpadHandlerState(). Even when forwarding is enabled,
|
//! handler is disabled by SetCrashpadHandlerState(). Even when forwarding is
|
||||||
//! the Crashpad handler may choose not to forward all exceptions to the
|
//! enabled, the Crashpad handler may choose not to forward all exceptions to
|
||||||
//! system’s crash reporter in cases where it has reason to believe that the
|
//! the system’s crash reporter in cases where it has reason to believe that
|
||||||
//! system’s crash reporter would not normally have handled the exception in
|
//! the system’s crash reporter would not normally have handled the exception
|
||||||
//! Crashpad’s absence.
|
//! in Crashpad’s absence.
|
||||||
void set_system_crash_reporter_forwarding(
|
void set_system_crash_reporter_forwarding(
|
||||||
TriState system_crash_reporter_forwarding) {
|
TriState system_crash_reporter_forwarding) {
|
||||||
system_crash_reporter_forwarding_ = 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
|
//! 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 process. The first one that has a CrashpadInfo structure populated with
|
||||||
//! a value other than #kUnset for this field will dictate whether the extra
|
//! a value other than TriState::kUnset for this field will dictate whether
|
||||||
//! memory is captured.
|
//! the extra memory is captured.
|
||||||
//!
|
//!
|
||||||
//! This causes Crashpad to include pages of data referenced by locals or
|
//! 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
|
//! 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
|
//! Note that streams will appear in the minidump in the reverse order to
|
||||||
//! which they are added.
|
//! 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
|
//! \param[in] stream_type The stream type identifier to use. This should be
|
||||||
//! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream`
|
//! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream`
|
||||||
|
@ -26,16 +26,13 @@
|
|||||||
#define NOTE_ALIGN 4
|
#define NOTE_ALIGN 4
|
||||||
|
|
||||||
// This section must be "a"llocated so that it appears in the final binary at
|
// 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
|
// runtime. The reference to CRASHPAD_INFO_SYMBOL uses an offset relative to
|
||||||
// be performed.
|
// this note to avoid making this note writable, which triggers a bug in GNU
|
||||||
.section .note.crashpad.info,"aw",%note
|
// 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
|
.balign NOTE_ALIGN
|
||||||
# .globl indicates that it's available to link against other .o files. .hidden
|
CRASHPAD_NOTE:
|
||||||
# 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:
|
|
||||||
.long name_end - name // namesz
|
.long name_end - name // namesz
|
||||||
.long desc_end - desc // descsz
|
.long desc_end - desc // descsz
|
||||||
.long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type
|
.long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type
|
||||||
@ -45,15 +42,26 @@ name_end:
|
|||||||
.balign NOTE_ALIGN
|
.balign NOTE_ALIGN
|
||||||
desc:
|
desc:
|
||||||
#if defined(__LP64__)
|
#if defined(__LP64__)
|
||||||
.quad CRASHPAD_INFO_SYMBOL
|
.quad CRASHPAD_INFO_SYMBOL - desc
|
||||||
#else
|
#else
|
||||||
#if defined(__LITTLE_ENDIAN__)
|
.long CRASHPAD_INFO_SYMBOL - desc
|
||||||
.long CRASHPAD_INFO_SYMBOL
|
|
||||||
.long 0
|
|
||||||
#else
|
|
||||||
.long 0
|
|
||||||
.long CRASHPAD_INFO_SYMBOL
|
|
||||||
#endif // __LITTLE_ENDIAN__
|
|
||||||
#endif // __LP64__
|
#endif // __LP64__
|
||||||
desc_end:
|
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 "client/prune_crash_reports.h"
|
||||||
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -24,7 +25,7 @@
|
|||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
void PruneCrashReportDatabase(CrashReportDatabase* database,
|
size_t PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||||
PruneCondition* condition) {
|
PruneCondition* condition) {
|
||||||
std::vector<CrashReportDatabase::Report> all_reports;
|
std::vector<CrashReportDatabase::Report> all_reports;
|
||||||
CrashReportDatabase::OperationStatus status;
|
CrashReportDatabase::OperationStatus status;
|
||||||
@ -32,14 +33,14 @@ void PruneCrashReportDatabase(CrashReportDatabase* database,
|
|||||||
status = database->GetPendingReports(&all_reports);
|
status = database->GetPendingReports(&all_reports);
|
||||||
if (status != CrashReportDatabase::kNoError) {
|
if (status != CrashReportDatabase::kNoError) {
|
||||||
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get pending reports";
|
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get pending reports";
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CrashReportDatabase::Report> completed_reports;
|
std::vector<CrashReportDatabase::Report> completed_reports;
|
||||||
status = database->GetCompletedReports(&completed_reports);
|
status = database->GetCompletedReports(&completed_reports);
|
||||||
if (status != CrashReportDatabase::kNoError) {
|
if (status != CrashReportDatabase::kNoError) {
|
||||||
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get completed reports";
|
LOG(ERROR) << "PruneCrashReportDatabase: Failed to get completed reports";
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
all_reports.insert(all_reports.end(), completed_reports.begin(),
|
all_reports.insert(all_reports.end(), completed_reports.begin(),
|
||||||
completed_reports.end());
|
completed_reports.end());
|
||||||
@ -50,16 +51,21 @@ void PruneCrashReportDatabase(CrashReportDatabase* database,
|
|||||||
return lhs.creation_time > rhs.creation_time;
|
return lhs.creation_time > rhs.creation_time;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
size_t num_pruned = 0;
|
||||||
for (const auto& report : all_reports) {
|
for (const auto& report : all_reports) {
|
||||||
if (condition->ShouldPruneReport(report)) {
|
if (condition->ShouldPruneReport(report)) {
|
||||||
status = database->DeleteReport(report.uuid);
|
status = database->DeleteReport(report.uuid);
|
||||||
if (status != CrashReportDatabase::kNoError) {
|
if (status != CrashReportDatabase::kNoError) {
|
||||||
LOG(ERROR) << "Database Pruning: Failed to remove report "
|
LOG(ERROR) << "Database Pruning: Failed to remove report "
|
||||||
<< report.uuid.ToString();
|
<< report.uuid.ToString();
|
||||||
|
} else {
|
||||||
|
num_pruned++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return num_pruned;
|
||||||
|
|
||||||
// TODO(rsesek): For databases that do not use a directory structure, it is
|
// TODO(rsesek): For databases that do not use a directory structure, it is
|
||||||
// possible for the metadata sidecar to become corrupted and thus leave
|
// possible for the metadata sidecar to become corrupted and thus leave
|
||||||
// orphaned crash report files on-disk. https://crashpad.chromium.org/bug/66
|
// orphaned crash report files on-disk. https://crashpad.chromium.org/bug/66
|
||||||
@ -96,19 +102,9 @@ DatabaseSizePruneCondition::~DatabaseSizePruneCondition() {}
|
|||||||
|
|
||||||
bool DatabaseSizePruneCondition::ShouldPruneReport(
|
bool DatabaseSizePruneCondition::ShouldPruneReport(
|
||||||
const CrashReportDatabase::Report& report) {
|
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.
|
// Round up fractional KB to the next 1-KB boundary.
|
||||||
measured_size_in_kb_ +=
|
measured_size_in_kb_ +=
|
||||||
static_cast<size_t>((statbuf.st_size + 1023) / 1024);
|
static_cast<size_t>((report.total_size + 1023) / 1024);
|
||||||
}
|
|
||||||
return measured_size_in_kb_ > max_size_in_kb_;
|
return measured_size_in_kb_ > max_size_in_kb_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +37,9 @@ class PruneCondition;
|
|||||||
//! \param[in] database The database from which crash reports will be deleted.
|
//! \param[in] database The database from which crash reports will be deleted.
|
||||||
//! \param[in] condition The condition against which all reports in the database
|
//! \param[in] condition The condition against which all reports in the database
|
||||||
//! will be evaluated.
|
//! will be evaluated.
|
||||||
void PruneCrashReportDatabase(CrashReportDatabase* database,
|
//!
|
||||||
|
//! \return The number of deleted crash reports.
|
||||||
|
size_t PruneCrashReportDatabase(CrashReportDatabase* database,
|
||||||
PruneCondition* condition);
|
PruneCondition* condition);
|
||||||
|
|
||||||
std::unique_ptr<PruneCondition> GetDefaultDatabasePruneCondition();
|
std::unique_ptr<PruneCondition> GetDefaultDatabasePruneCondition();
|
||||||
|
@ -15,14 +15,15 @@
|
|||||||
#include "client/prune_crash_reports.h"
|
#include "client/prune_crash_reports.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <random>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "base/numerics/safe_conversions.h"
|
#include "base/numerics/safe_conversions.h"
|
||||||
#include "base/rand_util.h"
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "test/scoped_temp_dir.h"
|
#include "test/scoped_temp_dir.h"
|
||||||
@ -81,56 +82,49 @@ TEST(PruneCrashReports, AgeCondition) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(PruneCrashReports, SizeCondition) {
|
TEST(PruneCrashReports, SizeCondition) {
|
||||||
ScopedTempDir temp_dir;
|
|
||||||
|
|
||||||
CrashReportDatabase::Report report_1k;
|
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;
|
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(
|
DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1);
|
||||||
LoggingOpenFileForWrite(report_1k.file_path,
|
// |report_1k| should not be pruned as the cumulated size is not past 1kB
|
||||||
FileWriteMode::kCreateOrFail,
|
// yet.
|
||||||
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);
|
|
||||||
EXPECT_FALSE(condition.ShouldPruneReport(report_1k));
|
EXPECT_FALSE(condition.ShouldPruneReport(report_1k));
|
||||||
EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
|
// |report_3k| should be pruned as the cumulated size is now past 1kB.
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
DatabaseSizePruneCondition condition(1);
|
|
||||||
EXPECT_TRUE(condition.ShouldPruneReport(report_3k));
|
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));
|
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));
|
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));
|
EXPECT_TRUE(condition.ShouldPruneReport(report_1k));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -211,18 +205,16 @@ TEST(PruneCrashReports, PruneOrder) {
|
|||||||
using ::testing::Return;
|
using ::testing::Return;
|
||||||
using ::testing::SetArgPointee;
|
using ::testing::SetArgPointee;
|
||||||
|
|
||||||
|
const size_t kNumReports = 10;
|
||||||
std::vector<CrashReportDatabase::Report> reports;
|
std::vector<CrashReportDatabase::Report> reports;
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (size_t i = 0; i < kNumReports; ++i) {
|
||||||
CrashReportDatabase::Report temp;
|
CrashReportDatabase::Report temp;
|
||||||
temp.uuid.data_1 = i;
|
temp.uuid.data_1 = static_cast<uint32_t>(i);
|
||||||
temp.creation_time = NDaysAgo(i * 10);
|
temp.creation_time = NDaysAgo(static_cast<int>(i) * 10);
|
||||||
reports.push_back(temp);
|
reports.push_back(temp);
|
||||||
}
|
}
|
||||||
// The randomness from std::rand() is not, so use a better rand() instead.
|
std::mt19937 urng(std::random_device{}());
|
||||||
const auto random_generator = [](ptrdiff_t rand_max) {
|
std::shuffle(reports.begin(), reports.end(), urng);
|
||||||
return base::RandInt(0, base::checked_cast<int>(rand_max) - 1);
|
|
||||||
};
|
|
||||||
std::random_shuffle(reports.begin(), reports.end(), random_generator);
|
|
||||||
std::vector<CrashReportDatabase::Report> pending_reports(
|
std::vector<CrashReportDatabase::Report> pending_reports(
|
||||||
reports.begin(), reports.begin() + 5);
|
reports.begin(), reports.begin() + 5);
|
||||||
std::vector<CrashReportDatabase::Report> completed_reports(
|
std::vector<CrashReportDatabase::Report> completed_reports(
|
||||||
@ -241,7 +233,7 @@ TEST(PruneCrashReports, PruneOrder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StaticCondition delete_all(true);
|
StaticCondition delete_all(true);
|
||||||
PruneCrashReportDatabase(&db, &delete_all);
|
EXPECT_EQ(PruneCrashReportDatabase(&db, &delete_all), kNumReports);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -63,7 +63,8 @@ void Settings::ScopedLockedFileHandle::Destroy() {
|
|||||||
CheckedCloseFile(handle_);
|
CheckedCloseFile(handle_);
|
||||||
}
|
}
|
||||||
if (!lockfile_path_.empty()) {
|
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
|
#endif // OS_FUCHSIA
|
||||||
|
|
||||||
struct Settings::Data {
|
struct Settings::Data {
|
||||||
static const uint32_t kSettingsMagic = 'CPds';
|
static constexpr uint32_t kSettingsMagic = 'CPds';
|
||||||
static const uint32_t kSettingsVersion = 1;
|
static constexpr uint32_t kSettingsVersion = 1;
|
||||||
|
|
||||||
enum Options : uint32_t {
|
enum Options : uint32_t {
|
||||||
kUploadsEnabled = 1 << 0,
|
kUploadsEnabled = 1 << 0,
|
||||||
@ -225,10 +226,10 @@ Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting(
|
|||||||
FileHandle handle;
|
FileHandle handle;
|
||||||
if (log_open_error) {
|
if (log_open_error) {
|
||||||
handle = LoggingOpenFileForReadAndWrite(
|
handle = LoggingOpenFileForReadAndWrite(
|
||||||
file_path(), mode, FilePermissions::kWorldReadable);
|
file_path(), mode, FilePermissions::kOwnerOnly);
|
||||||
} else {
|
} else {
|
||||||
handle = OpenFileForReadAndWrite(
|
handle = OpenFileForReadAndWrite(
|
||||||
file_path(), mode, FilePermissions::kWorldReadable);
|
file_path(), mode, FilePermissions::kOwnerOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MakeScopedLockedFileHandle(
|
return MakeScopedLockedFileHandle(
|
||||||
|
@ -75,6 +75,10 @@ class Settings {
|
|||||||
//!
|
//!
|
||||||
//! The default value is `false`.
|
//! 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.
|
//! \param[out] enabled Whether crash reports should be uploaded.
|
||||||
//!
|
//!
|
||||||
//! \return On success, returns `true`, otherwise returns `false` with an
|
//! \return On success, returns `true`, otherwise returns `false` with an
|
||||||
|
@ -73,7 +73,7 @@ TEST(SimpleStringDictionary, SimpleStringDictionary) {
|
|||||||
EXPECT_FALSE(dict.GetValueForKey("key3"));
|
EXPECT_FALSE(dict.GetValueForKey("key3"));
|
||||||
|
|
||||||
// Remove by setting value to nullptr
|
// 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
|
// Now make sure it's not there anymore
|
||||||
EXPECT_FALSE(dict.GetValueForKey("key2"));
|
EXPECT_FALSE(dict.GetValueForKey("key2"));
|
||||||
@ -254,13 +254,14 @@ TEST(SimpleStringDictionary, OutOfSpace) {
|
|||||||
|
|
||||||
TEST(SimpleStringDictionaryDeathTest, SetKeyValueWithNullKey) {
|
TEST(SimpleStringDictionaryDeathTest, SetKeyValueWithNullKey) {
|
||||||
TSimpleStringDictionary<4, 6, 6> map;
|
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) {
|
TEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithNullKey) {
|
||||||
TSimpleStringDictionary<4, 6, 6> map;
|
TSimpleStringDictionary<4, 6, 6> map;
|
||||||
map.SetKeyValue("hi", "there");
|
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"));
|
EXPECT_STREQ("there", map.GetValueForKey("hi"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/mac/mach_logging.h"
|
#include "base/mac/mach_logging.h"
|
||||||
#include "base/mac/scoped_mach_port.h"
|
#include "base/mac/scoped_mach_port.h"
|
||||||
#include "base/macros.h"
|
#include "base/stl_util.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "util/mach/exc_client_variants.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());
|
base::mac::ScopedMachSendRight thread(mach_thread_self());
|
||||||
exception_type_t exception = kMachExceptionSimulated;
|
exception_type_t exception = kMachExceptionSimulated;
|
||||||
mach_exception_data_type_t codes[] = {0, 0};
|
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
|
// 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
|
// 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;
|
bool success = false;
|
||||||
|
|
||||||
for (size_t target_type_index = 0;
|
for (size_t target_type_index = 0;
|
||||||
!success && target_type_index < arraysize(kTargetTypes);
|
!success && target_type_index < base::size(kTargetTypes);
|
||||||
++target_type_index) {
|
++target_type_index) {
|
||||||
ExceptionPorts::ExceptionHandlerVector handlers;
|
ExceptionPorts::ExceptionHandlerVector handlers;
|
||||||
ExceptionPorts exception_ports(kTargetTypes[target_type_index],
|
ExceptionPorts exception_ports(kTargetTypes[target_type_index],
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "base/stl_util.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
@ -342,15 +343,13 @@ TEST(SimulateCrash, SimulateCrash) {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
for (size_t target_index = 0;
|
for (size_t target_index = 0; target_index < base::size(kTargets);
|
||||||
target_index < arraysize(kTargets);
|
|
||||||
++target_index) {
|
++target_index) {
|
||||||
TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
|
TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index];
|
||||||
SCOPED_TRACE(base::StringPrintf(
|
SCOPED_TRACE(base::StringPrintf(
|
||||||
"target_index %zu, target %d", target_index, target));
|
"target_index %zu, target %d", target_index, target));
|
||||||
|
|
||||||
for (size_t behavior_index = 0;
|
for (size_t behavior_index = 0; behavior_index < base::size(kBehaviors);
|
||||||
behavior_index < arraysize(kBehaviors);
|
|
||||||
++behavior_index) {
|
++behavior_index) {
|
||||||
exception_behavior_t behavior = kBehaviors[behavior_index];
|
exception_behavior_t behavior = kBehaviors[behavior_index];
|
||||||
SCOPED_TRACE(base::StringPrintf(
|
SCOPED_TRACE(base::StringPrintf(
|
||||||
@ -364,8 +363,7 @@ TEST(SimulateCrash, SimulateCrash) {
|
|||||||
target, behavior, THREAD_STATE_NONE);
|
target, behavior, THREAD_STATE_NONE);
|
||||||
test_simulate_crash_mac.Run();
|
test_simulate_crash_mac.Run();
|
||||||
} else {
|
} else {
|
||||||
for (size_t flavor_index = 0;
|
for (size_t flavor_index = 0; flavor_index < base::size(kFlavors);
|
||||||
flavor_index < arraysize(kFlavors);
|
|
||||||
++flavor_index) {
|
++flavor_index) {
|
||||||
thread_state_flavor_t flavor = kFlavors[flavor_index];
|
thread_state_flavor_t flavor = kFlavors[flavor_index];
|
||||||
SCOPED_TRACE(base::StringPrintf(
|
SCOPED_TRACE(base::StringPrintf(
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
GERRIT_HOST: True
|
GERRIT_HOST: True
|
||||||
GERRIT_SQUASH_UPLOADS: True
|
|
||||||
CODE_REVIEW_SERVER: https://chromium-review.googlesource.com/
|
CODE_REVIEW_SERVER: https://chromium-review.googlesource.com/
|
||||||
VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/
|
VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/
|
||||||
PROJECT: crashpad
|
PROJECT: crashpad
|
||||||
|
@ -17,8 +17,14 @@ import("../build/crashpad_buildconfig.gni")
|
|||||||
config("compat_config") {
|
config("compat_config") {
|
||||||
include_dirs = []
|
include_dirs = []
|
||||||
|
|
||||||
if (crashpad_is_mac) {
|
if (crashpad_is_mac || crashpad_is_ios) {
|
||||||
include_dirs += [ "mac" ]
|
include_dirs += [ "mac" ]
|
||||||
|
} else {
|
||||||
|
include_dirs += [ "non_mac" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crashpad_is_ios) {
|
||||||
|
include_dirs += [ "ios" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crashpad_is_linux || crashpad_is_android) {
|
if (crashpad_is_linux || crashpad_is_android) {
|
||||||
@ -34,10 +40,14 @@ config("compat_config") {
|
|||||||
} else {
|
} else {
|
||||||
include_dirs += [ "non_win" ]
|
include_dirs += [ "non_win" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!crashpad_is_linux && !crashpad_is_android && !crashpad_is_fuchsia) {
|
||||||
|
include_dirs += [ "non_elf" ]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template("compat_target") {
|
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
|
# There are no sources to compile, which doesn’t mix will with a
|
||||||
# static_library.
|
# static_library.
|
||||||
group(target_name) {
|
group(target_name) {
|
||||||
@ -53,21 +63,39 @@ template("compat_target") {
|
|||||||
compat_target("compat") {
|
compat_target("compat") {
|
||||||
sources = []
|
sources = []
|
||||||
|
|
||||||
if (crashpad_is_mac) {
|
if (crashpad_is_mac || crashpad_is_ios) {
|
||||||
sources += [
|
sources += [
|
||||||
"mac/AvailabilityMacros.h",
|
"mac/AvailabilityMacros.h",
|
||||||
"mac/kern/exc_resource.h",
|
"mac/kern/exc_resource.h",
|
||||||
"mac/mach-o/loader.h",
|
"mac/mach-o/loader.h",
|
||||||
|
"mac/mach/i386/thread_state.h",
|
||||||
"mac/mach/mach.h",
|
"mac/mach/mach.h",
|
||||||
"mac/sys/resource.h",
|
"mac/sys/resource.h",
|
||||||
]
|
]
|
||||||
} else {
|
} 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) {
|
if (crashpad_is_linux || crashpad_is_android) {
|
||||||
sources += [
|
sources += [
|
||||||
"linux/signal.h",
|
"linux/signal.h",
|
||||||
|
"linux/sys/mman.cc",
|
||||||
|
"linux/sys/mman.h",
|
||||||
"linux/sys/ptrace.h",
|
"linux/sys/ptrace.h",
|
||||||
"linux/sys/user.h",
|
"linux/sys/user.h",
|
||||||
]
|
]
|
||||||
@ -75,6 +103,8 @@ compat_target("compat") {
|
|||||||
|
|
||||||
if (crashpad_is_android) {
|
if (crashpad_is_android) {
|
||||||
sources += [
|
sources += [
|
||||||
|
"android/android/api-level.cc",
|
||||||
|
"android/android/api-level.h",
|
||||||
"android/dlfcn_internal.cc",
|
"android/dlfcn_internal.cc",
|
||||||
"android/dlfcn_internal.h",
|
"android/dlfcn_internal.h",
|
||||||
"android/elf.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 = [
|
public_configs = [
|
||||||
":compat_config",
|
":compat_config",
|
||||||
"..:crashpad_config",
|
"..:crashpad_config",
|
||||||
@ -121,7 +155,15 @@ compat_target("compat") {
|
|||||||
|
|
||||||
deps = []
|
deps = []
|
||||||
|
|
||||||
|
if (!crashpad_is_mac) {
|
||||||
|
deps += [ "../third_party/xnu" ]
|
||||||
|
}
|
||||||
|
|
||||||
if (crashpad_is_win) {
|
if (crashpad_is_win) {
|
||||||
deps += [ "../third_party/getopt" ]
|
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 {
|
namespace {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T Align(T value, uint8_t alignment) {
|
T Align(T value, size_t alignment) {
|
||||||
return (value + alignment - 1) & ~(alignment - 1);
|
return (value + alignment - 1) & ~(alignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
{
|
{
|
||||||
'target_name': 'crashpad_compat',
|
'target_name': 'crashpad_compat',
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'android/android/api-level.cc',
|
||||||
|
'android/android/api-level.h',
|
||||||
'android/dlfcn_internal.cc',
|
'android/dlfcn_internal.cc',
|
||||||
'android/dlfcn_internal.h',
|
'android/dlfcn_internal.h',
|
||||||
'android/elf.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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#ifndef CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
|
#ifndef CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
|
||||||
#define CRASHPAD_UTIL_MISC_ARRAYSIZE_UNSAFE_H_
|
#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…
|
#endif // CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_
|
||||||
//!
|
|
||||||
//! `#%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_
|
|
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 constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
|
||||||
static_cast<__ptrace_request>(25);
|
static_cast<__ptrace_request>(25);
|
||||||
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
|
#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 constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
|
||||||
static_cast<__ptrace_request>(22);
|
static_cast<__ptrace_request>(22);
|
||||||
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
|
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
|
||||||
|
@ -59,4 +59,16 @@
|
|||||||
#define MAC_OS_X_VERSION_10_13 101300
|
#define MAC_OS_X_VERSION_10_13 101300
|
||||||
#endif
|
#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_
|
#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`.
|
//! `cpuid 0x80000001` `edx`.
|
||||||
//!
|
//!
|
||||||
//! This field is only valid if #VendorId identifies the CPU vendor as
|
//! This field is only valid if #VendorId identifies the CPU vendor as
|
||||||
//! “AuthenticAMD”.
|
//! “AuthenticAMD” or "HygonGenuine".
|
||||||
uint32_t AMDExtendedCpuFeatures;
|
uint32_t AMDExtendedCpuFeatures;
|
||||||
} X86CpuInfo;
|
} X86CpuInfo;
|
||||||
|
|
||||||
|
@ -20,6 +20,4 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
typedef unsigned int pid_t;
|
|
||||||
|
|
||||||
#endif // CRASHPAD_COMPAT_WIN_SYS_TYPES_H_
|
#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
|
[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s
|
||||||
no need to install them separately.
|
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
|
### Android
|
||||||
|
|
||||||
Crashpad’s Android port is in its early stages. This build relies on
|
This build relies on cross-compilation. It’s possible to develop Crashpad for
|
||||||
cross-compilation. It’s possible to develop Crashpad for Android on any platform
|
Android on any platform that the [Android NDK (Native Development Kit)]
|
||||||
that the [Android NDK (Native Development
|
(https://developer.android.com/ndk/) runs on.
|
||||||
Kit)](https://developer.android.com/ndk/) runs on.
|
|
||||||
|
|
||||||
If it’s not already present on your system, [download the NDK package for your
|
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
|
system](https://developer.android.com/ndk/downloads/) and expand it to a
|
||||||
suitable location. These instructions assume that it’s been expanded to
|
suitable location. These instructions assume that it’s been expanded to
|
||||||
`~/android-ndk-r16`.
|
`~/android-ndk-r20`.
|
||||||
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that Chrome uses Android API level 21 for 64-bit platforms and 16 for
|
Note that Chrome uses Android API level 21 for 64-bit platforms and 16 for
|
||||||
32-bit platforms. See Chrome’s
|
32-bit platforms. See Chrome’s
|
||||||
[`build/config/android/config.gni`](https://chromium.googlesource.com/chromium/src/+/master/build/config/android/config.gni)
|
[`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`.
|
which sets `_android_api_level` and `_android64_api_level`.
|
||||||
|
|
||||||
To configure a Crashpad build for Android using the standalone toolchain
|
To configure a Crashpad build for Android use `gyp_crashpad_android.py`. This
|
||||||
assembled above, use `gyp_crashpad_android.py`. This script is a wrapper for
|
script is a wrapper for `gyp_crashpad.py` that sets several environment
|
||||||
`gyp_crashpad.py` that sets several environment variables directing the build to
|
variables directing the build to the toolchain, and several GYP options to
|
||||||
the standalone toolchain, and several GYP options to identify an Android build.
|
identify an Android build. This must be done after any `gclient sync`, or
|
||||||
This must be done after any `gclient sync`, or instead of any `gclient runhooks`
|
instead of any `gclient runhooks` operation.
|
||||||
operation.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
$ cd ~/crashpad/crashpad
|
$ cd ~/crashpad/crashpad
|
||||||
$ python build/gyp_crashpad_android.py \
|
python build/gyp_crashpad_android.py \
|
||||||
--ndk ~/android-ndk-r16_arm64_api21 \
|
--ndk ~/usr/lib/android-ndk-r20 --arch arm64 --api-level 21 \
|
||||||
--generator-output out/android_arm64_api21
|
--generator-output=out/android_arm64_api21 \
|
||||||
```
|
```
|
||||||
|
|
||||||
`gyp_crashpad_android.py` detects the build type based on the characteristics of
|
To build, direct `ninja` to the specific `out` directory chosen by the
|
||||||
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
|
|
||||||
`--generator-output` argument to `gyp_crashpad_android.py`.
|
`--generator-output` argument to `gyp_crashpad_android.py`.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ninja -C out/android_arm64_api21/out/Debug \
|
$ ninja -C out/android_arm64_api21/out/Debug all
|
||||||
crashpad_test_test crashpad_util_test
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
@ -193,6 +192,13 @@ $ cd ~/crashpad/crashpad
|
|||||||
$ python build/run_tests.py out/Debug
|
$ 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
|
### Windows
|
||||||
|
|
||||||
On Windows, `end_to_end_test.py` requires the CDB debugger, installed with
|
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
|
location on the detected or selected device, run them, and clean up after itself
|
||||||
when done.
|
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
|
## Contributing
|
||||||
|
|
||||||
Crashpad’s contribution process is very similar to [Chromium’s contribution
|
Crashpad’s contribution process is very similar to [Chromium’s contribution
|
||||||
@ -303,7 +328,7 @@ file.
|
|||||||
|
|
||||||
## Buildbot
|
## Buildbot
|
||||||
|
|
||||||
The [Crashpad Buildbot](https://build.chromium.org/p/client.crashpad/) performs
|
The [Crashpad Buildbot](https://ci.chromium.org/p/crashpad/g/main/console)
|
||||||
automated builds and tests of Crashpad. Before checking out or updating the
|
performs automated builds and tests of Crashpad. Before checking out or updating
|
||||||
Crashpad source code, and after checking in a new change, it is prudent to check
|
the Crashpad source code, and after checking in a new change, it is prudent to
|
||||||
the Buildbot to ensure that “the tree is green.”
|
check the Buildbot to ensure that “the tree is green.”
|
||||||
|
@ -240,7 +240,24 @@ of command line arguments in this mode.
|
|||||||
|
|
||||||
#### Linux/Android
|
#### 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
|
### Capturing Exceptions
|
||||||
|
|
||||||
@ -290,8 +307,16 @@ here.](https://crashpad.chromium.org/bug/133)
|
|||||||
|
|
||||||
#### Linux/Android
|
#### Linux/Android
|
||||||
|
|
||||||
TODO(mmentovai): describe this. See [this preliminary
|
On Linux, exceptions are dispatched as signals to the crashing thread. Crashpad
|
||||||
doc.](https://goto.google.com/crashpad-android-dd)
|
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
|
### The CrashpadInfo structure
|
||||||
|
|
||||||
|
@ -18,24 +18,27 @@ limitations under the License.
|
|||||||
|
|
||||||
## Completed
|
## Completed
|
||||||
|
|
||||||
Crashpad currently consists of a crash-reporting client and some related tools
|
Crashpad has complete crash-reporting clients and some related tools for macOS,
|
||||||
for macOS and Windows. The core client work for both platforms is substantially
|
Windows, Fuchsia, and Linux (including Android and Chromium OS). Crashpad became
|
||||||
complete. Crashpad became the crash reporter client for
|
the crash reporter client for [Chromium](https://www.chromium.org/Home) on macOS
|
||||||
[Chromium](https://www.chromium.org/Home) on macOS as of [March
|
as of [March
|
||||||
2015](https://chromium.googlesource.com/chromium/src/\+/d413b2dcb54d523811d386f1ff4084f677a6d089),
|
2015](https://chromium.googlesource.com/chromium/src/\+/d413b2dcb54d523811d386f1ff4084f677a6d089),
|
||||||
and on Windows as of [November
|
Windows as of [November
|
||||||
2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c).
|
2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c),
|
||||||
|
and Android as of [January
|
||||||
|
2019](https://chromium.googlesource.com/chromium/src/+/f890e4b5495ab693d2d37aec3c378239946154f7).
|
||||||
|
|
||||||
|
|
||||||
## In Progress
|
## In Progress
|
||||||
|
|
||||||
Initial work on a Crashpad client for
|
Chromium is transitioning to Crashpad for [Chromium OS and Desktop
|
||||||
[Android](https://crashpad.chromium.org/bug/30) has begun. This is currently in
|
Linux](https://crbug.com/942279).
|
||||||
the early implementation phase.
|
|
||||||
|
Work has begun on a Crashpad client for
|
||||||
|
[iOS](https://crashpad.chromium.org/bug/31).
|
||||||
|
|
||||||
## Future
|
## Future
|
||||||
|
|
||||||
There are plans to bring Crashpad clients to other operating systems in the
|
There are also plans to implement a [crash report
|
||||||
future, including a more generic non-Android Linux implementation. There are
|
|
||||||
also plans to implement a [crash report
|
|
||||||
processor](https://crashpad.chromium.org/bug/29) as part of Crashpad. No
|
processor](https://crashpad.chromium.org/bug/29) as part of Crashpad. No
|
||||||
timeline for completing this work has been set yet.
|
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
|
# This file describes the settings to be used by the documentation system
|
||||||
# doxygen (www.doxygen.org) for a project.
|
# doxygen (www.doxygen.org) for a project.
|
||||||
@ -17,10 +17,10 @@
|
|||||||
# Project related configuration options
|
# Project related configuration options
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
|
|
||||||
# This tag specifies the encoding used for all characters in the config file
|
# This tag specifies the encoding used for all characters in the configuration
|
||||||
# that follow. The default is UTF-8 which is also the encoding used for all text
|
# file that follow. The default is UTF-8 which is also the encoding used for all
|
||||||
# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
|
# text before the first occurrence of this tag. Doxygen uses libiconv (or the
|
||||||
# built into libc) for the transcoding. See
|
# iconv built into libc) for the transcoding. See
|
||||||
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
|
# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
|
||||||
# The default value is: UTF-8.
|
# The default value is: UTF-8.
|
||||||
|
|
||||||
@ -93,6 +93,14 @@ ALLOW_UNICODE_NAMES = NO
|
|||||||
|
|
||||||
OUTPUT_LANGUAGE = English
|
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
|
# 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
|
# descriptions after the members that are listed in the file and class
|
||||||
# documentation (similar to Javadoc). Set to NO to disable this.
|
# documentation (similar to Javadoc). Set to NO to disable this.
|
||||||
@ -181,6 +189,16 @@ SHORT_NAMES = NO
|
|||||||
|
|
||||||
JAVADOC_AUTOBRIEF = 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
|
# 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
|
# 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
|
# 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
|
# "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
|
# 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.
|
# 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 =
|
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
|
# 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
|
# 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
|
# 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
|
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
|
# 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
|
# 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
|
# 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
|
# 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,
|
# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
|
||||||
# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
|
# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
|
||||||
# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
|
# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
|
||||||
# Fortran. In the later case the parser tries to guess whether the code is fixed
|
# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
|
||||||
# or free formatted code, this is the default for Fortran type files), VHDL. For
|
# tries to guess whether the code is fixed or free formatted code, this is the
|
||||||
# instance to make doxygen treat .inc files as Fortran files (default is PHP),
|
# default for Fortran type files). For instance to make doxygen treat .inc files
|
||||||
# and .f files as C (default is Fortran), use: inc=Fortran f=C.
|
# 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.
|
# 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
|
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
|
||||||
# according to the Markdown format, which allows for more readable
|
# 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
|
# 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
|
# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
|
||||||
# case of backward compatibilities issues.
|
# 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
|
# to that level are automatically included in the table of contents, even if
|
||||||
# they do not have an id attribute.
|
# they do not have an id attribute.
|
||||||
# Note: This feature currently applies only to Markdown headings.
|
# 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.
|
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||||
|
|
||||||
TOC_INCLUDE_HEADINGS = 0
|
TOC_INCLUDE_HEADINGS = 0
|
||||||
@ -436,6 +461,12 @@ EXTRACT_ALL = NO
|
|||||||
|
|
||||||
EXTRACT_PRIVATE = 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
|
# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
|
||||||
# scope will be included in the documentation.
|
# scope will be included in the documentation.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
@ -490,8 +521,8 @@ HIDE_UNDOC_MEMBERS = NO
|
|||||||
HIDE_UNDOC_CLASSES = NO
|
HIDE_UNDOC_CLASSES = NO
|
||||||
|
|
||||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
# 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
|
# declarations. If set to NO, these declarations will be included in the
|
||||||
# included in the documentation.
|
# documentation.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
|
|
||||||
HIDE_FRIEND_COMPOUNDS = 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
|
# 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
|
# 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
|
# 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.
|
# The default value is: system dependent.
|
||||||
|
|
||||||
CASE_SENSE_NAMES = YES
|
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
|
# 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
|
# are documented, but have no documentation for their parameters or return
|
||||||
# value. If set to NO, doxygen will only warn about wrong or incomplete
|
# 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.
|
# The default value is: NO.
|
||||||
|
|
||||||
WARN_NO_PARAMDOC = 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,
|
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||||
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
|
||||||
# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
|
||||||
# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf.
|
# *.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 \
|
FILE_PATTERNS = *.c \
|
||||||
*.cc \
|
*.cc \
|
||||||
@ -968,7 +1002,7 @@ INLINE_SOURCES = NO
|
|||||||
STRIP_CODE_COMMENTS = YES
|
STRIP_CODE_COMMENTS = YES
|
||||||
|
|
||||||
# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
|
# 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.
|
# The default value is: NO.
|
||||||
|
|
||||||
REFERENCED_BY_RELATION = NO
|
REFERENCED_BY_RELATION = NO
|
||||||
@ -1005,7 +1039,7 @@ SOURCE_TOOLTIPS = YES
|
|||||||
#
|
#
|
||||||
# To use it do the following:
|
# To use it do the following:
|
||||||
# - Install the latest version of global
|
# - 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
|
# - Make sure the INPUT points to the root of the source tree
|
||||||
# - Run doxygen as normal
|
# - 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
|
# 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
|
# 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
|
# 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.
|
# like the Qt help browser.
|
||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to 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
|
# 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
|
# generated that can be used as input for Apple's Xcode 3 integrated development
|
||||||
# environment (see: https://developer.apple.com/tools/xcode/), introduced with
|
# environment (see: https://developer.apple.com/xcode/), introduced with OSX
|
||||||
# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
|
# 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
|
# Makefile in the HTML output directory. Running make will produce the docset in
|
||||||
# that directory and running make install will install 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
|
# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
|
||||||
# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html
|
# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
|
||||||
# for more information.
|
# genXcode/_index.html for more information.
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# 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
|
# 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
|
# 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
|
# 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.
|
# Windows.
|
||||||
#
|
#
|
||||||
# The HTML Help Workshop contains a compiler that can convert all HTML output
|
# 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
|
# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
|
||||||
# Project output. For more information please see Qt Help Project / Namespace
|
# 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.
|
# The default value is: org.doxygen.Project.
|
||||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
# 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
|
# 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
|
# 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.
|
# The default value is: doc.
|
||||||
# This tag requires that the tag GENERATE_QHP is set to YES.
|
# 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
|
# 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
|
# 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.
|
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||||
|
|
||||||
QHP_CUST_FILTER_NAME =
|
QHP_CUST_FILTER_NAME =
|
||||||
|
|
||||||
# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
|
# 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
|
# 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.
|
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||||
|
|
||||||
QHP_CUST_FILTER_ATTRS =
|
QHP_CUST_FILTER_ATTRS =
|
||||||
|
|
||||||
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
|
||||||
# project's filter section matches. Qt Help Project / Filter Attributes (see:
|
# 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.
|
# This tag requires that the tag GENERATE_QHP is set to YES.
|
||||||
|
|
||||||
QHP_SECT_FILTER_ATTRS =
|
QHP_SECT_FILTER_ATTRS =
|
||||||
@ -1450,6 +1487,17 @@ TREEVIEW_WIDTH = 250
|
|||||||
|
|
||||||
EXT_LINKS_IN_WINDOW = NO
|
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
|
# 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
|
# 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
|
# doxygen run you need to manually remove any form_*.png images from the HTML
|
||||||
@ -1470,8 +1518,14 @@ FORMULA_FONTSIZE = 10
|
|||||||
|
|
||||||
FORMULA_TRANSPARENT = YES
|
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
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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. However, it is strongly recommended to install a local copy of
|
||||||
# MathJax from https://www.mathjax.org before deployment.
|
# 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.
|
# This tag requires that the tag USE_MATHJAX is set to YES.
|
||||||
|
|
||||||
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
|
||||||
@ -1541,7 +1595,7 @@ MATHJAX_CODEFILE =
|
|||||||
SEARCHENGINE = YES
|
SEARCHENGINE = YES
|
||||||
|
|
||||||
# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
|
# 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
|
# 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
|
# 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
|
# 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
|
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||||
# invoked.
|
# invoked.
|
||||||
#
|
#
|
||||||
# Note that when enabling USE_PDFLATEX this option is only used for generating
|
# Note that when not enabling USE_PDFLATEX the default is latex when enabling
|
||||||
# bitmaps for formulas in the HTML output, but not in the Makefile that is
|
# USE_PDFLATEX the default is pdflatex and when in the later case latex is
|
||||||
# written to the output directory.
|
# chosen this is overwritten by pdflatex. For specific output languages the
|
||||||
# The default file is: latex.
|
# 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.
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
LATEX_CMD_NAME = latex
|
LATEX_CMD_NAME = latex
|
||||||
|
|
||||||
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
|
# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
|
||||||
# index for LaTeX.
|
# 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.
|
# The default file is: makeindex.
|
||||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||||
|
|
||||||
MAKEINDEX_CMD_NAME = makeindex
|
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
|
# 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
|
# documents. This may be useful for small projects and may help to save some
|
||||||
# trees in general.
|
# trees in general.
|
||||||
@ -1774,6 +1842,14 @@ LATEX_BIB_STYLE = plain
|
|||||||
|
|
||||||
LATEX_TIMESTAMP = NO
|
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
|
# Configuration options related to the RTF output
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@ -1813,9 +1889,9 @@ COMPACT_RTF = NO
|
|||||||
|
|
||||||
RTF_HYPERLINKS = NO
|
RTF_HYPERLINKS = NO
|
||||||
|
|
||||||
# Load stylesheet definitions from file. Syntax is similar to doxygen's config
|
# Load stylesheet definitions from file. Syntax is similar to doxygen's
|
||||||
# file, i.e. a series of assignments. You only have to provide replacements,
|
# configuration file, i.e. a series of assignments. You only have to provide
|
||||||
# missing definitions are set to their default value.
|
# replacements, missing definitions are set to their default value.
|
||||||
#
|
#
|
||||||
# See also section "Doxygen usage" for information on how to generate the
|
# See also section "Doxygen usage" for information on how to generate the
|
||||||
# default style sheet that doxygen normally uses.
|
# default style sheet that doxygen normally uses.
|
||||||
@ -1824,8 +1900,8 @@ RTF_HYPERLINKS = NO
|
|||||||
RTF_STYLESHEET_FILE =
|
RTF_STYLESHEET_FILE =
|
||||||
|
|
||||||
# Set optional variables used in the generation of an RTF document. Syntax is
|
# 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
|
# similar to doxygen's configuration file. A template extensions file can be
|
||||||
# using doxygen -e rtf extensionFile.
|
# generated using doxygen -e rtf extensionFile.
|
||||||
# This tag requires that the tag GENERATE_RTF is set to YES.
|
# This tag requires that the tag GENERATE_RTF is set to YES.
|
||||||
|
|
||||||
RTF_EXTENSIONS_FILE =
|
RTF_EXTENSIONS_FILE =
|
||||||
@ -1911,6 +1987,13 @@ XML_OUTPUT = xml
|
|||||||
|
|
||||||
XML_PROGRAMLISTING = YES
|
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
|
# Configuration options related to the DOCBOOK output
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@ -2116,12 +2199,6 @@ EXTERNAL_GROUPS = YES
|
|||||||
|
|
||||||
EXTERNAL_PAGES = 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
|
# Configuration options related to the dot tool
|
||||||
#---------------------------------------------------------------------------
|
#---------------------------------------------------------------------------
|
||||||
@ -2135,15 +2212,6 @@ PERL_PATH = /usr/bin/perl
|
|||||||
|
|
||||||
CLASS_DIAGRAMS = YES
|
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
|
# 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
|
# 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.
|
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
# Copyright 2017 The Crashpad Authors. All rights reserved.
|
# Copyright 2017 The Crashpad Authors. All rights reserved.
|
||||||
#
|
#
|
||||||
|
155
handler/BUILD.gn
155
handler/BUILD.gn
@ -42,6 +42,8 @@ static_library("handler") {
|
|||||||
if (crashpad_is_linux || crashpad_is_android) {
|
if (crashpad_is_linux || crashpad_is_android) {
|
||||||
set_sources_assignment_filter([])
|
set_sources_assignment_filter([])
|
||||||
sources += [
|
sources += [
|
||||||
|
"linux/capture_snapshot.cc",
|
||||||
|
"linux/capture_snapshot.h",
|
||||||
"linux/crash_report_exception_handler.cc",
|
"linux/crash_report_exception_handler.cc",
|
||||||
"linux/crash_report_exception_handler.h",
|
"linux/crash_report_exception_handler.h",
|
||||||
"linux/exception_handler_server.cc",
|
"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) {
|
if (crashpad_is_win) {
|
||||||
sources += [
|
sources += [
|
||||||
"win/crash_report_exception_handler.cc",
|
"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" ]
|
public_configs = [ "..:crashpad_config" ]
|
||||||
|
|
||||||
deps = [
|
public_deps = [
|
||||||
"../client",
|
"../client",
|
||||||
"../compat",
|
"../third_party/mini_chromium:base",
|
||||||
|
"../util",
|
||||||
|
]
|
||||||
|
|
||||||
|
deps = [
|
||||||
"../minidump",
|
"../minidump",
|
||||||
"../snapshot",
|
"../snapshot",
|
||||||
"../third_party/mini_chromium:base",
|
|
||||||
"../tools:tool_support",
|
"../tools:tool_support",
|
||||||
"../util",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
if (crashpad_is_win) {
|
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") {
|
source_set("handler_test") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "minidump_to_upload_parameters_test.cc" ]
|
||||||
"minidump_to_upload_parameters_test.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
if (crashpad_is_linux || crashpad_is_android) {
|
if (crashpad_is_linux || crashpad_is_android) {
|
||||||
sources += [ "linux/exception_handler_server_test.cc" ]
|
sources += [ "linux/exception_handler_server_test.cc" ]
|
||||||
@ -110,6 +120,8 @@ source_set("handler_test") {
|
|||||||
]
|
]
|
||||||
|
|
||||||
if (crashpad_is_win) {
|
if (crashpad_is_win) {
|
||||||
|
deps += [ "../minidump:test_support" ]
|
||||||
|
|
||||||
data_deps = [
|
data_deps = [
|
||||||
":crashpad_handler_test_extended_handler",
|
":crashpad_handler_test_extended_handler",
|
||||||
":fake_handler_that_crashes_at_startup",
|
":fake_handler_that_crashes_at_startup",
|
||||||
@ -117,38 +129,20 @@ source_set("handler_test") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crashpad_executable("crashpad_handler") {
|
if (!crashpad_is_ios) {
|
||||||
sources = [
|
crashpad_executable("crashpad_handler") {
|
||||||
"main.cc",
|
sources = [ "main.cc" ]
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
":handler",
|
":handler",
|
||||||
"../build:default_exe_manifest_win",
|
"../build:default_exe_manifest_win",
|
||||||
"../compat",
|
"../compat",
|
||||||
"../third_party/mini_chromium:base",
|
"../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_win) {
|
||||||
if (crashpad_is_in_chromium) {
|
if (crashpad_is_in_chromium || crashpad_is_in_dart) {
|
||||||
remove_configs = [ "//build/config/win:console" ]
|
remove_configs = [ "//build/config/win:console" ]
|
||||||
configs = [ "//build/config/win:windowed" ]
|
configs = [ "//build/config/win:windowed" ]
|
||||||
} else {
|
} else {
|
||||||
@ -158,6 +152,7 @@ crashpad_executable("crashpad_handler") {
|
|||||||
[ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
|
[ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# There is not any normal way to package native executables in an Android APK.
|
# There is not any normal way to package native executables in an Android APK.
|
||||||
@ -166,26 +161,32 @@ crashpad_executable("crashpad_handler") {
|
|||||||
# handler executable an acceptable name.
|
# handler executable an acceptable name.
|
||||||
if (crashpad_is_android) {
|
if (crashpad_is_android) {
|
||||||
copy("crashpad_handler_named_as_so") {
|
copy("crashpad_handler_named_as_so") {
|
||||||
deps = [
|
deps = [ ":crashpad_handler" ]
|
||||||
":crashpad_handler",
|
|
||||||
]
|
|
||||||
|
|
||||||
sources = [
|
sources = [ "$root_out_dir/crashpad_handler" ]
|
||||||
"$root_out_dir/crashpad_handler",
|
|
||||||
]
|
|
||||||
|
|
||||||
outputs = [
|
outputs = [ "$root_out_dir/libcrashpad_handler.so" ]
|
||||||
"$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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
crashpad_executable("crashpad_handler_test_extended_handler") {
|
if (!crashpad_is_ios) {
|
||||||
|
crashpad_executable("crashpad_handler_test_extended_handler") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "crashpad_handler_test_extended_handler.cc" ]
|
||||||
"crashpad_handler_test_extended_handler.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
":handler",
|
":handler",
|
||||||
@ -195,13 +196,12 @@ crashpad_executable("crashpad_handler_test_extended_handler") {
|
|||||||
"../third_party/mini_chromium:base",
|
"../third_party/mini_chromium:base",
|
||||||
"../tools:tool_support",
|
"../tools:tool_support",
|
||||||
]
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (crashpad_is_win) {
|
if (crashpad_is_win) {
|
||||||
crashpad_executable("crashpad_handler_com") {
|
crashpad_executable("crashpad_handler_com") {
|
||||||
sources = [
|
sources = [ "main.cc" ]
|
||||||
"main.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
# Avoid .exp, .ilk, and .lib file collisions with crashpad_handler.exe by
|
# 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
|
# 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",
|
"../build:default_exe_manifest_win",
|
||||||
"../compat",
|
"../compat",
|
||||||
"../third_party/mini_chromium:base",
|
"../third_party/mini_chromium:base",
|
||||||
|
"../tools:tool_support",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
copy("crashpad_handler_console") {
|
copy("crashpad_handler_console") {
|
||||||
deps = [
|
deps = [ ":crashpad_handler_com" ]
|
||||||
":crashpad_handler_com",
|
sources = [ "$root_out_dir/crashpad_handler_com.com" ]
|
||||||
]
|
outputs = [ "$root_out_dir/crashpad_handler.com" ]
|
||||||
sources = [
|
|
||||||
"$root_out_dir/crashpad_handler_com.com",
|
|
||||||
]
|
|
||||||
outputs = [
|
|
||||||
"$root_out_dir/crashpad_handler.com",
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crashpad_executable("crash_other_program") {
|
crashpad_executable("crash_other_program") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/crash_other_program.cc" ]
|
||||||
"win/crash_other_program.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"../client",
|
"../client",
|
||||||
@ -246,9 +239,7 @@ if (crashpad_is_win) {
|
|||||||
crashpad_executable("crashy_program") {
|
crashpad_executable("crashy_program") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/crashy_test_program.cc" ]
|
||||||
"win/crashy_test_program.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"../client",
|
"../client",
|
||||||
@ -259,9 +250,7 @@ if (crashpad_is_win) {
|
|||||||
crashpad_executable("crashy_signal") {
|
crashpad_executable("crashy_signal") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/crashy_signal.cc" ]
|
||||||
"win/crashy_signal.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
cflags = [ "/wd4702" ] # Unreachable code.
|
cflags = [ "/wd4702" ] # Unreachable code.
|
||||||
|
|
||||||
@ -274,17 +263,13 @@ if (crashpad_is_win) {
|
|||||||
crashpad_executable("fake_handler_that_crashes_at_startup") {
|
crashpad_executable("fake_handler_that_crashes_at_startup") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/fake_handler_that_crashes_at_startup.cc" ]
|
||||||
"win/fake_handler_that_crashes_at_startup.cc",
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crashpad_executable("hanging_program") {
|
crashpad_executable("hanging_program") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/hanging_program.cc" ]
|
||||||
"win/hanging_program.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"../client",
|
"../client",
|
||||||
@ -295,17 +280,13 @@ if (crashpad_is_win) {
|
|||||||
crashpad_loadable_module("loader_lock_dll") {
|
crashpad_loadable_module("loader_lock_dll") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/loader_lock_dll.cc" ]
|
||||||
"win/loader_lock_dll.cc",
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
crashpad_executable("self_destroying_program") {
|
crashpad_executable("self_destroying_program") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/self_destroying_test_program.cc" ]
|
||||||
"win/self_destroying_test_program.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"../client",
|
"../client",
|
||||||
@ -320,9 +301,7 @@ if (crashpad_is_win) {
|
|||||||
crashpad_executable("crashy_z7_loader") {
|
crashpad_executable("crashy_z7_loader") {
|
||||||
testonly = true
|
testonly = true
|
||||||
|
|
||||||
sources = [
|
sources = [ "win/crashy_test_z7_loader.cc" ]
|
||||||
"win/crashy_test_z7_loader.cc",
|
|
||||||
]
|
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
"../client",
|
"../client",
|
||||||
|
@ -246,12 +246,6 @@ void CrashReportUploadThread::ProcessPendingReport(
|
|||||||
CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
|
CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
|
||||||
const CrashReportDatabase::UploadReport* report,
|
const CrashReportDatabase::UploadReport* report,
|
||||||
std::string* response_body) {
|
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;
|
std::map<std::string, std::string> parameters;
|
||||||
|
|
||||||
FileReader* reader = report->Reader();
|
FileReader* reader = report->Reader();
|
||||||
@ -338,7 +332,6 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return UploadResult::kSuccess;
|
return UploadResult::kSuccess;
|
||||||
#endif // OS_ANDROID
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReportUploadThread::DoWork(const WorkerThread* thread) {
|
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
|
Either this option or **--mach-service**, but not both, is required. This
|
||||||
option is only valid on macOS.
|
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*
|
* **--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.
|
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
|
client to register, and exits when all clients have exited, after waiting for
|
||||||
any uploads in progress to complete.
|
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_
|
* **--mach-service**=_SERVICE_
|
||||||
|
|
||||||
Check in with the bootstrap server under the name _SERVICE_. Either this
|
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
|
To prevent excessive accumulation of handler processes, _ARGUMENT_ must not
|
||||||
be `--monitor-self`.
|
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**
|
* **--no-periodic-tasks**
|
||||||
|
|
||||||
Do not scan for new pending crash reports or prune the crash report database.
|
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
|
for use with collection servers that don’t accept uploads compressed in this
|
||||||
way.
|
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_
|
* **--pipe-name**=_PIPE_
|
||||||
|
|
||||||
Listen on the given pipe name for connections from clients. _PIPE_ must be of
|
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
|
parent process. This option is only valid on macOS. Use of this option is
|
||||||
discouraged. It should not be used absent extraordinary circumstances.
|
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_
|
* **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_
|
||||||
|
|
||||||
Causes the handler process to trace its parent process and exit. The parent
|
Causes the handler process to trace its parent process and exit. The parent
|
||||||
process should have an ExceptionInformation struct at
|
process should have an ExceptionInformation struct at
|
||||||
_EXCEPTION-INFORMATION-ADDRESS_.
|
_EXCEPTION-INFORMATION-ADDRESS_. This option is only valid on Linux
|
||||||
|
platforms.
|
||||||
* **--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.
|
|
||||||
|
|
||||||
* **--url**=_URL_
|
* **--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
|
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.
|
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**
|
* **--help**
|
||||||
|
|
||||||
Display help and exit.
|
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");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with 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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
#include "util/net/http_transport.h"
|
#include "handler/handler_main.h"
|
||||||
|
|
||||||
#include "base/logging.h"
|
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
std::unique_ptr<HTTPTransport> HTTPTransport::Create() {
|
extern "C" {
|
||||||
NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196
|
|
||||||
return std::unique_ptr<HTTPTransport>();
|
__attribute__((visibility("default"), used)) int CrashpadHandlerMain(
|
||||||
|
int argc,
|
||||||
|
char* argv[]) {
|
||||||
|
return HandlerMain(argc, argv, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
} // namespace crashpad
|
} // 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',
|
'../minidump/minidump.gyp:crashpad_minidump',
|
||||||
'../snapshot/snapshot.gyp:crashpad_snapshot',
|
'../snapshot/snapshot.gyp:crashpad_snapshot',
|
||||||
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
'../third_party/mini_chromium/mini_chromium.gyp:base',
|
||||||
|
'../third_party/zlib/zlib.gyp:zlib',
|
||||||
'../tools/tools.gyp:crashpad_tool_support',
|
'../tools/tools.gyp:crashpad_tool_support',
|
||||||
'../util/util.gyp:crashpad_util',
|
'../util/util.gyp:crashpad_util',
|
||||||
],
|
],
|
||||||
@ -39,6 +40,8 @@
|
|||||||
'crash_report_upload_thread.h',
|
'crash_report_upload_thread.h',
|
||||||
'handler_main.cc',
|
'handler_main.cc',
|
||||||
'handler_main.h',
|
'handler_main.h',
|
||||||
|
'linux/capture_snapshot.cc',
|
||||||
|
'linux/capture_snapshot.h',
|
||||||
'linux/crash_report_exception_handler.cc',
|
'linux/crash_report_exception_handler.cc',
|
||||||
'linux/crash_report_exception_handler.h',
|
'linux/crash_report_exception_handler.h',
|
||||||
'linux/exception_handler_server.cc',
|
'linux/exception_handler_server.cc',
|
||||||
|
@ -56,6 +56,10 @@
|
|||||||
#include "util/string/split_string.h"
|
#include "util/string/split_string.h"
|
||||||
#include "util/synchronization/semaphore.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)
|
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -70,8 +74,8 @@
|
|||||||
#include "handler/mac/crash_report_exception_handler.h"
|
#include "handler/mac/crash_report_exception_handler.h"
|
||||||
#include "handler/mac/exception_handler_server.h"
|
#include "handler/mac/exception_handler_server.h"
|
||||||
#include "handler/mac/file_limit_annotation.h"
|
#include "handler/mac/file_limit_annotation.h"
|
||||||
|
#include "util/mach/bootstrap.h"
|
||||||
#include "util/mach/child_port_handshake.h"
|
#include "util/mach/child_port_handshake.h"
|
||||||
#include "util/mach/mach_extensions.h"
|
|
||||||
#include "util/posix/close_stdio.h"
|
#include "util/posix/close_stdio.h"
|
||||||
#include "util/posix/signals.h"
|
#include "util/posix/signals.h"
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
@ -82,12 +86,6 @@
|
|||||||
#include "util/win/handle.h"
|
#include "util/win/handle.h"
|
||||||
#include "util/win/initial_client_data.h"
|
#include "util/win/initial_client_data.h"
|
||||||
#include "util/win/session_end_watcher.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)
|
#elif defined(OS_LINUX)
|
||||||
#include "handler/linux/crash_report_exception_handler.h"
|
#include "handler/linux/crash_report_exception_handler.h"
|
||||||
#include "handler/linux/exception_handler_server.h"
|
#include "handler/linux/exception_handler_server.h"
|
||||||
@ -118,6 +116,9 @@ void Usage(const base::FilePath& me) {
|
|||||||
" Address_debug_critical_section\n"
|
" Address_debug_critical_section\n"
|
||||||
" use precreated data to register initial client\n"
|
" use precreated data to register initial client\n"
|
||||||
#endif // OS_WIN
|
#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)
|
#if defined(OS_MACOSX)
|
||||||
" --mach-service=SERVICE register SERVICE with the bootstrap server\n"
|
" --mach-service=SERVICE register SERVICE with the bootstrap server\n"
|
||||||
#endif // OS_MACOSX
|
#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-periodic-tasks don't scan for new reports or prune the database\n"
|
||||||
" --no-rate-limit don't rate limit crash uploads\n"
|
" --no-rate-limit don't rate limit crash uploads\n"
|
||||||
" --no-upload-gzip don't use gzip compression when uploading\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)
|
#if defined(OS_WIN)
|
||||||
" --pipe-name=PIPE communicate with the client over PIPE\n"
|
" --pipe-name=PIPE communicate with the client over PIPE\n"
|
||||||
#endif // OS_WIN
|
#endif // OS_WIN
|
||||||
@ -141,14 +146,31 @@ void Usage(const base::FilePath& me) {
|
|||||||
" reset the server's exception handler to default\n"
|
" reset the server's exception handler to default\n"
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
#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"
|
" --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n"
|
||||||
" request a dump for the handler's parent process\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
|
#endif // OS_LINUX || OS_ANDROID
|
||||||
" --url=URL send crash reports to this Breakpad server URL,\n"
|
" --url=URL send crash reports to this Breakpad server URL,\n"
|
||||||
" only if uploads are enabled for the database\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"
|
" --help display this help and exit\n"
|
||||||
" --version output version information and exit\n",
|
" --version output version information and exit\n",
|
||||||
me.value().c_str());
|
me.value().c_str());
|
||||||
@ -168,8 +190,13 @@ struct Options {
|
|||||||
bool reset_own_crash_exception_port_to_system_default;
|
bool reset_own_crash_exception_port_to_system_default;
|
||||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
VMAddress exception_information_address;
|
VMAddress exception_information_address;
|
||||||
int initial_client_fd;
|
|
||||||
VMAddress sanitization_information_address;
|
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)
|
#elif defined(OS_WIN)
|
||||||
std::string pipe_name;
|
std::string pipe_name;
|
||||||
InitialClientData initial_client_data;
|
InitialClientData initial_client_data;
|
||||||
@ -179,6 +206,11 @@ struct Options {
|
|||||||
bool periodic_tasks;
|
bool periodic_tasks;
|
||||||
bool rate_limit;
|
bool rate_limit;
|
||||||
bool upload_gzip;
|
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|.
|
// 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)
|
#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) {
|
void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
||||||
MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
|
MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed);
|
||||||
|
|
||||||
@ -274,9 +304,7 @@ void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) {
|
|||||||
}
|
}
|
||||||
Metrics::HandlerCrashed(metrics_code);
|
Metrics::HandlerCrashed(metrics_code);
|
||||||
|
|
||||||
struct sigaction* old_action =
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||||
g_old_crash_signal_handlers.ActionForSignal(sig);
|
|
||||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) {
|
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);
|
Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
|
||||||
|
|
||||||
void ReinstallCrashHandler() {
|
void ReinstallCrashHandler() {
|
||||||
// This is used to re-enable the metrics-recording crash handler after
|
// This is used to re-enable the metrics-recording crash handler after
|
||||||
// MonitorSelf() sets up a Crashpad exception handler. On macOS, the
|
// MonitorSelf() sets up a Crashpad exception handler. On macOS, the
|
||||||
// metrics-recording handler uses signals and the Crashpad handler uses Mach
|
// metrics-recording handler uses signals and the Crashpad handler uses Mach
|
||||||
// exceptions, so there’s nothing to re-enable.
|
// 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() {
|
void InstallCrashHandler() {
|
||||||
@ -300,6 +328,8 @@ void InstallCrashHandler() {
|
|||||||
Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
|
Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(OS_MACOSX)
|
||||||
|
|
||||||
struct ResetSIGTERMTraits {
|
struct ResetSIGTERMTraits {
|
||||||
static struct sigaction* InvalidValue() {
|
static struct sigaction* InvalidValue() {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@ -324,21 +354,6 @@ void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) {
|
|||||||
g_exception_handler_server->Stop();
|
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
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
@ -396,22 +411,6 @@ void InstallCrashHandler() {
|
|||||||
ALLOW_UNUSED_LOCAL(terminate_handler);
|
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
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
void MonitorSelf(const Options& options) {
|
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
|
// instance of crashpad_handler to be writing metrics at a time, and it should
|
||||||
// be the primary instance.
|
// be the primary instance.
|
||||||
CrashpadClient crashpad_client;
|
CrashpadClient crashpad_client;
|
||||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
#if defined(OS_ANDROID)
|
||||||
if (!crashpad_client.StartHandlerAtCrash(executable_path,
|
if (!crashpad_client.StartHandlerAtCrash(executable_path,
|
||||||
options.database,
|
options.database,
|
||||||
base::FilePath(),
|
base::FilePath(),
|
||||||
@ -500,6 +499,12 @@ class ScopedStoppable {
|
|||||||
int HandlerMain(int argc,
|
int HandlerMain(int argc,
|
||||||
char* argv[],
|
char* argv[],
|
||||||
const UserStreamDataSources* user_stream_sources) {
|
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();
|
InstallCrashHandler();
|
||||||
CallMetricsRecordNormalExit metrics_record_normal_exit;
|
CallMetricsRecordNormalExit metrics_record_normal_exit;
|
||||||
|
|
||||||
@ -518,6 +523,9 @@ int HandlerMain(int argc,
|
|||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
kOptionInitialClientData,
|
kOptionInitialClientData,
|
||||||
#endif // OS_WIN
|
#endif // OS_WIN
|
||||||
|
#if defined(OS_ANDROID) || defined(OS_LINUX)
|
||||||
|
kOptionInitialClientFD,
|
||||||
|
#endif // OS_ANDROID || OS_LINUX
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
kOptionMachService,
|
kOptionMachService,
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
@ -529,6 +537,9 @@ int HandlerMain(int argc,
|
|||||||
kOptionNoPeriodicTasks,
|
kOptionNoPeriodicTasks,
|
||||||
kOptionNoRateLimit,
|
kOptionNoRateLimit,
|
||||||
kOptionNoUploadGzip,
|
kOptionNoUploadGzip,
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
kOptionNoWriteMinidumpToDatabase,
|
||||||
|
#endif // OS_ANDROID
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
kOptionPipeName,
|
kOptionPipeName,
|
||||||
#endif // OS_WIN
|
#endif // OS_WIN
|
||||||
@ -536,11 +547,19 @@ int HandlerMain(int argc,
|
|||||||
kOptionResetOwnCrashExceptionPortToSystemDefault,
|
kOptionResetOwnCrashExceptionPortToSystemDefault,
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
kOptionTraceParentWithException,
|
|
||||||
kOptionInitialClientFD,
|
|
||||||
kOptionSanitizationInformation,
|
kOptionSanitizationInformation,
|
||||||
|
kOptionSharedClientConnection,
|
||||||
|
kOptionTraceParentWithException,
|
||||||
#endif
|
#endif
|
||||||
kOptionURL,
|
kOptionURL,
|
||||||
|
#if defined(OS_CHROMEOS)
|
||||||
|
kOptionUseCrosCrashReporter,
|
||||||
|
kOptionMinidumpDirForTests,
|
||||||
|
kOptionAlwaysAllowFeedback,
|
||||||
|
#endif // OS_CHROMEOS
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
kOptionWriteMinidumpToLog,
|
||||||
|
#endif // OS_ANDROID
|
||||||
|
|
||||||
// Standard options.
|
// Standard options.
|
||||||
kOptionHelp = -2,
|
kOptionHelp = -2,
|
||||||
@ -559,6 +578,9 @@ int HandlerMain(int argc,
|
|||||||
nullptr,
|
nullptr,
|
||||||
kOptionInitialClientData},
|
kOptionInitialClientData},
|
||||||
#endif // OS_MACOSX
|
#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)
|
#if defined(OS_MACOSX)
|
||||||
{"mach-service", required_argument, nullptr, kOptionMachService},
|
{"mach-service", required_argument, nullptr, kOptionMachService},
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
@ -579,6 +601,12 @@ int HandlerMain(int argc,
|
|||||||
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
|
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
|
||||||
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
||||||
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
|
{"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)
|
#if defined(OS_WIN)
|
||||||
{"pipe-name", required_argument, nullptr, kOptionPipeName},
|
{"pipe-name", required_argument, nullptr, kOptionPipeName},
|
||||||
#endif // OS_WIN
|
#endif // OS_WIN
|
||||||
@ -589,17 +617,37 @@ int HandlerMain(int argc,
|
|||||||
kOptionResetOwnCrashExceptionPortToSystemDefault},
|
kOptionResetOwnCrashExceptionPortToSystemDefault},
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
{"trace-parent-with-exception",
|
|
||||||
required_argument,
|
|
||||||
nullptr,
|
|
||||||
kOptionTraceParentWithException},
|
|
||||||
{"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD},
|
|
||||||
{"sanitization-information",
|
{"sanitization-information",
|
||||||
required_argument,
|
required_argument,
|
||||||
nullptr,
|
nullptr,
|
||||||
kOptionSanitizationInformation},
|
kOptionSanitizationInformation},
|
||||||
|
{"shared-client-connection",
|
||||||
|
no_argument,
|
||||||
|
nullptr,
|
||||||
|
kOptionSharedClientConnection},
|
||||||
|
{"trace-parent-with-exception",
|
||||||
|
required_argument,
|
||||||
|
nullptr,
|
||||||
|
kOptionTraceParentWithException},
|
||||||
#endif // OS_LINUX || OS_ANDROID
|
#endif // OS_LINUX || OS_ANDROID
|
||||||
{"url", required_argument, nullptr, kOptionURL},
|
{"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},
|
{"help", no_argument, nullptr, kOptionHelp},
|
||||||
{"version", no_argument, nullptr, kOptionVersion},
|
{"version", no_argument, nullptr, kOptionVersion},
|
||||||
{nullptr, 0, nullptr, 0},
|
{nullptr, 0, nullptr, 0},
|
||||||
@ -610,13 +658,14 @@ int HandlerMain(int argc,
|
|||||||
options.handshake_fd = -1;
|
options.handshake_fd = -1;
|
||||||
#endif
|
#endif
|
||||||
options.identify_client_via_url = true;
|
options.identify_client_via_url = true;
|
||||||
|
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
|
options.initial_client_fd = kInvalidFileHandle;
|
||||||
|
#endif
|
||||||
options.periodic_tasks = true;
|
options.periodic_tasks = true;
|
||||||
options.rate_limit = true;
|
options.rate_limit = true;
|
||||||
options.upload_gzip = true;
|
options.upload_gzip = true;
|
||||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
#if defined(OS_ANDROID)
|
||||||
options.exception_information_address = 0;
|
options.write_minidump_to_database = true;
|
||||||
options.initial_client_fd = kInvalidFileHandle;
|
|
||||||
options.sanitization_information_address = 0;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int opt;
|
int opt;
|
||||||
@ -658,6 +707,15 @@ int HandlerMain(int argc,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif // OS_WIN
|
#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: {
|
case kOptionMetrics: {
|
||||||
options.metrics_dir = base::FilePath(
|
options.metrics_dir = base::FilePath(
|
||||||
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
|
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
|
||||||
@ -695,6 +753,12 @@ int HandlerMain(int argc,
|
|||||||
options.upload_gzip = false;
|
options.upload_gzip = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
case kOptionNoWriteMinidumpToDatabase: {
|
||||||
|
options.write_minidump_to_database = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // OS_ANDROID
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
case kOptionPipeName: {
|
case kOptionPipeName: {
|
||||||
options.pipe_name = optarg;
|
options.pipe_name = optarg;
|
||||||
@ -708,21 +772,6 @@ int HandlerMain(int argc,
|
|||||||
}
|
}
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
#if defined(OS_LINUX) || defined(OS_ANDROID)
|
#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: {
|
case kOptionSanitizationInformation: {
|
||||||
if (!StringToNumber(optarg,
|
if (!StringToNumber(optarg,
|
||||||
&options.sanitization_information_address)) {
|
&options.sanitization_information_address)) {
|
||||||
@ -732,11 +781,44 @@ int HandlerMain(int argc,
|
|||||||
}
|
}
|
||||||
break;
|
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
|
#endif // OS_LINUX || OS_ANDROID
|
||||||
case kOptionURL: {
|
case kOptionURL: {
|
||||||
options.url = optarg;
|
options.url = optarg;
|
||||||
break;
|
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: {
|
case kOptionHelp: {
|
||||||
Usage(me);
|
Usage(me);
|
||||||
MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
|
MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly);
|
||||||
@ -781,7 +863,7 @@ int HandlerMain(int argc,
|
|||||||
if (!options.exception_information_address &&
|
if (!options.exception_information_address &&
|
||||||
options.initial_client_fd == kInvalidFileHandle) {
|
options.initial_client_fd == kInvalidFileHandle) {
|
||||||
ToolSupport::UsageHint(
|
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();
|
return ExitFailure();
|
||||||
}
|
}
|
||||||
if (options.sanitization_information_address &&
|
if (options.sanitization_information_address &&
|
||||||
@ -791,6 +873,20 @@ int HandlerMain(int argc,
|
|||||||
"--sanitization_information requires --trace-parent-with-exception");
|
"--sanitization_information requires --trace-parent-with-exception");
|
||||||
return ExitFailure();
|
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
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
if (options.database.empty()) {
|
if (options.database.empty()) {
|
||||||
@ -856,23 +952,61 @@ int HandlerMain(int argc,
|
|||||||
upload_thread.Get()->Start();
|
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(),
|
database.get(),
|
||||||
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
|
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
|
||||||
&options.annotations,
|
&options.annotations,
|
||||||
#if defined(OS_FUCHSIA)
|
true,
|
||||||
// TODO(scottmg): Process level file attachments, and for all platforms.
|
false,
|
||||||
nullptr,
|
|
||||||
#endif
|
|
||||||
user_stream_sources);
|
user_stream_sources);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
exception_handler = std::make_unique<CrashReportExceptionHandler>(
|
||||||
|
database.get(),
|
||||||
|
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
|
||||||
|
&options.annotations,
|
||||||
|
#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) {
|
if (options.exception_information_address) {
|
||||||
ClientInformation info;
|
ExceptionHandlerProtocol::ClientInformation info;
|
||||||
info.exception_information_address = options.exception_information_address;
|
info.exception_information_address = options.exception_information_address;
|
||||||
info.sanitization_information_address =
|
info.sanitization_information_address =
|
||||||
options.sanitization_information_address;
|
options.sanitization_information_address;
|
||||||
return exception_handler.HandleException(getppid(), info) ? EXIT_SUCCESS
|
return exception_handler->HandleException(getppid(), geteuid(), info)
|
||||||
|
? EXIT_SUCCESS
|
||||||
: ExitFailure();
|
: ExitFailure();
|
||||||
}
|
}
|
||||||
#endif // OS_LINUX || OS_ANDROID
|
#endif // OS_LINUX || OS_ANDROID
|
||||||
@ -937,27 +1071,6 @@ int HandlerMain(int argc,
|
|||||||
if (!options.pipe_name.empty()) {
|
if (!options.pipe_name.empty()) {
|
||||||
exception_handler_server.SetPipeName(base::UTF8ToUTF16(options.pipe_name));
|
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)
|
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
ExceptionHandlerServer exception_handler_server;
|
ExceptionHandlerServer exception_handler_server;
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
@ -978,17 +1091,18 @@ int HandlerMain(int argc,
|
|||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
if (options.initial_client_data.IsValid()) {
|
if (options.initial_client_data.IsValid()) {
|
||||||
exception_handler_server.InitializeWithInheritedDataForInitialClient(
|
exception_handler_server.InitializeWithInheritedDataForInitialClient(
|
||||||
options.initial_client_data, &exception_handler);
|
options.initial_client_data, exception_handler.get());
|
||||||
}
|
}
|
||||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
if (options.initial_client_fd == kInvalidFileHandle ||
|
if (options.initial_client_fd == kInvalidFileHandle ||
|
||||||
!exception_handler_server.InitializeWithClient(
|
!exception_handler_server.InitializeWithClient(
|
||||||
ScopedFileHandle(options.initial_client_fd))) {
|
ScopedFileHandle(options.initial_client_fd),
|
||||||
|
options.shared_client_connection)) {
|
||||||
return ExitFailure();
|
return ExitFailure();
|
||||||
}
|
}
|
||||||
#endif // OS_WIN
|
#endif // OS_WIN
|
||||||
|
|
||||||
exception_handler_server.Run(&exception_handler);
|
exception_handler_server.Run(exception_handler.get());
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
||||||
#define CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
#define CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
||||||
|
|
||||||
|
#include "build/build_config.h"
|
||||||
#include "handler/user_stream_data_source.h"
|
#include "handler/user_stream_data_source.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -34,6 +35,16 @@ int HandlerMain(int argc,
|
|||||||
char* argv[],
|
char* argv[],
|
||||||
const UserStreamDataSources* user_stream_sources);
|
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
|
} // namespace crashpad
|
||||||
|
|
||||||
#endif // CRASHPAD_HANDLER_HANDLER_MAIN_H_
|
#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 "handler/linux/crash_report_exception_handler.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "client/settings.h"
|
#include "client/settings.h"
|
||||||
|
#include "handler/linux/capture_snapshot.h"
|
||||||
#include "minidump/minidump_file_writer.h"
|
#include "minidump/minidump_file_writer.h"
|
||||||
#include "snapshot/crashpad_info_client_options.h"
|
|
||||||
#include "snapshot/linux/process_snapshot_linux.h"
|
#include "snapshot/linux/process_snapshot_linux.h"
|
||||||
#include "snapshot/sanitized/process_snapshot_sanitized.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/direct_ptrace_connection.h"
|
||||||
#include "util/linux/ptrace_client.h"
|
#include "util/linux/ptrace_client.h"
|
||||||
|
#include "util/misc/implicit_cast.h"
|
||||||
#include "util/misc/metrics.h"
|
#include "util/misc/metrics.h"
|
||||||
#include "util/misc/tri_state.h"
|
|
||||||
#include "util/misc/uuid.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 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(
|
CrashReportExceptionHandler::CrashReportExceptionHandler(
|
||||||
CrashReportDatabase* database,
|
CrashReportDatabase* database,
|
||||||
CrashReportUploadThread* upload_thread,
|
CrashReportUploadThread* upload_thread,
|
||||||
const std::map<std::string, std::string>* process_annotations,
|
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)
|
const UserStreamDataSources* user_stream_data_sources)
|
||||||
: database_(database),
|
: database_(database),
|
||||||
upload_thread_(upload_thread),
|
upload_thread_(upload_thread),
|
||||||
process_annotations_(process_annotations),
|
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;
|
CrashReportExceptionHandler::~CrashReportExceptionHandler() = default;
|
||||||
|
|
||||||
bool CrashReportExceptionHandler::HandleException(
|
bool CrashReportExceptionHandler::HandleException(
|
||||||
pid_t client_process_id,
|
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();
|
Metrics::ExceptionEncountered();
|
||||||
|
|
||||||
DirectPtraceConnection connection;
|
DirectPtraceConnection connection;
|
||||||
@ -55,13 +92,20 @@ bool CrashReportExceptionHandler::HandleException(
|
|||||||
return false;
|
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(
|
bool CrashReportExceptionHandler::HandleExceptionWithBroker(
|
||||||
pid_t client_process_id,
|
pid_t client_process_id,
|
||||||
const ClientInformation& info,
|
uid_t client_uid,
|
||||||
int broker_sock) {
|
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||||
|
int broker_sock,
|
||||||
|
UUID* local_report_id) {
|
||||||
Metrics::ExceptionEncountered();
|
Metrics::ExceptionEncountered();
|
||||||
|
|
||||||
PtraceClient client;
|
PtraceClient client;
|
||||||
@ -71,30 +115,30 @@ bool CrashReportExceptionHandler::HandleExceptionWithBroker(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return HandleExceptionWithConnection(&client, info);
|
return HandleExceptionWithConnection(
|
||||||
|
&client, info, client_uid, 0, nullptr, local_report_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
||||||
PtraceConnection* connection,
|
PtraceConnection* connection,
|
||||||
const ClientInformation& info) {
|
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||||
ProcessSnapshotLinux process_snapshot;
|
uid_t client_uid,
|
||||||
if (!process_snapshot.Initialize(connection)) {
|
VMAddress requesting_thread_stack_address,
|
||||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!process_snapshot.InitializeException(
|
|
||||||
info.exception_information_address)) {
|
|
||||||
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) {
|
|
||||||
UUID client_id;
|
UUID client_id;
|
||||||
Settings* const settings = database_->GetSettings();
|
Settings* const settings = database_->GetSettings();
|
||||||
if (settings) {
|
if (settings) {
|
||||||
@ -103,10 +147,22 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
|||||||
// which is appropriate.
|
// which is appropriate.
|
||||||
settings->GetClientID(&client_id);
|
settings->GetClientID(&client_id);
|
||||||
}
|
}
|
||||||
|
process_snapshot->SetClientID(client_id);
|
||||||
|
|
||||||
process_snapshot.SetClientID(client_id);
|
return write_minidump_to_database_
|
||||||
process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
|
? 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;
|
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||||
CrashReportDatabase::OperationStatus database_status =
|
CrashReportDatabase::OperationStatus database_status =
|
||||||
database_->PrepareNewCrashReport(&new_report);
|
database_->PrepareNewCrashReport(&new_report);
|
||||||
@ -117,48 +173,11 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
process_snapshot.SetReportID(new_report->ReportID());
|
process_snapshot->SetReportID(new_report->ReportID());
|
||||||
|
|
||||||
ProcessSnapshot* snapshot = nullptr;
|
ProcessSnapshot* snapshot =
|
||||||
ProcessSnapshotSanitized sanitized;
|
sanitized_snapshot ? implicit_cast<ProcessSnapshot*>(sanitized_snapshot)
|
||||||
std::vector<std::string> whitelist;
|
: implicit_cast<ProcessSnapshot*>(process_snapshot);
|
||||||
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;
|
MinidumpFileWriter minidump;
|
||||||
minidump.InitializeFromSnapshot(snapshot);
|
minidump.InitializeFromSnapshot(snapshot);
|
||||||
@ -171,6 +190,16 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
|||||||
return false;
|
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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UUID uuid;
|
UUID uuid;
|
||||||
database_status =
|
database_status =
|
||||||
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
||||||
@ -184,10 +213,36 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
|||||||
if (upload_thread_) {
|
if (upload_thread_) {
|
||||||
upload_thread_->ReportPending(uuid);
|
upload_thread_->ReportPending(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (local_report_id != nullptr) {
|
||||||
|
*local_report_id = uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
|
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
|
} // namespace crashpad
|
||||||
|
@ -26,9 +26,13 @@
|
|||||||
#include "util/linux/exception_handler_protocol.h"
|
#include "util/linux/exception_handler_protocol.h"
|
||||||
#include "util/linux/ptrace_connection.h"
|
#include "util/linux/ptrace_connection.h"
|
||||||
#include "util/misc/address_types.h"
|
#include "util/misc/address_types.h"
|
||||||
|
#include "util/misc/uuid.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
|
class ProcessSnapshotLinux;
|
||||||
|
class ProcessSnapshotSanitized;
|
||||||
|
|
||||||
//! \brief An exception handler that writes crash reports for exceptions
|
//! \brief An exception handler that writes crash reports for exceptions
|
||||||
//! to a CrashReportDatabase.
|
//! to a CrashReportDatabase.
|
||||||
class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
||||||
@ -49,6 +53,10 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
|||||||
//! To interoperate with Breakpad servers, the recommended practice is to
|
//! To interoperate with Breakpad servers, the recommended practice is to
|
||||||
//! specify values for the `"prod"` and `"ver"` keys as process
|
//! specify values for the `"prod"` and `"ver"` keys as process
|
||||||
//! annotations.
|
//! 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
|
//! \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
|
//! crash reports. For each crash report that is written, the data sources
|
||||||
//! are called in turn. These data sources may contribute additional
|
//! are called in turn. These data sources may contribute additional
|
||||||
@ -57,26 +65,49 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
|||||||
CrashReportDatabase* database,
|
CrashReportDatabase* database,
|
||||||
CrashReportUploadThread* upload_thread,
|
CrashReportUploadThread* upload_thread,
|
||||||
const std::map<std::string, std::string>* process_annotations,
|
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);
|
const UserStreamDataSources* user_stream_data_sources);
|
||||||
|
|
||||||
~CrashReportExceptionHandler();
|
~CrashReportExceptionHandler() override;
|
||||||
|
|
||||||
// ExceptionHandlerServer::Delegate:
|
// ExceptionHandlerServer::Delegate:
|
||||||
|
|
||||||
bool HandleException(pid_t client_process_id,
|
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,
|
bool HandleExceptionWithBroker(
|
||||||
const ClientInformation& info,
|
pid_t client_process_id,
|
||||||
int broker_sock) override;
|
uid_t client_uid,
|
||||||
|
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||||
|
int broker_sock,
|
||||||
|
UUID* local_report_id = nullptr) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool HandleExceptionWithConnection(PtraceConnection* connection,
|
bool HandleExceptionWithConnection(
|
||||||
const ClientInformation& info);
|
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
|
CrashReportDatabase* database_; // weak
|
||||||
CrashReportUploadThread* upload_thread_; // weak
|
CrashReportUploadThread* upload_thread_; // weak
|
||||||
const std::map<std::string, std::string>* process_annotations_; // 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
|
const UserStreamDataSources* user_stream_data_sources_; // weak
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
|
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 "handler/linux/exception_handler_server.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
@ -31,6 +32,8 @@
|
|||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
#include "util/file/filesystem.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"
|
#include "util/misc/as_underlying_type.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -90,54 +93,96 @@ PtraceScope GetPtraceScope() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HaveCapSysPtrace() {
|
bool HaveCapSysPtrace() {
|
||||||
struct __user_cap_header_struct cap_header = {};
|
__user_cap_header_struct cap_header;
|
||||||
struct __user_cap_data_struct cap_data = {};
|
|
||||||
|
|
||||||
cap_header.pid = getpid();
|
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";
|
PLOG(ERROR) << "capget";
|
||||||
return false;
|
LOG_IF(ERROR, errno == EINVAL) << "cap_header.version " << std::hex
|
||||||
}
|
|
||||||
|
|
||||||
if (cap_header.version != _LINUX_CAPABILITY_VERSION_3) {
|
|
||||||
LOG(ERROR) << "Unexpected capability version " << std::hex
|
|
||||||
<< cap_header.version;
|
<< cap_header.version;
|
||||||
return false;
|
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) {
|
bool SendMessageToClient(
|
||||||
ServerToClientMessage message = {};
|
int client_sock,
|
||||||
|
ExceptionHandlerProtocol::ServerToClientMessage::Type type) {
|
||||||
|
ExceptionHandlerProtocol::ServerToClientMessage message = {};
|
||||||
message.type = type;
|
message.type = type;
|
||||||
if (type == ServerToClientMessage::kTypeSetPtracer) {
|
if (type ==
|
||||||
|
ExceptionHandlerProtocol::ServerToClientMessage::kTypeSetPtracer) {
|
||||||
message.pid = getpid();
|
message.pid = getpid();
|
||||||
}
|
}
|
||||||
return LoggingWriteFile(client_sock, &message, sizeof(message));
|
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 {
|
class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
|
||||||
public:
|
public:
|
||||||
PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {}
|
PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {}
|
||||||
~PtraceStrategyDeciderImpl() = default;
|
~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()) {
|
switch (GetPtraceScope()) {
|
||||||
case PtraceScope::kClassic:
|
case PtraceScope::kClassic:
|
||||||
if (getuid() == client_credentials.uid) {
|
if (getuid() == client_credentials.uid || HaveCapSysPtrace()) {
|
||||||
return Strategy::kDirectPtrace;
|
return Strategy::kDirectPtrace;
|
||||||
}
|
}
|
||||||
return TryForkingBroker(sock);
|
return multiple_clients ? Strategy::kNoPtrace : TryForkingBroker(sock);
|
||||||
|
|
||||||
case PtraceScope::kRestricted:
|
case PtraceScope::kRestricted:
|
||||||
|
if (multiple_clients) {
|
||||||
|
return Strategy::kDirectPtrace;
|
||||||
|
}
|
||||||
if (!SendMessageToClient(sock,
|
if (!SendMessageToClient(sock,
|
||||||
|
ExceptionHandlerProtocol::
|
||||||
ServerToClientMessage::kTypeSetPtracer)) {
|
ServerToClientMessage::kTypeSetPtracer)) {
|
||||||
return Strategy::kError;
|
return Strategy::kError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno status;
|
ExceptionHandlerProtocol::Errno status;
|
||||||
if (!LoggingReadFileExactly(sock, &status, sizeof(status))) {
|
if (!LoggingReadFileExactly(sock, &status, sizeof(status))) {
|
||||||
return Strategy::kError;
|
return Strategy::kError;
|
||||||
}
|
}
|
||||||
@ -169,12 +214,13 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static Strategy TryForkingBroker(int client_sock) {
|
static Strategy TryForkingBroker(int client_sock) {
|
||||||
if (!SendMessageToClient(client_sock,
|
if (!SendMessageToClient(
|
||||||
ServerToClientMessage::kTypeForkBroker)) {
|
client_sock,
|
||||||
|
ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker)) {
|
||||||
return Strategy::kError;
|
return Strategy::kError;
|
||||||
}
|
}
|
||||||
|
|
||||||
Errno status;
|
ExceptionHandlerProtocol::Errno status;
|
||||||
if (!LoggingReadFileExactly(client_sock, &status, sizeof(status))) {
|
if (!LoggingReadFileExactly(client_sock, &status, sizeof(status))) {
|
||||||
return Strategy::kError;
|
return Strategy::kError;
|
||||||
}
|
}
|
||||||
@ -190,12 +236,6 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
struct ExceptionHandlerServer::Event {
|
|
||||||
enum class Type { kShutdown, kClientMessage } type;
|
|
||||||
|
|
||||||
ScopedFileHandle fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
ExceptionHandlerServer::ExceptionHandlerServer()
|
ExceptionHandlerServer::ExceptionHandlerServer()
|
||||||
: clients_(),
|
: clients_(),
|
||||||
shutdown_event_(),
|
shutdown_event_(),
|
||||||
@ -211,7 +251,8 @@ void ExceptionHandlerServer::SetPtraceStrategyDecider(
|
|||||||
strategy_decider_ = std::move(decider);
|
strategy_decider_ = std::move(decider);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock) {
|
bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock,
|
||||||
|
bool multiple_clients) {
|
||||||
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
||||||
|
|
||||||
pollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
|
pollfd_.reset(epoll_create1(EPOLL_CLOEXEC));
|
||||||
@ -239,7 +280,9 @@ bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!InstallClientSocket(std::move(sock))) {
|
if (!InstallClientSocket(std::move(sock),
|
||||||
|
multiple_clients ? Event::Type::kSharedSocketMessage
|
||||||
|
: Event::Type::kClientMessage)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,8 +324,8 @@ void ExceptionHandlerServer::Stop() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {
|
void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {
|
||||||
DCHECK_EQ(AsUnderlyingType(event->type),
|
DCHECK_NE(AsUnderlyingType(event->type),
|
||||||
AsUnderlyingType(Event::Type::kClientMessage));
|
AsUnderlyingType(Event::Type::kShutdown));
|
||||||
|
|
||||||
if (event_type & EPOLLERR) {
|
if (event_type & EPOLLERR) {
|
||||||
LogSocketError(event->fd.get());
|
LogSocketError(event->fd.get());
|
||||||
@ -306,16 +349,30 @@ void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket) {
|
bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket,
|
||||||
int optval = 1;
|
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);
|
socklen_t optlen = sizeof(optval);
|
||||||
if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) != 0) {
|
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";
|
PLOG(ERROR) << "setsockopt";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto event = std::make_unique<Event>();
|
auto event = std::make_unique<Event>();
|
||||||
event->type = Event::Type::kClientMessage;
|
event->type = type;
|
||||||
event->fd.reset(socket.release());
|
event->fd.reset(socket.release());
|
||||||
|
|
||||||
Event* eventp = event.get();
|
Event* eventp = event.get();
|
||||||
@ -355,53 +412,24 @@ bool ExceptionHandlerServer::UninstallClientSocket(Event* event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {
|
bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {
|
||||||
ClientToServerMessage message;
|
ExceptionHandlerProtocol::ClientToServerMessage message;
|
||||||
iovec iov;
|
ucred creds;
|
||||||
iov.iov_base = &message;
|
if (!UnixCredentialSocket::RecvMsg(
|
||||||
iov.iov_len = sizeof(message);
|
event->fd.get(), &message, sizeof(message), &creds)) {
|
||||||
|
|
||||||
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.
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.msg_name != nullptr || msg.msg_namelen != 0) {
|
switch (message.type) {
|
||||||
LOG(ERROR) << "unexpected msg name";
|
case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCheckCredentials:
|
||||||
return false;
|
return SendCredentials(event->fd.get());
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.msg_iovlen != 1) {
|
case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCrashDumpRequest:
|
||||||
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:
|
|
||||||
return HandleCrashDumpRequest(
|
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);
|
DCHECK(false);
|
||||||
@ -410,53 +438,56 @@ bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ExceptionHandlerServer::HandleCrashDumpRequest(
|
bool ExceptionHandlerServer::HandleCrashDumpRequest(
|
||||||
const msghdr& msg,
|
const ucred& creds,
|
||||||
const ClientInformation& client_info,
|
const ExceptionHandlerProtocol::ClientInformation& client_info,
|
||||||
int client_sock) {
|
VMAddress requesting_thread_stack_address,
|
||||||
cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
|
int client_sock,
|
||||||
if (cmsg == nullptr) {
|
bool multiple_clients) {
|
||||||
LOG(ERROR) << "missing credentials";
|
pid_t client_process_id = creds.pid;
|
||||||
return false;
|
pid_t requesting_thread_id = -1;
|
||||||
}
|
uid_t client_uid = creds.uid;
|
||||||
|
|
||||||
if (cmsg->cmsg_level != SOL_SOCKET) {
|
switch (
|
||||||
LOG(ERROR) << "unexpected cmsg_level " << cmsg->cmsg_level;
|
strategy_decider_->ChooseStrategy(client_sock, multiple_clients, creds)) {
|
||||||
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)) {
|
|
||||||
case PtraceStrategyDecider::Strategy::kError:
|
case PtraceStrategyDecider::Strategy::kError:
|
||||||
|
if (multiple_clients) {
|
||||||
|
SendSIGCONT(client_process_id, requesting_thread_id);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case PtraceStrategyDecider::Strategy::kNoPtrace:
|
case PtraceStrategyDecider::Strategy::kNoPtrace:
|
||||||
return SendMessageToClient(client_sock,
|
if (multiple_clients) {
|
||||||
ServerToClientMessage::kTypeCrashDumpFailed);
|
SendSIGCONT(client_process_id, requesting_thread_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return SendMessageToClient(
|
||||||
|
client_sock,
|
||||||
|
ExceptionHandlerProtocol::ServerToClientMessage::
|
||||||
|
kTypeCrashDumpFailed);
|
||||||
|
|
||||||
case PtraceStrategyDecider::Strategy::kDirectPtrace:
|
case PtraceStrategyDecider::Strategy::kDirectPtrace: {
|
||||||
delegate_->HandleException(client_process_id, client_info);
|
delegate_->HandleException(client_process_id,
|
||||||
break;
|
client_uid,
|
||||||
|
client_info,
|
||||||
case PtraceStrategyDecider::Strategy::kUseBroker:
|
requesting_thread_stack_address,
|
||||||
delegate_->HandleExceptionWithBroker(
|
&requesting_thread_id);
|
||||||
client_process_id, client_info, client_sock);
|
if (multiple_clients) {
|
||||||
|
SendSIGCONT(client_process_id, requesting_thread_id);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SendMessageToClient(client_sock,
|
case PtraceStrategyDecider::Strategy::kUseBroker:
|
||||||
ServerToClientMessage::kTypeCrashDumpComplete);
|
DCHECK(!multiple_clients);
|
||||||
|
delegate_->HandleExceptionWithBroker(
|
||||||
|
client_process_id, client_uid, client_info, client_sock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SendMessageToClient(
|
||||||
|
client_sock,
|
||||||
|
ExceptionHandlerProtocol::ServerToClientMessage::kTypeCrashDumpComplete);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
@ -26,6 +27,7 @@
|
|||||||
#include "util/linux/exception_handler_protocol.h"
|
#include "util/linux/exception_handler_protocol.h"
|
||||||
#include "util/misc/address_types.h"
|
#include "util/misc/address_types.h"
|
||||||
#include "util/misc/initialization_state_dcheck.h"
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
|
#include "util/misc/uuid.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
@ -53,9 +55,12 @@ class PtraceStrategyDecider {
|
|||||||
//! \brief Chooses an appropriate `ptrace` strategy.
|
//! \brief Chooses an appropriate `ptrace` strategy.
|
||||||
//!
|
//!
|
||||||
//! \param[in] sock A socket conncted to a ExceptionHandlerClient.
|
//! \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.
|
//! \param[in] client_credentials The credentials for the connected client.
|
||||||
//! \return the chosen #Strategy.
|
//! \return the chosen #Strategy.
|
||||||
virtual Strategy ChooseStrategy(int sock,
|
virtual Strategy ChooseStrategy(int sock,
|
||||||
|
bool multiple_clients,
|
||||||
const ucred& client_credentials) = 0;
|
const ucred& client_credentials) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -71,24 +76,43 @@ class ExceptionHandlerServer {
|
|||||||
//! \brief Called on receipt of a crash dump request from a client.
|
//! \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_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] 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.
|
//! \return `true` on success. `false` on failure with a message logged.
|
||||||
virtual bool HandleException(pid_t client_process_id,
|
virtual bool HandleException(
|
||||||
const ClientInformation& info) = 0;
|
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
|
//! \brief Called on the receipt of a crash dump request from a client for a
|
||||||
//! crash that should be mediated by a PtraceBroker.
|
//! crash that should be mediated by a PtraceBroker.
|
||||||
//!
|
//!
|
||||||
//! \param[in] client_process_id The process ID of the crashing client.
|
//! \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] info Information on the client.
|
||||||
//! \param[in] broker_sock A socket connected to the PtraceBroker.
|
//! \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.
|
//! \return `true` on success. `false` on failure with a message logged.
|
||||||
virtual bool HandleExceptionWithBroker(pid_t client_process_id,
|
virtual bool HandleExceptionWithBroker(
|
||||||
const ClientInformation& info,
|
pid_t client_process_id,
|
||||||
int broker_sock) = 0;
|
uid_t client_uid,
|
||||||
|
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||||
|
int broker_sock,
|
||||||
|
UUID* local_report_id = nullptr) = 0;
|
||||||
|
|
||||||
protected:
|
virtual ~Delegate() {}
|
||||||
~Delegate() {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ExceptionHandlerServer();
|
ExceptionHandlerServer();
|
||||||
@ -105,8 +129,11 @@ class ExceptionHandlerServer {
|
|||||||
//! This method must be successfully called before Run().
|
//! This method must be successfully called before Run().
|
||||||
//!
|
//!
|
||||||
//! \param[in] sock A socket on which to receive client requests.
|
//! \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.
|
//! \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.
|
//! \brief Runs the exception-handling server.
|
||||||
//!
|
//!
|
||||||
@ -126,22 +153,39 @@ class ExceptionHandlerServer {
|
|||||||
void Stop();
|
void Stop();
|
||||||
|
|
||||||
private:
|
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);
|
void HandleEvent(Event* event, uint32_t event_type);
|
||||||
bool InstallClientSocket(ScopedFileHandle socket);
|
bool InstallClientSocket(ScopedFileHandle socket, Event::Type type);
|
||||||
bool UninstallClientSocket(Event* event);
|
bool UninstallClientSocket(Event* event);
|
||||||
bool ReceiveClientMessage(Event* event);
|
bool ReceiveClientMessage(Event* event);
|
||||||
bool HandleCrashDumpRequest(const msghdr& msg,
|
bool HandleCrashDumpRequest(
|
||||||
const ClientInformation& client_info,
|
const ucred& creds,
|
||||||
int client_sock);
|
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::unordered_map<int, std::unique_ptr<Event>> clients_;
|
||||||
std::unique_ptr<Event> shutdown_event_;
|
std::unique_ptr<Event> shutdown_event_;
|
||||||
std::unique_ptr<PtraceStrategyDecider> strategy_decider_;
|
std::unique_ptr<PtraceStrategyDecider> strategy_decider_;
|
||||||
Delegate* delegate_;
|
Delegate* delegate_;
|
||||||
ScopedFileHandle pollfd_;
|
ScopedFileHandle pollfd_;
|
||||||
bool keep_running_;
|
std::atomic<bool> keep_running_;
|
||||||
InitializationStateDcheck initialized_;
|
InitializationStateDcheck initialized_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
|
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServer);
|
||||||
|
@ -18,16 +18,23 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "snapshot/linux/process_snapshot_linux.h"
|
||||||
#include "test/errors.h"
|
#include "test/errors.h"
|
||||||
#include "test/multiprocess.h"
|
#include "test/multiprocess.h"
|
||||||
#include "util/linux/direct_ptrace_connection.h"
|
#include "util/linux/direct_ptrace_connection.h"
|
||||||
#include "util/linux/exception_handler_client.h"
|
#include "util/linux/exception_handler_client.h"
|
||||||
#include "util/linux/ptrace_client.h"
|
#include "util/linux/ptrace_client.h"
|
||||||
#include "util/linux/scoped_pr_set_ptracer.h"
|
#include "util/linux/scoped_pr_set_ptracer.h"
|
||||||
|
#include "util/misc/uuid.h"
|
||||||
#include "util/synchronization/semaphore.h"
|
#include "util/synchronization/semaphore.h"
|
||||||
#include "util/thread/thread.h"
|
#include "util/thread/thread.h"
|
||||||
|
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
#include <android/api-level.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
namespace {
|
namespace {
|
||||||
@ -101,7 +108,11 @@ class TestDelegate : public ExceptionHandlerServer::Delegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool HandleException(pid_t client_process_id,
|
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;
|
DirectPtraceConnection connection;
|
||||||
bool connected = connection.Initialize(client_process_id);
|
bool connected = connection.Initialize(client_process_id);
|
||||||
EXPECT_TRUE(connected);
|
EXPECT_TRUE(connected);
|
||||||
@ -109,12 +120,32 @@ class TestDelegate : public ExceptionHandlerServer::Delegate {
|
|||||||
last_exception_address_ = info.exception_information_address;
|
last_exception_address_ = info.exception_information_address;
|
||||||
last_client_ = client_process_id;
|
last_client_ = client_process_id;
|
||||||
sem_.Signal();
|
sem_.Signal();
|
||||||
return connected;
|
if (!connected) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HandleExceptionWithBroker(pid_t client_process_id,
|
if (requesting_thread_id) {
|
||||||
const ClientInformation& info,
|
if (requesting_thread_stack_address) {
|
||||||
int broker_sock) override {
|
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,
|
||||||
|
uid_t client_uid,
|
||||||
|
const ExceptionHandlerProtocol::ClientInformation& info,
|
||||||
|
int broker_sock,
|
||||||
|
UUID* local_report_id = nullptr) override {
|
||||||
PtraceClient client;
|
PtraceClient client;
|
||||||
bool connected = client.Initialize(broker_sock, client_process_id);
|
bool connected = client.Initialize(broker_sock, client_process_id);
|
||||||
EXPECT_TRUE(connected);
|
EXPECT_TRUE(connected);
|
||||||
@ -140,12 +171,15 @@ class MockPtraceStrategyDecider : public PtraceStrategyDecider {
|
|||||||
|
|
||||||
~MockPtraceStrategyDecider() {}
|
~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) {
|
if (strategy_ == Strategy::kUseBroker) {
|
||||||
ServerToClientMessage message = {};
|
ExceptionHandlerProtocol::ServerToClientMessage message = {};
|
||||||
message.type = ServerToClientMessage::kTypeForkBroker;
|
message.type =
|
||||||
|
ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker;
|
||||||
|
|
||||||
Errno status;
|
ExceptionHandlerProtocol::Errno status;
|
||||||
bool result = LoggingWriteFile(sock, &message, sizeof(message)) &&
|
bool result = LoggingWriteFile(sock, &message, sizeof(message)) &&
|
||||||
LoggingReadFileExactly(sock, &status, sizeof(status));
|
LoggingReadFileExactly(sock, &status, sizeof(status));
|
||||||
EXPECT_TRUE(result);
|
EXPECT_TRUE(result);
|
||||||
@ -169,13 +203,14 @@ class MockPtraceStrategyDecider : public PtraceStrategyDecider {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(MockPtraceStrategyDecider);
|
DISALLOW_COPY_AND_ASSIGN(MockPtraceStrategyDecider);
|
||||||
};
|
};
|
||||||
|
|
||||||
class ExceptionHandlerServerTest : public testing::Test {
|
class ExceptionHandlerServerTest : public testing::TestWithParam<bool> {
|
||||||
public:
|
public:
|
||||||
ExceptionHandlerServerTest()
|
ExceptionHandlerServerTest()
|
||||||
: server_(),
|
: server_(),
|
||||||
delegate_(),
|
delegate_(),
|
||||||
server_thread_(&server_, &delegate_),
|
server_thread_(&server_, &delegate_),
|
||||||
sock_to_handler_() {}
|
sock_to_handler_(),
|
||||||
|
use_multi_client_socket_(GetParam()) {}
|
||||||
|
|
||||||
~ExceptionHandlerServerTest() = default;
|
~ExceptionHandlerServerTest() = default;
|
||||||
|
|
||||||
@ -197,7 +232,7 @@ class ExceptionHandlerServerTest : public testing::Test {
|
|||||||
~CrashDumpTest() = default;
|
~CrashDumpTest() = default;
|
||||||
|
|
||||||
void MultiprocessParent() override {
|
void MultiprocessParent() override {
|
||||||
ClientInformation info;
|
ExceptionHandlerProtocol::ClientInformation info;
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info)));
|
LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info)));
|
||||||
|
|
||||||
@ -216,9 +251,8 @@ class ExceptionHandlerServerTest : public testing::Test {
|
|||||||
void MultiprocessChild() override {
|
void MultiprocessChild() override {
|
||||||
ASSERT_EQ(close(server_test_->sock_to_client_), 0);
|
ASSERT_EQ(close(server_test_->sock_to_client_), 0);
|
||||||
|
|
||||||
ClientInformation info;
|
ExceptionHandlerProtocol::ClientInformation info;
|
||||||
info.exception_information_address = 42;
|
info.exception_information_address = 42;
|
||||||
|
|
||||||
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info)));
|
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info)));
|
||||||
|
|
||||||
// If the current ptrace_scope is restricted, the broker needs to be set
|
// 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.
|
// ptracer allows the broker to inherit this condition.
|
||||||
ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ true);
|
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);
|
ASSERT_EQ(client.RequestCrashDump(info), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,16 +274,18 @@ class ExceptionHandlerServerTest : public testing::Test {
|
|||||||
|
|
||||||
void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,
|
void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy,
|
||||||
bool succeeds) {
|
bool succeeds) {
|
||||||
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
|
||||||
ServerThread()->Start();
|
|
||||||
|
|
||||||
Server()->SetPtraceStrategyDecider(
|
Server()->SetPtraceStrategyDecider(
|
||||||
std::make_unique<MockPtraceStrategyDecider>(strategy));
|
std::make_unique<MockPtraceStrategyDecider>(strategy));
|
||||||
|
|
||||||
|
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
||||||
|
ServerThread()->Start();
|
||||||
|
|
||||||
CrashDumpTest test(this, succeeds);
|
CrashDumpTest test(this, succeeds);
|
||||||
test.Run();
|
test.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UsingMultiClientSocket() const { return use_multi_client_socket_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
int socks[2];
|
int socks[2];
|
||||||
@ -256,7 +293,8 @@ class ExceptionHandlerServerTest : public testing::Test {
|
|||||||
sock_to_handler_.reset(socks[0]);
|
sock_to_handler_.reset(socks[0]);
|
||||||
sock_to_client_ = socks[1];
|
sock_to_client_ = socks[1];
|
||||||
|
|
||||||
ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1])));
|
ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1]),
|
||||||
|
use_multi_client_socket_));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -265,36 +303,37 @@ class ExceptionHandlerServerTest : public testing::Test {
|
|||||||
RunServerThread server_thread_;
|
RunServerThread server_thread_;
|
||||||
ScopedFileHandle sock_to_handler_;
|
ScopedFileHandle sock_to_handler_;
|
||||||
int sock_to_client_;
|
int sock_to_client_;
|
||||||
|
bool use_multi_client_socket_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest);
|
DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerTest);
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, ShutdownWithNoClients) {
|
TEST_P(ExceptionHandlerServerTest, ShutdownWithNoClients) {
|
||||||
ServerThread()->Start();
|
ServerThread()->Start();
|
||||||
Hangup();
|
Hangup();
|
||||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, StopWithClients) {
|
TEST_P(ExceptionHandlerServerTest, StopWithClients) {
|
||||||
ServerThread()->Start();
|
ServerThread()->Start();
|
||||||
Server()->Stop();
|
Server()->Stop();
|
||||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, StopBeforeRun) {
|
TEST_P(ExceptionHandlerServerTest, StopBeforeRun) {
|
||||||
Server()->Stop();
|
Server()->Stop();
|
||||||
ServerThread()->Start();
|
ServerThread()->Start();
|
||||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, MultipleStops) {
|
TEST_P(ExceptionHandlerServerTest, MultipleStops) {
|
||||||
ServerThread()->Start();
|
ServerThread()->Start();
|
||||||
Server()->Stop();
|
Server()->Stop();
|
||||||
Server()->Stop();
|
Server()->Stop();
|
||||||
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
|
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
|
||||||
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
ScopedStopServerAndJoinThread stop_server(Server(), ServerThread());
|
||||||
ServerThread()->Start();
|
ServerThread()->Start();
|
||||||
|
|
||||||
@ -302,25 +341,35 @@ TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDefault) {
|
|||||||
test.Run();
|
test.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
|
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) {
|
||||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace,
|
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace,
|
||||||
false);
|
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,
|
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kUseBroker,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
|
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) {
|
||||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace,
|
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ExceptionHandlerServerTest, RequestCrashDumpError) {
|
TEST_P(ExceptionHandlerServerTest, RequestCrashDumpError) {
|
||||||
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false);
|
ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(ExceptionHandlerServerTestSuite,
|
||||||
|
ExceptionHandlerServerTest,
|
||||||
|
testing::Bool()
|
||||||
|
);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // 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