summaryrefslogtreecommitdiff
path: root/chromium/third_party/crashpad
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-03 13:42:47 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:27:51 +0000
commit8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (patch)
treed29d987c4d7b173cf853279b79a51598f104b403 /chromium/third_party/crashpad
parent830c9e163d31a9180fadca926b3e1d7dfffb5021 (diff)
downloadqtwebengine-chromium-8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec.tar.gz
BASELINE: Update Chromium to 66.0.3359.156
Change-Id: I0c9831ad39911a086b6377b16f995ad75a51e441 Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/third_party/crashpad')
-rw-r--r--chromium/third_party/crashpad/README.chromium3
-rw-r--r--chromium/third_party/crashpad/crashpad/AUTHORS1
-rw-r--r--chromium/third_party/crashpad/crashpad/BUILD.gn22
-rw-r--r--chromium/third_party/crashpad/crashpad/DEPS4
-rw-r--r--chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni21
-rwxr-xr-xchromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py21
-rwxr-xr-xchromium/third_party/crashpad/crashpad/build/run_tests.py135
-rw-r--r--chromium/third_party/crashpad/crashpad/client/BUILD.gn4
-rw-r--r--chromium/third_party/crashpad/crashpad/client/client.gyp1
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_client.h71
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_client_linux.cc204
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc11
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc3
-rw-r--r--chromium/third_party/crashpad/crashpad/client/crashpad_info.cc27
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/BUILD.gn26
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.cc166
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.h35
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc13
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc6
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/compat.gyp2
-rw-r--r--chromium/third_party/crashpad/crashpad/compat/linux/signal.h33
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/BUILD.gn4
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc3
-rw-r--r--chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc9
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context.h96
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc100
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h80
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc116
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc4
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc106
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h40
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc296
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc15
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc6
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc2
-rw-r--r--chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc4
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn62
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h6
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc4
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h50
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc20
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc187
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.h75
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc199
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.cc153
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.h76
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc164
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc10
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h6
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc133
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h34
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc245
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc17
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h4
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/hash_types_test.cc17
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc89
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h67
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc162
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h7
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc171
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc5
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h7
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.cc188
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.h96
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc93
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h33
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc94
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc57
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h11
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h143
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc21
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc14
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h7
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc5
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h7
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.cc95
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h64
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot_test.cc152
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp8
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp18
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc6
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h7
-rw-r--r--chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h5
-rw-r--r--chromium/third_party/crashpad/crashpad/test/BUILD.gn30
-rw-r--r--chromium/third_party/crashpad/crashpad/test/test.gyp3
-rw-r--r--chromium/third_party/crashpad/crashpad/test/test_test.gyp3
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn7
-rw-r--r--chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn8
-rw-r--r--chromium/third_party/crashpad/crashpad/util/BUILD.gn34
-rw-r--r--chromium/third_party/crashpad/crashpad/util/linux/thread_info.h2
-rw-r--r--chromium/third_party/crashpad/crashpad/util/linux/traits.h2
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/address_types.h7
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h3
-rw-r--r--chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc5
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory.cc78
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory.h20
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.cc56
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.h56
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc78
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h9
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory_native.h34
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc13
-rw-r--r--chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc628
-rw-r--r--chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc4
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h7
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc42
-rw-r--r--chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc5
-rw-r--r--chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc12
-rw-r--r--chromium/third_party/crashpad/crashpad/util/util.gyp2
-rw-r--r--chromium/third_party/crashpad/crashpad/util/win/nt_internals.h5
110 files changed, 5311 insertions, 626 deletions
diff --git a/chromium/third_party/crashpad/README.chromium b/chromium/third_party/crashpad/README.chromium
index cb7b8cca5e6..3101da52699 100644
--- a/chromium/third_party/crashpad/README.chromium
+++ b/chromium/third_party/crashpad/README.chromium
@@ -2,7 +2,7 @@ Name: Crashpad
Short Name: crashpad
URL: https://crashpad.chromium.org/
Version: unknown
-Revision: e9d2ca60f7329bf86951a6c9704953303ecaf6a6
+Revision: a8ecdbc973d969a87aaa2efffb1668efb52b799d
License: Apache 2.0
License File: crashpad/LICENSE
Security Critical: yes
@@ -36,3 +36,4 @@ $ git am --3way --message-id -p4 /tmp/patchdir
Local Modifications:
- codereview.settings has been excluded.
+ - thread_log_messages.cc (using ThreadLocalStorage::Slot instead of StaticSlot)
diff --git a/chromium/third_party/crashpad/crashpad/AUTHORS b/chromium/third_party/crashpad/crashpad/AUTHORS
index b1e4ddf9686..eb3534a6f8d 100644
--- a/chromium/third_party/crashpad/crashpad/AUTHORS
+++ b/chromium/third_party/crashpad/crashpad/AUTHORS
@@ -8,3 +8,4 @@
Google Inc.
Opera Software ASA
+Vewd Software AS
diff --git a/chromium/third_party/crashpad/crashpad/BUILD.gn b/chromium/third_party/crashpad/crashpad/BUILD.gn
index 9f386afa478..ffd1c5763a4 100644
--- a/chromium/third_party/crashpad/crashpad/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/BUILD.gn
@@ -19,7 +19,7 @@ config("crashpad_config") {
include_dirs = [ "." ]
}
-if (crashpad_is_in_chromium) {
+if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) {
test("crashpad_tests") {
deps = [
"client:client_test",
@@ -31,7 +31,25 @@ if (crashpad_is_in_chromium) {
"util:util_test",
]
}
-} else {
+
+ if (crashpad_is_in_fuchsia) {
+ import("//build/package.gni")
+ package("crashpad_test") {
+ testonly = true
+ system_image = true
+
+ deps = [
+ ":crashpad_tests",
+ ]
+
+ tests = [
+ {
+ name = "crashpad_tests"
+ },
+ ]
+ }
+ }
+} else if (crashpad_is_standalone) {
test("crashpad_client_test") {
deps = [
"client:client_test",
diff --git a/chromium/third_party/crashpad/crashpad/DEPS b/chromium/third_party/crashpad/crashpad/DEPS
index b197e6565df..6936f9f2014 100644
--- a/chromium/third_party/crashpad/crashpad/DEPS
+++ b/chromium/third_party/crashpad/crashpad/DEPS
@@ -19,7 +19,7 @@ vars = {
deps = {
'buildtools':
Var('chromium_git') + '/chromium/buildtools.git@' +
- '505de88083136eefd056e5ee4ca0f01fe9b33de8',
+ '6fe4a3251488f7af86d64fc25cf442e817cf6133',
'crashpad/third_party/gtest/gtest':
Var('chromium_git') + '/external/github.com/google/googletest@' +
'd175c8bf823e709d570772b038757fadf63bc632',
@@ -28,7 +28,7 @@ deps = {
'5e2b3ddde7cda5eb6bc09a5546a76b00e49d888f',
'crashpad/third_party/mini_chromium/mini_chromium':
Var('chromium_git') + '/chromium/mini_chromium@' +
- 'edfe51ce818e55eae73ab3f144a360e855533888',
+ '3b953302848580cdf23b50402befc0ae09d03ff9',
'crashpad/third_party/zlib/zlib':
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
'13dc246a58e4b72104d35f9b1809af95221ebda7',
diff --git a/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni b/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
index 021bd53aefb..e9ac5713e90 100644
--- a/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
+++ b/chromium/third_party/crashpad/crashpad/build/crashpad_buildconfig.gni
@@ -17,6 +17,14 @@ declare_args() {
# targets to use for dependencies. Valid values are "standalone", "chromium",
# and "fuchsia".
crashpad_dependencies = "standalone"
+
+ if (defined(is_fuchsia_tree) && is_fuchsia_tree) {
+ # Determines various flavors of build configuration, and which concrete
+ # targets to use for dependencies. Valid values are "standalone",
+ # "chromium", and "fuchsia". Defaulted to "fuchsia" because
+ # "is_fuchsia_tree" is set.
+ crashpad_dependencies = "fuchsia"
+ }
}
assert(
@@ -38,15 +46,10 @@ if (crashpad_is_in_chromium) {
crashpad_is_clang = is_clang
} else {
- # Using mini_chromium, but in different locations depending on whether in
- # Fuchsia, or standalone.
- if (crashpad_is_in_fuchsia) {
- import("//third_party/mini_chromium/build/compiler.gni")
- import("//third_party/mini_chromium/build/platform.gni")
- } else {
- import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
- import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
- }
+ # Both standalone and in Fuchsia tree use mini_chromium, and it's mapped into
+ # the same location in both cases.
+ import("../third_party/mini_chromium/mini_chromium/build/compiler.gni")
+ import("../third_party/mini_chromium/mini_chromium/build/platform.gni")
crashpad_is_mac = mini_chromium_is_mac
crashpad_is_win = mini_chromium_is_win
crashpad_is_linux = mini_chromium_is_linux
diff --git a/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py b/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py
index e3f4efc3e1f..135b314ea3a 100755
--- a/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py
+++ b/chromium/third_party/crashpad/crashpad/build/run_fuchsia_qemu.py
@@ -27,6 +27,7 @@ import signal
import subprocess
import sys
import tempfile
+import time
try:
from subprocess import DEVNULL
@@ -57,7 +58,7 @@ def _CheckForTun():
if returncode != 0:
print('To use QEMU with networking on Linux, configure TUN/TAP. See:',
file=sys.stderr)
- print(' https://fuchsia.googlesource.com/magenta/+/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)
return 2
return 0
@@ -77,6 +78,8 @@ def _Start(pid_file):
initrd_path = os.path.join(kernel_data_dir, 'bootdata.bin')
mac_tail = ':'.join('%02x' % random.randint(0, 255) for x in range(3))
+ instance_name = 'crashpad_qemu_' + \
+ ''.join(chr(random.randint(ord('A'), ord('Z'))) for x in range(8))
# These arguments are from the Fuchsia repo in zircon/scripts/run-zircon.
popen = subprocess.Popen([
@@ -93,12 +96,24 @@ def _Start(pid_file):
'-enable-kvm',
'-netdev', 'type=tap,ifname=qemu,script=no,downscript=no,id=net0',
'-device', 'e1000,netdev=net0,mac=52:54:00:' + mac_tail,
- '-append', 'TERM=dumb',
+ '-append', 'TERM=dumb zircon.nodename=' + instance_name,
], stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL)
with open(pid_file, 'wb') as f:
f.write('%d\n' % popen.pid)
+ for i in range(10):
+ netaddr_path = os.path.join(fuchsia_dir, 'sdk', arch, 'tools', 'netaddr')
+ if subprocess.call([netaddr_path, '--nowait', instance_name],
+ stdout=open(os.devnull), stderr=open(os.devnull)) == 0:
+ break
+ time.sleep(.5)
+ else:
+ print('instance did not respond after start', file=sys.stderr)
+ return 1
+
+ return 0
+
def main(args):
if len(args) != 1 or args[0] not in ('start', 'stop'):
@@ -110,7 +125,7 @@ def main(args):
pid_file = os.path.join(tempfile.gettempdir(), 'crashpad_fuchsia_qemu_pid')
_Stop(pid_file)
if command == 'start':
- _Start(pid_file)
+ return _Start(pid_file)
return 0
diff --git a/chromium/third_party/crashpad/crashpad/build/run_tests.py b/chromium/third_party/crashpad/crashpad/build/run_tests.py
index e180a6cbac8..6c2dacf4dd7 100755
--- a/chromium/third_party/crashpad/crashpad/build/run_tests.py
+++ b/chromium/third_party/crashpad/crashpad/build/run_tests.py
@@ -17,6 +17,7 @@
from __future__ import print_function
+import argparse
import os
import pipes
import posixpath
@@ -30,19 +31,54 @@ CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
IS_WINDOWS_HOST = sys.platform.startswith('win')
+def _FindGNFromBinaryDir(binary_dir):
+ """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
+ the path or might be in a non-standard location, particularly on build
+ machines."""
+
+ build_ninja = os.path.join(binary_dir, 'build.ninja')
+ if os.path.isfile(build_ninja):
+ with open(build_ninja, 'rb') as f:
+ # Look for the always-generated regeneration rule of the form:
+ #
+ # rule gn
+ # command = <gn binary> ... arguments ...
+ #
+ # to extract the gn binary's full path.
+ found_rule_gn = False
+ for line in f:
+ if line.strip() == 'rule gn':
+ found_rule_gn = True
+ continue
+ if found_rule_gn:
+ if len(line) == 0 or line[0] != ' ':
+ return None
+ if line.startswith(' command = '):
+ gn_command_line_parts = line.strip().split(' ')
+ if len(gn_command_line_parts) > 2:
+ return os.path.join(binary_dir, gn_command_line_parts[2])
+
+ return None
+
+
def _BinaryDirTargetOS(binary_dir):
"""Returns the apparent target OS of binary_dir, or None if none appear to be
explicitly specified."""
- # Look for a GN “target_os”.
- popen = subprocess.Popen(
- ['gn', 'args', binary_dir, '--list=target_os', '--short'],
- shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull))
- value = popen.communicate()[0]
- if popen.returncode == 0:
- match = re.match('target_os = "(.*)"$', value.decode('utf-8'))
- if match:
- return match.group(1)
+ gn_path = _FindGNFromBinaryDir(binary_dir)
+
+ if gn_path:
+ # Look for a GN “target_os”.
+ popen = subprocess.Popen(
+ [gn_path, 'args', binary_dir, '--list=target_os', '--short'],
+ shell=IS_WINDOWS_HOST, stdout=subprocess.PIPE, stderr=open(os.devnull),
+ cwd=CRASHPAD_DIR)
+ value = popen.communicate()[0]
+ if popen.returncode == 0:
+ match = re.match('target_os = "(.*)"$', value.decode('utf-8'))
+ if match:
+ return match.group(1)
# For GYP with Ninja, look for the appearance of “linux-android” in the path
# to ar. This path is configured by gyp_crashpad_android.py.
@@ -97,7 +133,7 @@ def _EnableVTProcessingOnWindowsConsole():
return True
-def _RunOnAndroidTarget(binary_dir, test, android_device):
+def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line):
local_test_path = os.path.join(binary_dir, test)
MAYBE_UNSUPPORTED_TESTS = (
'crashpad_client_test',
@@ -262,7 +298,7 @@ def _RunOnAndroidTarget(binary_dir, test, android_device):
else:
gtest_color = 'no'
env['GTEST_COLOR'] = gtest_color
- _adb_shell([posixpath.join(device_out_dir, test)], env)
+ _adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line, env)
finally:
_adb_shell(['rm', '-rf', device_temp_dir])
@@ -277,8 +313,10 @@ def _GenerateFuchsiaRuntimeDepsFiles(binary_dir, tests):
targets_file = os.path.abspath(os.path.join(binary_dir, 'targets.txt'))
with open(targets_file, 'wb') as f:
f.write('//:' + '\n//:'.join(tests) + '\n')
+ gn_path = _FindGNFromBinaryDir(binary_dir)
subprocess.check_call(
- ['gn', 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file])
+ [gn_path, 'gen', binary_dir, '--runtime-deps-list-file=' + targets_file],
+ cwd=CRASHPAD_DIR)
def _HandleOutputFromFuchsiaLogListener(process, done_message):
@@ -300,7 +338,7 @@ def _HandleOutputFromFuchsiaLogListener(process, done_message):
return success
-def _RunOnFuchsiaTarget(binary_dir, test, device_name):
+def _RunOnFuchsiaTarget(binary_dir, test, device_name, extra_command_line):
"""Runs the given Fuchsia |test| executable on the given |device_name|. The
device must already be booted.
@@ -330,8 +368,9 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name):
try:
unique_id = uuid.uuid4().hex
- tmp_root = '/tmp/%s_%s/tmp' % (test, unique_id)
- staging_root = '/tmp/%s_%s/pkg' % (test, unique_id)
+ test_root = '/tmp/%s_%s' % (test, unique_id)
+ tmp_root = test_root + '/tmp'
+ staging_root = test_root + '/pkg'
# Make a staging directory tree on the target.
directories_to_create = [tmp_root, '%s/bin' % staging_root,
@@ -341,11 +380,21 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name):
def netcp(local_path):
"""Uses `netcp` to copy a file or directory to the device. Files located
inside the build dir are stored to /pkg/bin, otherwise to /pkg/assets.
+ .so files are stored somewhere completely different, into /boot/lib (!).
+ This is because the loader service does not yet correctly handle the
+ namespace in which the caller is being run, and so can only load .so files
+ from a couple hardcoded locations, the only writable one of which is
+ /boot/lib, so we copy all .so files there. This bug is filed upstream as
+ ZX-1619.
"""
in_binary_dir = local_path.startswith(binary_dir + '/')
if in_binary_dir:
- target_path = os.path.join(
- staging_root, 'bin', local_path[len(binary_dir)+1:])
+ if local_path.endswith('.so'):
+ target_path = os.path.join(
+ '/boot/lib', local_path[len(binary_dir)+1:])
+ else:
+ target_path = os.path.join(
+ staging_root, 'bin', local_path[len(binary_dir)+1:])
else:
target_path = os.path.join(staging_root, 'assets', local_path)
netcp_path = os.path.join(sdk_root, 'tools', 'netcp')
@@ -365,8 +414,9 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name):
done_message = 'TERMINATED: ' + unique_id
namespace_command = [
- 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root, '--',
- staging_root + '/bin/' + test]
+ 'namespace', '/pkg=' + staging_root, '/tmp=' + tmp_root,
+ '--replace-child-argv0=/pkg/bin/' + test, '--',
+ staging_root + '/bin/' + test] + extra_command_line
netruncmd(namespace_command, ['echo', done_message])
success = _HandleOutputFromFuchsiaLogListener(
@@ -374,19 +424,19 @@ def _RunOnFuchsiaTarget(binary_dir, test, device_name):
if not success:
raise subprocess.CalledProcessError(1, test)
finally:
- netruncmd(['rm', '-rf', tmp_root, staging_root])
+ netruncmd(['rm', '-rf', test_root])
# 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
# location in the recipe.
def main(args):
- if len(args) != 1 and len(args) != 2:
- print('usage: run_tests.py <binary_dir> [test_to_run]', file=sys.stderr)
- return 1
-
- binary_dir = args[0]
- single_test = args[1] if len(args) == 2 else None
+ parser = argparse.ArgumentParser(description='Run Crashpad unittests.')
+ parser.add_argument('binary_dir', help='Root of build dir')
+ parser.add_argument('test', nargs='*', help='Specific test(s) to run.')
+ parser.add_argument('--gtest_filter',
+ help='GTest filter applied to GTest binary runs.')
+ args = parser.parse_args()
# 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
@@ -394,13 +444,13 @@ def main(args):
# 64-bit build. This is not a universally valid assumption, and if it’s not
# met, 64-bit tests that require 32-bit build output will disable themselves
# dynamically.
- if (sys.platform == 'win32' and 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):
- binary_dir_32 = binary_dir[:-4]
+ binary_dir_32 = args.binary_dir[:-4]
if os.path.isdir(binary_dir_32):
os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32
- target_os = _BinaryDirTargetOS(binary_dir)
+ target_os = _BinaryDirTargetOS(args.binary_dir)
is_android = target_os == 'android'
is_fuchsia = target_os == 'fuchsia'
@@ -445,15 +495,16 @@ def main(args):
zircon_nodename = devices[0].strip().split()[1]
print('Using autodetected Fuchsia device:', zircon_nodename)
_GenerateFuchsiaRuntimeDepsFiles(
- 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_WINDOWS_HOST:
tests.append('snapshot/win/end_to_end_test.py')
- if single_test:
- if single_test not in tests:
- print('Unrecognized test:', single_test, file=sys.stderr)
- return 3
- tests = [single_test]
+ if args.test:
+ for t in args.test:
+ if t not in tests:
+ print('Unrecognized test:', t, file=sys.stderr)
+ return 3
+ tests = args.test
for test in tests:
print('-' * 80)
@@ -461,14 +512,20 @@ def main(args):
print('-' * 80)
if test.endswith('.py'):
subprocess.check_call(
- [sys.executable, os.path.join(CRASHPAD_DIR, test), binary_dir])
+ [sys.executable, os.path.join(CRASHPAD_DIR, test), args.binary_dir])
else:
+ extra_command_line = []
+ if args.gtest_filter:
+ extra_command_line.append('--gtest_filter=' + args.gtest_filter)
if is_android:
- _RunOnAndroidTarget(binary_dir, test, android_device)
+ _RunOnAndroidTarget(args.binary_dir, test, android_device,
+ extra_command_line)
elif is_fuchsia:
- _RunOnFuchsiaTarget(binary_dir, test, zircon_nodename)
+ _RunOnFuchsiaTarget(args.binary_dir, test, zircon_nodename,
+ extra_command_line)
else:
- subprocess.check_call(os.path.join(binary_dir, test))
+ subprocess.check_call([os.path.join(args.binary_dir, test)] +
+ extra_command_line)
return 0
diff --git a/chromium/third_party/crashpad/crashpad/client/BUILD.gn b/chromium/third_party/crashpad/crashpad/client/BUILD.gn
index f7fd288ffa4..f054e7caab8 100644
--- a/chromium/third_party/crashpad/crashpad/client/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/client/BUILD.gn
@@ -45,6 +45,10 @@ static_library("client") {
]
}
+ if (crashpad_is_linux || crashpad_is_android) {
+ sources += [ "crashpad_client_linux.cc" ]
+ }
+
if (crashpad_is_win) {
sources += [
"crash_report_database_win.cc",
diff --git a/chromium/third_party/crashpad/crashpad/client/client.gyp b/chromium/third_party/crashpad/crashpad/client/client.gyp
index 4b14c4bf166..f75f9c40daa 100644
--- a/chromium/third_party/crashpad/crashpad/client/client.gyp
+++ b/chromium/third_party/crashpad/crashpad/client/client.gyp
@@ -40,6 +40,7 @@
'crash_report_database_mac.mm',
'crash_report_database_win.cc',
'crashpad_client.h',
+ 'crashpad_client_linux.cc',
'crashpad_client_mac.cc',
'crashpad_client_win.cc',
'crashpad_info.cc',
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client.h b/chromium/third_party/crashpad/crashpad/client/crashpad_client.h
index 7799bd9c9f1..7cf2eb8aa1f 100644
--- a/chromium/third_party/crashpad/crashpad/client/crashpad_client.h
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client.h
@@ -105,6 +105,71 @@ class CrashpadClient {
bool restartable,
bool asynchronous_start);
+#if defined(OS_LINUX) || defined(OS_ANDROID) || DOXYGEN
+ //! \brief Installs a signal handler to launch a handler process in reponse to
+ //! a crash.
+ //!
+ //! The handler process will create a crash dump for this process and exit.
+ //!
+ //! \param[in] handler The path to a Crashpad handler executable.
+ //! \param[in] database The path to a Crashpad database. The handler will be
+ //! started with this path as its `--database` argument.
+ //! \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 StartHandlerAtCrash(
+ const base::FilePath& handler,
+ 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 handler process with an initial client.
+ //!
+ //! This method allows a process to launch the handler process on behalf of
+ //! another process.
+ //!
+ //! \param[in] handler The path to a Crashpad handler executable.
+ //! \param[in] database The path to a Crashpad database. The handler will be
+ //! started with this path as its `--database` argument.
+ //! \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.
+ bool StartHandlerForClient(
+ const base::FilePath& handler,
+ 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_LINUX || OS_ANDROID || DOXYGEN
+
#if defined(OS_MACOSX) || DOXYGEN
//! \brief Sets the process’ crash handler to a Mach service registered with
//! the bootstrap server.
@@ -239,9 +304,9 @@ class CrashpadClient {
//! used as the exception code in the exception record.
//!
//! \return `true` if the exception was triggered successfully.
- bool DumpAndCrashTargetProcess(HANDLE process,
- HANDLE blame_thread,
- DWORD exception_code) const;
+ static bool DumpAndCrashTargetProcess(HANDLE process,
+ HANDLE blame_thread,
+ DWORD exception_code);
enum : uint32_t {
//! \brief The exception code (roughly "Client called") used when
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_linux.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
new file mode 100644
index 00000000000..a36d92297a8
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_linux.cc
@@ -0,0 +1,204 @@
+// 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 "client/crashpad_client.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "util/file/file_io.h"
+#include "util/linux/exception_handler_client.h"
+#include "util/linux/exception_information.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/posix/double_fork_and_exec.h"
+#include "util/posix/signals.h"
+
+namespace crashpad {
+
+namespace {
+
+std::string FormatArgumentString(const std::string& name,
+ const std::string& value) {
+ return base::StringPrintf("--%s=%s", name.c_str(), value.c_str());
+}
+
+std::string FormatArgumentInt(const std::string& name, int value) {
+ return base::StringPrintf("--%s=%d", name.c_str(), value);
+}
+
+std::string FormatArgumentAddress(const std::string& name, void* addr) {
+ return base::StringPrintf("--%s=%p", name.c_str(), addr);
+}
+
+void BuildHandlerArgvStrings(
+ const base::FilePath& handler,
+ 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_strings) {
+ argv_strings->clear();
+
+ argv_strings->push_back(handler.value());
+ for (const auto& argument : arguments) {
+ argv_strings->push_back(argument);
+ }
+
+ if (!database.empty()) {
+ argv_strings->push_back(FormatArgumentString("database", database.value()));
+ }
+
+ if (!metrics_dir.empty()) {
+ argv_strings->push_back(
+ FormatArgumentString("metrics-dir", metrics_dir.value()));
+ }
+
+ if (!url.empty()) {
+ argv_strings->push_back(FormatArgumentString("url", url));
+ }
+
+ for (const auto& kv : annotations) {
+ argv_strings->push_back(
+ FormatArgumentString("annotation", kv.first + '=' + kv.second));
+ }
+}
+
+void ConvertArgvStrings(const std::vector<std::string> argv_strings,
+ std::vector<const char*>* argv) {
+ argv->clear();
+ argv->reserve(argv_strings.size() + 1);
+ for (const auto& arg : argv_strings) {
+ argv->push_back(arg.c_str());
+ }
+ argv->push_back(nullptr);
+}
+
+// Launches a single use handler to snapshot this process.
+class LaunchAtCrashHandler {
+ 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);
+ }
+
+ private:
+ LaunchAtCrashHandler() = default;
+
+ ~LaunchAtCrashHandler() = delete;
+
+ static void HandleCrash(int signo, siginfo_t* siginfo, void* context) {
+ auto state = Get();
+ auto exception_information = &state->exception_information_;
+
+ exception_information->siginfo_address =
+ FromPointerCast<decltype(exception_information->siginfo_address)>(
+ siginfo);
+ exception_information->context_address =
+ FromPointerCast<decltype(exception_information->context_address)>(
+ context);
+ exception_information->thread_id = syscall(SYS_gettid);
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ return;
+ }
+ if (pid == 0) {
+ execv(state->argv_[0], const_cast<char* const*>(state->argv_.data()));
+ return;
+ }
+
+ int status;
+ waitpid(pid, &status, 0);
+ }
+
+ std::vector<std::string> argv_strings_;
+ std::vector<const char*> argv_;
+ ExceptionInformation exception_information_;
+
+ DISALLOW_COPY_AND_ASSIGN(LaunchAtCrashHandler);
+};
+
+} // namespace
+
+CrashpadClient::CrashpadClient() {}
+
+CrashpadClient::~CrashpadClient() {}
+
+bool CrashpadClient::StartHandler(
+ const base::FilePath& handler,
+ 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,
+ bool restartable,
+ bool asynchronous_start) {
+ // TODO(jperaza): Implement this after the Android/Linux ExceptionHandlerSever
+ // supports accepting new connections.
+ // https://crashpad.chromium.org/bug/30
+ NOTREACHED();
+ return false;
+}
+
+bool CrashpadClient::StartHandlerAtCrash(
+ const base::FilePath& handler,
+ 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;
+ BuildHandlerArgvStrings(
+ handler, database, metrics_dir, url, annotations, arguments, &argv);
+
+ auto signal_handler = LaunchAtCrashHandler::Get();
+ return signal_handler->Initialize(&argv);
+}
+
+bool CrashpadClient::StartHandlerForClient(
+ const base::FilePath& handler,
+ 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;
+ BuildHandlerArgvStrings(
+ handler, database, metrics_dir, url, annotations, arguments, &argv);
+
+ argv.push_back(FormatArgumentInt("initial-client", socket));
+
+ return DoubleForkAndExec(argv, socket, true, nullptr);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
index a16f8d28d57..4a408206e9f 100644
--- a/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_mac.cc
@@ -331,17 +331,6 @@ class HandlerStarter final : public NotifyServer::DefaultInterface {
}
argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get()));
- // argv_c contains const char* pointers and is terminated by nullptr. argv
- // is required because the pointers in argv_c need to point somewhere, and
- // they can’t point to temporaries such as those returned by
- // FormatArgumentString().
- std::vector<const char*> argv_c;
- argv_c.reserve(argv.size() + 1);
- for (const std::string& argument : argv) {
- argv_c.push_back(argument.c_str());
- }
- argv_c.push_back(nullptr);
-
// When restarting, reset the system default crash handler first. Otherwise,
// the crash exception port in the handler will have been inherited from
// this parent process, which was probably using the exception server now
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc
index 7d9c1fb29c4..dd25c9ef9b4 100644
--- a/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_client_win.cc
@@ -790,9 +790,10 @@ void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) {
UnhandledExceptionHandler(exception_pointers);
}
+// static
bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process,
HANDLE blame_thread,
- DWORD exception_code) const {
+ DWORD exception_code) {
// Confirm we're on Vista or later.
const DWORD version = GetVersion();
const DWORD major_version = LOBYTE(LOWORD(version));
diff --git a/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc b/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc
index d2896f4a7ea..b545a3cd5c7 100644
--- a/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc
+++ b/chromium/third_party/crashpad/crashpad/client/crashpad_info.cc
@@ -51,17 +51,24 @@ static_assert(std::is_standard_layout<CrashpadInfo>::value,
// This may result in a static module initializer in debug-mode builds, but
// because it’s POD, no code should need to run to initialize this under
// release-mode optimization.
+
+// Platforms that use ELF objects need to locate this structure via the dynamic
+// symbol table, so avoid name mangling.
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+extern "C" {
+#endif
+
#if defined(OS_POSIX)
__attribute__((
+#if defined(OS_MACOSX)
// Put the structure in a well-known section name where it can be easily
// found without having to consult the symbol table.
-#if defined(OS_MACOSX)
section(SEG_DATA ",crashpad_info"),
-#elif defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
- section("crashpad_info"),
-#else
-#error Port
+
+ // There's no need to expose this as a public symbol from the symbol table.
+ // All accesses from the outside can locate the well-known section name.
+ visibility("hidden"),
#endif
#if defined(ADDRESS_SANITIZER)
@@ -74,11 +81,7 @@ __attribute__((
#endif // defined(ADDRESS_SANITIZER)
// The “used” attribute prevents the structure from being dead-stripped.
- used,
-
- // There’s no need to expose this as a public symbol from the symbol table.
- // All accesses from the outside can locate the well-known section name.
- visibility("hidden")))
+ used))
#elif defined(OS_WIN)
@@ -93,6 +96,10 @@ __declspec(allocate("CPADinfo"))
CrashpadInfo g_crashpad_info;
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_FUCHSIA)
+} // extern "C"
+#endif
+
// static
CrashpadInfo* CrashpadInfo::GetCrashpadInfo() {
return &g_crashpad_info;
diff --git a/chromium/third_party/crashpad/crashpad/compat/BUILD.gn b/chromium/third_party/crashpad/crashpad/compat/BUILD.gn
index 52eda78bc87..8db7b4dee22 100644
--- a/chromium/third_party/crashpad/crashpad/compat/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/compat/BUILD.gn
@@ -21,10 +21,14 @@ config("compat_config") {
include_dirs += [ "mac" ]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
include_dirs += [ "linux" ]
}
+ if (crashpad_is_android) {
+ include_dirs += [ "android" ]
+ }
+
if (crashpad_is_win) {
include_dirs += [ "win" ]
} else {
@@ -61,13 +65,31 @@ compat_target("compat") {
sources += [ "non_mac/mach/mach.h" ]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/signal.h",
"linux/sys/ptrace.h",
]
}
+ if (crashpad_is_android) {
+ sources += [
+ "android/dlfcn_internal.cc",
+ "android/dlfcn_internal.h",
+ "android/elf.h",
+ "android/linux/elf.h",
+ "android/linux/prctl.h",
+ "android/linux/ptrace.h",
+ "android/sched.h",
+ "android/sys/epoll.cc",
+ "android/sys/epoll.h",
+ "android/sys/mman.cc",
+ "android/sys/mman.h",
+ "android/sys/syscall.h",
+ "android/sys/user.h",
+ ]
+ }
+
if (crashpad_is_win) {
sources += [
"win/getopt.h",
diff --git a/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.cc b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.cc
new file mode 100644
index 00000000000..5d98fe8472a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.cc
@@ -0,0 +1,166 @@
+// 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 "dlfcn_internal.h"
+
+#include <android/api-level.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/syscall.h>
+#include <sys/system_properties.h>
+#include <unistd.h>
+
+#include <mutex>
+
+namespace crashpad {
+namespace internal {
+
+// KitKat supports API levels up to 20.
+#if __ANDROID_API__ < 21
+
+namespace {
+
+class ScopedSigactionRestore {
+ public:
+ ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {}
+
+ ~ScopedSigactionRestore() { Reset(); }
+
+ bool Reset() {
+ bool result = true;
+ if (valid_) {
+ result = sigaction(signo_, &old_action_, nullptr) == 0;
+ if (!result) {
+ PrintErrmsg(errno);
+ }
+ }
+ valid_ = false;
+ signo_ = -1;
+ return result;
+ }
+
+ bool ResetAndInstallHandler(int signo,
+ void (*handler)(int, siginfo_t*, void*)) {
+ Reset();
+
+ struct sigaction act;
+ act.sa_sigaction = handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+ if (sigaction(signo, &act, &old_action_) != 0) {
+ PrintErrmsg(errno);
+ return false;
+ }
+ signo_ = signo;
+ valid_ = true;
+ return true;
+ }
+
+ private:
+ void PrintErrmsg(int err) {
+ char errmsg[256];
+
+ if (strerror_r(err, errmsg, sizeof(errmsg)) != 0) {
+ snprintf(errmsg,
+ sizeof(errmsg),
+ "%s:%d: Couldn't set errmsg for %d: %d",
+ __FILE__,
+ __LINE__,
+ err,
+ errno);
+ return;
+ }
+
+ fprintf(stderr, "%s:%d: sigaction: %s", __FILE__, __LINE__, errmsg);
+ }
+
+ struct sigaction old_action_;
+ int signo_;
+ bool valid_;
+};
+
+bool IsKitKat() {
+ char prop_buf[PROP_VALUE_MAX];
+ int length = __system_property_get("ro.build.version.sdk", prop_buf);
+ if (length <= 0) {
+ fprintf(stderr, "%s:%d: Couldn't get version", __FILE__, __LINE__);
+ // It's safer to assume this is KitKat and execute dlsym with a signal
+ // handler installed.
+ return true;
+ }
+ if (strcmp(prop_buf, "19") == 0 || strcmp(prop_buf, "20") == 0) {
+ return true;
+ }
+ return false;
+}
+
+class ScopedSetTID {
+ public:
+ explicit ScopedSetTID(pid_t* tid) : tid_(tid) { *tid_ = syscall(SYS_gettid); }
+
+ ~ScopedSetTID() { *tid_ = -1; }
+
+ private:
+ pid_t* tid_;
+};
+
+sigjmp_buf dlsym_sigjmp_env;
+
+pid_t dlsym_tid = -1;
+
+void HandleSIGFPE(int signo, siginfo_t* siginfo, void* context) {
+ if (siginfo->si_code != FPE_INTDIV || syscall(SYS_gettid) != dlsym_tid) {
+ return;
+ }
+ siglongjmp(dlsym_sigjmp_env, 1);
+}
+
+} // namespace
+
+void* Dlsym(void* handle, const char* symbol) {
+ if (!IsKitKat()) {
+ return dlsym(handle, symbol);
+ }
+
+ static std::mutex* signal_handler_mutex = new std::mutex();
+ std::lock_guard<std::mutex> lock(*signal_handler_mutex);
+
+ ScopedSetTID set_tid(&dlsym_tid);
+
+ ScopedSigactionRestore sig_restore;
+ if (!sig_restore.ResetAndInstallHandler(SIGFPE, HandleSIGFPE)) {
+ return nullptr;
+ }
+
+ if (sigsetjmp(dlsym_sigjmp_env, 1) != 0) {
+ return nullptr;
+ }
+
+ return dlsym(handle, symbol);
+}
+
+#else
+
+void* Dlsym(void* handle, const char* symbol) {
+ return dlsym(handle, symbol);
+}
+
+#endif // __ANDROID_API__ < 21
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.h b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.h
new file mode 100644
index 00000000000..ed4083dbc82
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/compat/android/dlfcn_internal.h
@@ -0,0 +1,35 @@
+// 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_COMPAT_ANDROID_DLFCN_INTERNAL_H_
+#define CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_
+
+namespace crashpad {
+namespace internal {
+
+//! \brief Provide a wrapper for `dlsym`.
+//!
+//! dlsym on Android KitKat (4.4.*) raises SIGFPE when searching for a
+//! non-existent symbol. This wrapper avoids crashing in this circumstance.
+//! https://code.google.com/p/android/issues/detail?id=61799
+//!
+//! The parameters and return value for this function are the same as for
+//! `dlsym`, but a return value for `dlerror` may not be set in the event of an
+//! error.
+void* Dlsym(void* handle, const char* symbol);
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_
diff --git a/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc b/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc
index 64de763fad6..7fd3bb3781b 100644
--- a/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc
+++ b/chromium/third_party/crashpad/crashpad/compat/android/sys/epoll.cc
@@ -18,18 +18,17 @@
#include <sys/syscall.h>
#include <unistd.h>
+#include "dlfcn_internal.h"
+
#if __ANDROID_API__ < 21
extern "C" {
int epoll_create1(int flags) {
- static const auto epoll_create1_p =
- reinterpret_cast<int (*)(int)>(dlsym(RTLD_DEFAULT, "epoll_create1"));
- if (epoll_create1_p) {
- return epoll_create1_p(flags);
- }
-
- return syscall(SYS_epoll_create1, flags);
+ static const auto epoll_create1_p = reinterpret_cast<int (*)(int)>(
+ crashpad::internal::Dlsym(RTLD_DEFAULT, "epoll_create1"));
+ return epoll_create1_p ? epoll_create1_p(flags)
+ : syscall(SYS_epoll_create1, flags);
}
} // extern "C"
diff --git a/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc b/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc
index f4d722c9d22..4c29a9dff98 100644
--- a/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc
+++ b/chromium/third_party/crashpad/crashpad/compat/android/sys/mman.cc
@@ -19,6 +19,8 @@
#include <stdint.h>
#include <unistd.h>
+#include "dlfcn_internal.h"
+
#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21
// Bionic has provided a wrapper for __mmap2() since the beginning of time. See
@@ -87,8 +89,8 @@ void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) {
// Use the system’s mmap64() wrapper if available. It will be available on
// Android 5.0 (“Lollipop”) and later.
using Mmap64Type = void* (*)(void*, size_t, int, int, int, off64_t);
- static const Mmap64Type mmap64 =
- reinterpret_cast<Mmap64Type>(dlsym(RTLD_DEFAULT, "mmap64"));
+ static const Mmap64Type mmap64 = reinterpret_cast<Mmap64Type>(
+ crashpad::internal::Dlsym(RTLD_DEFAULT, "mmap64"));
if (mmap64) {
return mmap64(addr, size, prot, flags, fd, offset);
}
diff --git a/chromium/third_party/crashpad/crashpad/compat/compat.gyp b/chromium/third_party/crashpad/crashpad/compat/compat.gyp
index d3b785b98c2..c5fe350f613 100644
--- a/chromium/third_party/crashpad/crashpad/compat/compat.gyp
+++ b/chromium/third_party/crashpad/crashpad/compat/compat.gyp
@@ -21,6 +21,8 @@
'target_name': 'crashpad_compat',
'type': 'static_library',
'sources': [
+ 'android/dlfcn_internal.cc',
+ 'android/dlfcn_internal.h',
'android/elf.h',
'android/linux/elf.h',
'android/linux/prctl.h',
diff --git a/chromium/third_party/crashpad/crashpad/compat/linux/signal.h b/chromium/third_party/crashpad/crashpad/compat/linux/signal.h
index 62b9c08636d..4e1cdc1f4b6 100644
--- a/chromium/third_party/crashpad/crashpad/compat/linux/signal.h
+++ b/chromium/third_party/crashpad/crashpad/compat/linux/signal.h
@@ -18,10 +18,43 @@
#include_next <signal.h>
// Missing from glibc and bionic-x86_64
+
#if defined(__x86_64__) || defined(__i386__)
#if !defined(X86_FXSR_MAGIC)
#define X86_FXSR_MAGIC 0x0000
#endif
#endif // __x86_64__ || __i386__
+#if defined(__aarch64__) || defined(__arm__)
+
+#if !defined(FPSIMD_MAGIC)
+#define FPSIMD_MAGIC 0x46508001
+#endif
+
+#if !defined(ESR_MAGIC)
+#define ESR_MAGIC 0x45535201
+#endif
+
+#if !defined(EXTRA_MAGIC)
+#define EXTRA_MAGIC 0x45585401
+#endif
+
+#if !defined(VFP_MAGIC)
+#define VFP_MAGIC 0x56465001
+#endif
+
+#if !defined(CRUNCH_MAGIC)
+#define CRUNCH_MAGIC 0x5065cf03
+#endif
+
+#if !defined(DUMMY_MAGIC)
+#define DUMMY_MAGIC 0xb0d9ed01
+#endif
+
+#if !defined(IWMMXT_MAGIC)
+#define IWMMXT_MAGIC 0x12ef842a
+#endif
+
+#endif // __aarch64__ || __arm__
+
#endif // CRASHPAD_COMPAT_LINUX_SIGNAL_H_
diff --git a/chromium/third_party/crashpad/crashpad/handler/BUILD.gn b/chromium/third_party/crashpad/crashpad/handler/BUILD.gn
index 71037bb04d5..9c337697b60 100644
--- a/chromium/third_party/crashpad/crashpad/handler/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/handler/BUILD.gn
@@ -37,7 +37,7 @@ static_library("handler") {
]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/exception_handler_server.cc",
"linux/exception_handler_server.h",
@@ -89,7 +89,7 @@ source_set("handler_test") {
"minidump_to_upload_parameters_test.cc",
]
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [ "linux/exception_handler_server_test.cc" ]
}
diff --git a/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc b/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc
index 3369c4f8861..6bb55cdaa31 100644
--- a/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc
+++ b/chromium/third_party/crashpad/crashpad/handler/linux/exception_handler_server.cc
@@ -24,6 +24,7 @@
#include <utility>
+#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "base/strings/string_number_conversions.h"
@@ -150,7 +151,7 @@ class PtraceStrategyDeciderImpl : public PtraceStrategyDecider {
if (HaveCapSysPtrace()) {
return Strategy::kDirectPtrace;
}
- // fallthrough
+ FALLTHROUGH;
case PtraceScope::kNoAttach:
LOG(WARNING) << "no ptrace";
return Strategy::kNoPtrace;
diff --git a/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc b/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc
index 2b62aea269a..55c06994ee3 100644
--- a/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc
+++ b/chromium/third_party/crashpad/crashpad/handler/win/crash_other_program.cc
@@ -33,7 +33,7 @@ namespace {
constexpr DWORD kCrashAndDumpTargetExitCode = 0xdeadbea7;
-bool CrashAndDumpTarget(const CrashpadClient& client, HANDLE process) {
+bool CrashAndDumpTarget(HANDLE process) {
DWORD target_pid = GetProcessId(process);
ScopedFileHANDLE thread_snap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
@@ -61,7 +61,7 @@ bool CrashAndDumpTarget(const CrashpadClient& client, HANDLE process) {
PLOG(ERROR) << "OpenThread";
return false;
}
- if (!client.DumpAndCrashTargetProcess(
+ if (!CrashpadClient::DumpAndCrashTargetProcess(
process, thread.get(), kCrashAndDumpTargetExitCode)) {
return false;
}
@@ -109,11 +109,12 @@ int CrashOtherProgram(int argc, wchar_t* argv[]) {
DWORD expect_exit_code;
if (argc == 3 && wcscmp(argv[2], L"noexception") == 0) {
expect_exit_code = CrashpadClient::kTriggeredExceptionCode;
- if (!client.DumpAndCrashTargetProcess(child.process_handle(), 0, 0))
+ if (!CrashpadClient::DumpAndCrashTargetProcess(
+ child.process_handle(), 0, 0))
return EXIT_FAILURE;
} else {
expect_exit_code = kCrashAndDumpTargetExitCode;
- if (!CrashAndDumpTarget(client, child.process_handle())) {
+ if (!CrashAndDumpTarget(child.process_handle())) {
return EXIT_FAILURE;
}
}
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h
index 1226b654aa5..a7328cad0f6 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context.h
@@ -336,6 +336,102 @@ struct alignas(16) MinidumpContextAMD64 {
//! \}
};
+//! \brief 32-bit ARM-specifc flags for MinidumpContextARM::context_flags.
+enum MinidumpContextARMFlags : uint32_t {
+ //! \brief Identifies the context structure as 32-bit ARM.
+ kMinidumpContextARM = 0x40000000,
+
+ //! \brief Indicates the validity of integer regsiters.
+ //!
+ //! Regsiters `r0`-`r15` and `cpsr` are valid.
+ kMinidumpContextARMInteger = kMinidumpContextARM | 0x00000002,
+
+ //! \brief Inidicates the validity of VFP regsiters.
+ //!
+ //! Registers `d0`-`d31` and `fpscr` are valid.
+ kMinidumpContextARMVFP = kMinidumpContextARM | 0x00000004,
+
+ //! \brief Indicates the validity of all registers.
+ kMinidumpContextARMAll = kMinidumpContextARMInteger | kMinidumpContextARMVFP,
+};
+
+//! \brief A 32-bit ARM CPU context (register state) carried in a minidump file.
+struct MinidumpContextARM {
+ //! \brief A bitfield composed of values of #MinidumpContextFlags and
+ //! #MinidumpContextARMFlags.
+ //!
+ //! This field identifies the context structure as a 32-bit ARM CPU context,
+ //! and indicates which other fields in the structure are valid.
+ uint32_t context_flags;
+
+ //! \brief General-purpose registers `r0`-`r15`.
+ uint32_t regs[11];
+ uint32_t fp; // r11
+ uint32_t ip; // r12
+ uint32_t sp; // r13
+ uint32_t lr; // r14
+ uint32_t pc; // r15
+
+ //! \brief Current program status register.
+ uint32_t cpsr;
+
+ //! \brief Floating-point status and control register.
+ uint32_t fpscr;
+
+ //! \brief VFP registers `d0`-`d31`.
+ uint64_t vfp[32];
+
+ //! \brief This space is unused. It is included for compatibility with
+ //! breakpad (which also doesn't use it).
+ uint32_t extra[8];
+};
+
+//! \brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags.
+enum MinidumpContextARM64Flags : uint32_t {
+ //! \brief Identifies the context structure as 64-bit ARM.
+ kMinidumpContextARM64 = 0x80000000,
+
+ //! \brief Indicates the validty of integer registers.
+ //!
+ //! Registers `x0`-`x31`, `pc`, and `cpsr`.
+ kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002,
+
+ //! \brief Indicates the validity of fpsimd registers.
+ //!
+ //! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid.
+ kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004,
+
+ //! \brief Indicates the validity of all registers.
+ kMinidumpContextARM64All =
+ kMinidumpContextARM64Integer | kMinidumpContextARM64Fpsimd,
+};
+
+//! \brief A 64-bit ARM CPU context (register state) carried in a minidump file.
+struct MinidumpContextARM64 {
+ uint64_t context_flags;
+
+ //! \brief General-purpose registers `x0`-`x30`.
+ uint64_t regs[31];
+
+ //! \brief Stack pointer or `x31`.
+ uint64_t sp;
+
+ //! \brief Program counter.
+ uint64_t pc;
+
+ //! \brief Current program status register.
+ uint32_t cpsr;
+
+ //! \brief Floating-point status register.
+ uint32_t fpsr;
+
+ //! \brief Floating-point control register.
+ uint32_t fpcr;
+
+ //! \brief NEON registers `v0`-`v31`.
+ uint128_struct fpsimd[32];
+};
+
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc
index 218d7775a72..2fa2e53e38f 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.cc
@@ -73,6 +73,20 @@ MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) {
break;
}
+ case kCPUArchitectureARM: {
+ context = std::make_unique<MinidumpContextARMWriter>();
+ reinterpret_cast<MinidumpContextARMWriter*>(context.get())
+ ->InitializeFromSnapshot(context_snapshot->arm);
+ break;
+ }
+
+ case kCPUArchitectureARM64: {
+ context = std::make_unique<MinidumpContextARM64Writer>();
+ reinterpret_cast<MinidumpContextARM64Writer*>(context.get())
+ ->InitializeFromSnapshot(context_snapshot->arm64);
+ break;
+ }
+
default: {
LOG(ERROR) << "unknown context architecture "
<< context_snapshot->architecture;
@@ -239,4 +253,90 @@ size_t MinidumpContextAMD64Writer::ContextSize() const {
return sizeof(context_);
}
+MinidumpContextARMWriter::MinidumpContextARMWriter()
+ : MinidumpContextWriter(), context_() {
+ context_.context_flags = kMinidumpContextARM;
+}
+
+MinidumpContextARMWriter::~MinidumpContextARMWriter() = default;
+
+void MinidumpContextARMWriter::InitializeFromSnapshot(
+ const CPUContextARM* context_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(context_.context_flags, kMinidumpContextARM);
+
+ context_.context_flags = kMinidumpContextARMAll;
+
+ static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
+ "GPRS size mismatch");
+ memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
+ context_.fp = context_snapshot->fp;
+ context_.ip = context_snapshot->ip;
+ context_.sp = context_snapshot->sp;
+ context_.lr = context_snapshot->lr;
+ context_.pc = context_snapshot->pc;
+ context_.cpsr = context_snapshot->cpsr;
+
+ context_.fpscr = context_snapshot->vfp_regs.fpscr;
+ static_assert(sizeof(context_.vfp) == sizeof(context_snapshot->vfp_regs.vfp),
+ "VFP size mismatch");
+ memcpy(context_.vfp, context_snapshot->vfp_regs.vfp, sizeof(context_.vfp));
+
+ memset(context_.extra, 0, sizeof(context_.extra));
+}
+
+bool MinidumpContextARMWriter::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+ return file_writer->Write(&context_, sizeof(context_));
+}
+
+size_t MinidumpContextARMWriter::ContextSize() const {
+ DCHECK_GE(state(), kStateFrozen);
+ return sizeof(context_);
+}
+
+MinidumpContextARM64Writer::MinidumpContextARM64Writer()
+ : MinidumpContextWriter(), context_() {
+ context_.context_flags = kMinidumpContextARM64;
+}
+
+MinidumpContextARM64Writer::~MinidumpContextARM64Writer() = default;
+
+void MinidumpContextARM64Writer::InitializeFromSnapshot(
+ const CPUContextARM64* context_snapshot) {
+ DCHECK_EQ(state(), kStateMutable);
+ DCHECK_EQ(context_.context_flags, kMinidumpContextARM64);
+
+ context_.context_flags = kMinidumpContextARM64All;
+
+ static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs),
+ "GPRs size mismatch");
+ memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs));
+ context_.sp = context_snapshot->sp;
+ context_.pc = context_snapshot->pc;
+
+ if (context_snapshot->pstate >
+ std::numeric_limits<decltype(context_.cpsr)>::max()) {
+ LOG(WARNING) << "pstate truncation";
+ }
+ context_.cpsr =
+ static_cast<decltype(context_.cpsr)>(context_snapshot->pstate);
+
+ context_.fpsr = context_snapshot->fpsr;
+ context_.fpcr = context_snapshot->fpcr;
+ static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd),
+ "FPSIMD size mismatch");
+ memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd));
+}
+
+bool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) {
+ DCHECK_EQ(state(), kStateWritable);
+ return file_writer->Write(&context_, sizeof(context_));
+}
+
+size_t MinidumpContextARM64Writer::ContextSize() const {
+ DCHECK_GE(state(), kStateFrozen);
+ return sizeof(context_);
+}
+
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h
index 25d717e585d..fb3b1513fb3 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer.h
@@ -155,6 +155,86 @@ class MinidumpContextAMD64Writer final : public MinidumpContextWriter {
DISALLOW_COPY_AND_ASSIGN(MinidumpContextAMD64Writer);
};
+//! \brief The writer for a MinidumpContextARM structure in a minidump file.
+class MinidumpContextARMWriter final : public MinidumpContextWriter {
+ public:
+ MinidumpContextARMWriter();
+ ~MinidumpContextARMWriter() override;
+
+ //! \brief Initializes the MinidumpContextARM based on \a context_snapshot.
+ //!
+ //! \param[in] context_snapshot The context snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutation of context() may be done before
+ //! calling this method, and it is not normally necessary to alter
+ //! context() after calling this method.
+ void InitializeFromSnapshot(const CPUContextARM* context_snapshot);
+
+ //! \brief Returns a pointer to the context structure that this object will
+ //! write.
+ //!
+ //! \attention This returns a non-`const` pointer to this object’s private
+ //! data so that a caller can populate the context structure directly.
+ //! This is done because providing setter interfaces to each field in the
+ //! context structure would be unwieldy and cumbersome. Care must be taken
+ //! to populate the context structure correctly. The context structure
+ //! must only be modified while this object is in the #kStateMutable
+ //! state.
+ MinidumpContextARM* context() { return &context_; }
+
+ protected:
+ // MinidumpWritable:
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpContextWriter:
+ size_t ContextSize() const override;
+
+ private:
+ MinidumpContextARM context_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpContextARMWriter);
+};
+
+//! \brief The writer for a MinidumpContextARM64 structure in a minidump file.
+class MinidumpContextARM64Writer final : public MinidumpContextWriter {
+ public:
+ MinidumpContextARM64Writer();
+ ~MinidumpContextARM64Writer() override;
+
+ //! \brief Initializes the MinidumpContextARM64 based on \a context_snapshot.
+ //!
+ //! \param[in] context_snapshot The context snapshot to use as source data.
+ //!
+ //! \note Valid in #kStateMutable. No mutation of context() may be done before
+ //! calling this method, and it is not normally necessary to alter
+ //! context() after calling this method.
+ void InitializeFromSnapshot(const CPUContextARM64* context_snapshot);
+
+ //! \brief Returns a pointer to the context structure that this object will
+ //! write.
+ //!
+ //! \attention This returns a non-`const` pointer to this object’s private
+ //! data so that a caller can populate the context structure directly.
+ //! This is done because providing setter interfaces to each field in the
+ //! context structure would be unwieldy and cumbersome. Care must be taken
+ //! to populate the context structure correctly. The context structure
+ //! must only be modified while this object is in the #kStateMutable
+ //! state.
+ MinidumpContextARM64* context() { return &context_; }
+
+ protected:
+ // MinidumpWritable:
+ bool WriteObject(FileWriterInterface* file_writer) override;
+
+ // MinidumpContextWriter:
+ size_t ContextSize() const override;
+
+ private:
+ MinidumpContextARM64 context_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpContextARM64Writer);
+};
+
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc
index 82b4db751ff..0122683cb1d 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_context_writer_test.cc
@@ -28,6 +28,20 @@ namespace crashpad {
namespace test {
namespace {
+template <typename Writer, typename Context>
+void EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) {
+ Writer context_writer;
+ StringFile string_file;
+ EXPECT_TRUE(context_writer.WriteEverything(&string_file));
+ ASSERT_EQ(string_file.string().size(), sizeof(Context));
+
+ const Context* observed =
+ MinidumpWritableAtRVA<Context>(string_file.string(), 0);
+ ASSERT_TRUE(observed);
+
+ expect_context(0, observed, false);
+}
+
TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
StringFile string_file;
@@ -36,16 +50,8 @@ TEST(MinidumpContextWriter, MinidumpContextX86Writer) {
// context.
SCOPED_TRACE("zero");
- MinidumpContextX86Writer context_writer;
-
- EXPECT_TRUE(context_writer.WriteEverything(&string_file));
- ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86));
-
- const MinidumpContextX86* observed =
- MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
- ASSERT_TRUE(observed);
-
- ExpectMinidumpContextX86(0, observed, false);
+ EmptyContextTest<MinidumpContextX86Writer, MinidumpContextX86>(
+ ExpectMinidumpContextX86);
}
{
@@ -85,16 +91,8 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
// context.
SCOPED_TRACE("zero");
- MinidumpContextAMD64Writer context_writer;
-
- EXPECT_TRUE(context_writer.WriteEverything(&string_file));
- ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64));
-
- const MinidumpContextAMD64* observed =
- MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
- ASSERT_TRUE(observed);
-
- ExpectMinidumpContextAMD64(0, observed, false);
+ EmptyContextTest<MinidumpContextAMD64Writer, MinidumpContextAMD64>(
+ ExpectMinidumpContextAMD64);
}
{
@@ -117,48 +115,72 @@ TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) {
}
}
-TEST(MinidumpContextWriter, CreateFromSnapshot_X86) {
- constexpr uint32_t kSeed = 32;
-
- CPUContextX86 context_snapshot_x86;
- CPUContext context_snapshot;
- context_snapshot.x86 = &context_snapshot_x86;
- InitializeCPUContextX86(&context_snapshot, kSeed);
-
+template <typename Writer, typename Context>
+void FromSnapshotTest(const CPUContext& snapshot_context,
+ void (*expect_context)(uint32_t, const Context*, bool),
+ uint32_t seed) {
std::unique_ptr<MinidumpContextWriter> context_writer =
- MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
+ MinidumpContextWriter::CreateFromSnapshot(&snapshot_context);
ASSERT_TRUE(context_writer);
StringFile string_file;
ASSERT_TRUE(context_writer->WriteEverything(&string_file));
- const MinidumpContextX86* observed =
- MinidumpWritableAtRVA<MinidumpContextX86>(string_file.string(), 0);
+ const Context* observed =
+ MinidumpWritableAtRVA<Context>(string_file.string(), 0);
ASSERT_TRUE(observed);
- ExpectMinidumpContextX86(kSeed, observed, true);
+ expect_context(seed, observed, true);
}
-TEST(MinidumpContextWriter, CreateFromSnapshot_AMD64) {
- constexpr uint32_t kSeed = 64;
+TEST(MinidumpContextWriter, X86_FromSnapshot) {
+ constexpr uint32_t kSeed = 32;
+ CPUContextX86 context_x86;
+ CPUContext context;
+ context.x86 = &context_x86;
+ InitializeCPUContextX86(&context, kSeed);
+ FromSnapshotTest<MinidumpContextX86Writer, MinidumpContextX86>(
+ context, ExpectMinidumpContextX86, kSeed);
+}
- CPUContextX86_64 context_snapshot_x86_64;
- CPUContext context_snapshot;
- context_snapshot.x86_64 = &context_snapshot_x86_64;
- InitializeCPUContextX86_64(&context_snapshot, kSeed);
+TEST(MinidumpContextWriter, AMD64_FromSnapshot) {
+ constexpr uint32_t kSeed = 64;
+ CPUContextX86_64 context_x86_64;
+ CPUContext context;
+ context.x86_64 = &context_x86_64;
+ InitializeCPUContextX86_64(&context, kSeed);
+ FromSnapshotTest<MinidumpContextAMD64Writer, MinidumpContextAMD64>(
+ context, ExpectMinidumpContextAMD64, kSeed);
+}
- std::unique_ptr<MinidumpContextWriter> context_writer =
- MinidumpContextWriter::CreateFromSnapshot(&context_snapshot);
- ASSERT_TRUE(context_writer);
+TEST(MinidumpContextWriter, ARM_Zeros) {
+ EmptyContextTest<MinidumpContextARMWriter, MinidumpContextARM>(
+ ExpectMinidumpContextARM);
+}
- StringFile string_file;
- ASSERT_TRUE(context_writer->WriteEverything(&string_file));
+TEST(MinidumpContextWRiter, ARM64_Zeros) {
+ EmptyContextTest<MinidumpContextARM64Writer, MinidumpContextARM64>(
+ ExpectMinidumpContextARM64);
+}
- const MinidumpContextAMD64* observed =
- MinidumpWritableAtRVA<MinidumpContextAMD64>(string_file.string(), 0);
- ASSERT_TRUE(observed);
+TEST(MinidumpContextWriter, ARM_FromSnapshot) {
+ constexpr uint32_t kSeed = 32;
+ CPUContextARM context_arm;
+ CPUContext context;
+ context.arm = &context_arm;
+ InitializeCPUContextARM(&context, kSeed);
+ FromSnapshotTest<MinidumpContextARMWriter, MinidumpContextARM>(
+ context, ExpectMinidumpContextARM, kSeed);
+}
- ExpectMinidumpContextAMD64(kSeed, observed, true);
+TEST(MinidumpContextWriter, ARM64_FromSnapshot) {
+ constexpr uint32_t kSeed = 64;
+ CPUContextARM64 context_arm64;
+ CPUContext context;
+ context.arm64 = &context_arm64;
+ InitializeCPUContextARM64(&context, kSeed);
+ FromSnapshotTest<MinidumpContextARM64Writer, MinidumpContextARM64>(
+ context, ExpectMinidumpContextARM64, kSeed);
}
} // namespace
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc
index 7371039230b..634d3f1af19 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_info_writer_test.cc
@@ -84,14 +84,16 @@ TEST(MinidumpMemoryInfoWriter, OneRegion) {
auto memory_map_region = std::make_unique<TestMemoryMapRegionSnapshot>();
- MINIDUMP_MEMORY_INFO mmi = {0};
+ MINIDUMP_MEMORY_INFO mmi;
mmi.BaseAddress = 0x12340000;
mmi.AllocationBase = 0x12000000;
mmi.AllocationProtect = PAGE_READWRITE;
+ mmi.__alignment1 = 0;
mmi.RegionSize = 0x6000;
mmi.State = MEM_COMMIT;
mmi.Protect = PAGE_NOACCESS;
mmi.Type = MEM_PRIVATE;
+ mmi.__alignment2 = 0;
memory_map_region->SetMindumpMemoryInfo(mmi);
std::vector<const MemoryMapRegionSnapshot*> memory_map;
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc
index f6aca081bbd..0e5f0861247 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.cc
@@ -14,6 +14,8 @@
#include "minidump/minidump_memory_writer.h"
+#include <algorithm>
+#include <iterator>
#include <utility>
#include "base/auto_reset.h"
@@ -37,7 +39,7 @@ SnapshotMinidumpMemoryWriter::~SnapshotMinidumpMemoryWriter() {}
bool SnapshotMinidumpMemoryWriter::MemorySnapshotDelegateRead(void* data,
size_t size) {
DCHECK_EQ(state(), kStateWritable);
- DCHECK_EQ(size, UnderlyingSnapshot().Size());
+ DCHECK_EQ(size, UnderlyingSnapshot()->Size());
return file_writer_->Write(data, size);
}
@@ -89,7 +91,7 @@ size_t SnapshotMinidumpMemoryWriter::Alignment() {
size_t SnapshotMinidumpMemoryWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
- return UnderlyingSnapshot().Size();
+ return UnderlyingSnapshot()->Size();
}
bool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) {
@@ -99,7 +101,7 @@ bool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) {
// object’s own memory_descriptor_ field.
DCHECK_GE(registered_memory_descriptors_.size(), 1u);
- uint64_t base_address = UnderlyingSnapshot().Address();
+ uint64_t base_address = UnderlyingSnapshot()->Address();
decltype(registered_memory_descriptors_[0]->StartOfMemoryRange) local_address;
if (!AssignIfInRange(&local_address, base_address)) {
LOG(ERROR) << "base_address " << base_address << " out of range";
@@ -125,10 +127,11 @@ internal::MinidumpWritable::Phase SnapshotMinidumpMemoryWriter::WritePhase() {
MinidumpMemoryListWriter::MinidumpMemoryListWriter()
: MinidumpStreamWriter(),
- memory_writers_(),
+ non_owned_memory_writers_(),
children_(),
- memory_list_base_() {
-}
+ snapshots_created_during_merge_(),
+ all_memory_writers_(),
+ memory_list_base_() {}
MinidumpMemoryListWriter::~MinidumpMemoryListWriter() {
}
@@ -148,25 +151,80 @@ void MinidumpMemoryListWriter::AddMemory(
std::unique_ptr<SnapshotMinidumpMemoryWriter> memory_writer) {
DCHECK_EQ(state(), kStateMutable);
- AddExtraMemory(memory_writer.get());
children_.push_back(std::move(memory_writer));
}
-void MinidumpMemoryListWriter::AddExtraMemory(
+void MinidumpMemoryListWriter::AddNonOwnedMemory(
SnapshotMinidumpMemoryWriter* memory_writer) {
DCHECK_EQ(state(), kStateMutable);
- memory_writers_.push_back(memory_writer);
+ non_owned_memory_writers_.push_back(memory_writer);
+}
+
+void MinidumpMemoryListWriter::CoalesceOwnedMemory() {
+ if (children_.empty())
+ return;
+
+ DropRangesThatOverlapNonOwned();
+
+ std::sort(children_.begin(),
+ children_.end(),
+ [](const std::unique_ptr<SnapshotMinidumpMemoryWriter>& a_ptr,
+ const std::unique_ptr<SnapshotMinidumpMemoryWriter>& b_ptr) {
+ const MemorySnapshot* a = a_ptr->UnderlyingSnapshot();
+ const MemorySnapshot* b = b_ptr->UnderlyingSnapshot();
+ if (a->Address() == b->Address()) {
+ return a->Size() < b->Size();
+ }
+ return a->Address() < b->Address();
+ });
+
+ // Remove any empty ranges.
+ children_.erase(
+ std::remove_if(children_.begin(),
+ children_.end(),
+ [](const auto& snapshot) {
+ return snapshot->UnderlyingSnapshot()->Size() == 0;
+ }),
+ children_.end());
+
+ std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> all_merged;
+ all_merged.push_back(std::move(children_.front()));
+ for (size_t i = 1; i < children_.size(); ++i) {
+ SnapshotMinidumpMemoryWriter* top = all_merged.back().get();
+ auto& child = children_[i];
+ if (!DetermineMergedRange(
+ child->UnderlyingSnapshot(), top->UnderlyingSnapshot(), nullptr)) {
+ // If it doesn't overlap with the current range, push it.
+ all_merged.push_back(std::move(child));
+ } else {
+ // Otherwise, merge and update the current element.
+ std::unique_ptr<const MemorySnapshot> merged(
+ top->UnderlyingSnapshot()->MergeWithOtherSnapshot(
+ child->UnderlyingSnapshot()));
+ top->SetSnapshot(merged.get());
+ snapshots_created_during_merge_.push_back(std::move(merged));
+ }
+ }
+ std::swap(children_, all_merged);
}
bool MinidumpMemoryListWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
+ CoalesceOwnedMemory();
+
+ std::copy(non_owned_memory_writers_.begin(),
+ non_owned_memory_writers_.end(),
+ std::back_inserter(all_memory_writers_));
+ for (const auto& ptr : children_)
+ all_memory_writers_.push_back(ptr.get());
+
if (!MinidumpStreamWriter::Freeze()) {
return false;
}
- size_t memory_region_count = memory_writers_.size();
+ size_t memory_region_count = all_memory_writers_.size();
CHECK_LE(children_.size(), memory_region_count);
if (!AssignIfInRange(&memory_list_base_.NumberOfMemoryRanges,
@@ -181,15 +239,15 @@ bool MinidumpMemoryListWriter::Freeze() {
size_t MinidumpMemoryListWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
- DCHECK_LE(children_.size(), memory_writers_.size());
+ DCHECK_LE(children_.size(), all_memory_writers_.size());
return sizeof(memory_list_base_) +
- memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
+ all_memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
}
std::vector<internal::MinidumpWritable*> MinidumpMemoryListWriter::Children() {
DCHECK_GE(state(), kStateFrozen);
- DCHECK_LE(children_.size(), memory_writers_.size());
+ DCHECK_LE(children_.size(), all_memory_writers_.size());
std::vector<MinidumpWritable*> children;
for (const auto& child : children_) {
@@ -207,7 +265,8 @@ bool MinidumpMemoryListWriter::WriteObject(FileWriterInterface* file_writer) {
iov.iov_len = sizeof(memory_list_base_);
std::vector<WritableIoVec> iovecs(1, iov);
- for (const SnapshotMinidumpMemoryWriter* memory_writer : memory_writers_) {
+ for (const SnapshotMinidumpMemoryWriter* memory_writer :
+ all_memory_writers_) {
iov.iov_base = memory_writer->MinidumpMemoryDescriptor();
iov.iov_len = sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
iovecs.push_back(iov);
@@ -220,4 +279,23 @@ MinidumpStreamType MinidumpMemoryListWriter::StreamType() const {
return kMinidumpStreamTypeMemoryList;
}
+void MinidumpMemoryListWriter::DropRangesThatOverlapNonOwned() {
+ std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> non_overlapping;
+ non_overlapping.reserve(children_.size());
+ for (auto& child_ptr : children_) {
+ bool overlaps = false;
+ for (const auto* non_owned : non_owned_memory_writers_) {
+ if (DetermineMergedRange(child_ptr->UnderlyingSnapshot(),
+ non_owned->UnderlyingSnapshot(),
+ nullptr)) {
+ overlaps = true;
+ break;
+ }
+ }
+ if (!overlaps)
+ non_overlapping.push_back(std::move(child_ptr));
+ }
+ std::swap(children_, non_overlapping);
+}
+
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h
index 955209076ce..4f4f27f6cde 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer.h
@@ -60,7 +60,15 @@ class SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable,
//! \note Valid in #kStateFrozen or any preceding state.
void RegisterMemoryDescriptor(MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor);
+ //! \brief Sets the underlying memory snapshot. Does not take ownership of \a
+ //! memory_snapshot.
+ void SetSnapshot(const MemorySnapshot* memory_snapshot) {
+ memory_snapshot_ = memory_snapshot;
+ }
+
private:
+ friend class MinidumpMemoryListWriter;
+
// MemorySnapshot::Delegate:
bool MemorySnapshotDelegateRead(void* data, size_t size) override;
@@ -95,7 +103,7 @@ class SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable,
//! \brief Gets the underlying memory snapshot that the memory writer will
//! write to the minidump.
- const MemorySnapshot& UnderlyingSnapshot() const { return *memory_snapshot_; }
+ const MemorySnapshot* UnderlyingSnapshot() const { return memory_snapshot_; }
MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor_;
@@ -147,7 +155,7 @@ class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter {
//! a SnapshotMinidumpMemoryWriter for thread stack memory, is an example.
//!
//! \note Valid in #kStateMutable.
- void AddExtraMemory(SnapshotMinidumpMemoryWriter* memory_writer);
+ void AddNonOwnedMemory(SnapshotMinidumpMemoryWriter* memory_writer);
protected:
// MinidumpWritable:
@@ -159,9 +167,35 @@ class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter {
// MinidumpStreamWriter:
MinidumpStreamType StreamType() const override;
+ //! \brief Merges any overlapping and abutting memory ranges that were added
+ //! via AddFromSnapshot() and AddMemory() into single entries.
+ //!
+ //! This is expected to be called once just before writing, generally from
+ //! Freeze().
+ //!
+ //! This function has the side-effect of merging owned ranges, dropping any
+ //! owned ranges that overlap with non-owned ranges, removing empty ranges,
+ //! and sorting all ranges by address.
+ //!
+ //! Per its name, this coalesces owned memory, however, this is not a complete
+ //! solution for ensuring that no overlapping memory ranges are emitted in the
+ //! minidump. In particular, if AddNonOwnedMemory() is used to add multiple
+ //! overlapping ranges, then overlapping ranges will still be emitted to the
+ //! minidump. Currently, AddNonOwnedMemory() is used only for adding thread
+ //! stacks, so overlapping shouldn't be a problem in practice. For more
+ //! details see https://crashpad.chromium.org/bug/61 and
+ //! https://crrev.com/c/374539.
+ void CoalesceOwnedMemory();
+
private:
- std::vector<SnapshotMinidumpMemoryWriter*> memory_writers_; // weak
+ //! \brief Drops children_ ranges that overlap non_owned_memory_writers_.
+ void DropRangesThatOverlapNonOwned();
+
+ std::vector<SnapshotMinidumpMemoryWriter*> non_owned_memory_writers_; // weak
std::vector<std::unique_ptr<SnapshotMinidumpMemoryWriter>> children_;
+ std::vector<std::unique_ptr<const MemorySnapshot>>
+ snapshots_created_during_merge_;
+ std::vector<SnapshotMinidumpMemoryWriter*> all_memory_writers_; // weak
MINIDUMP_MEMORY_LIST memory_list_base_;
DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryListWriter);
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
index 0290b20dd4c..acff9c299fb 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_memory_writer_test.cc
@@ -241,7 +241,7 @@ TEST(MinidumpMemoryWriter, ExtraMemory) {
std::make_unique<TestMemoryStream>(kBaseAddress0, kSize0, kValue0);
auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
- memory_list_writer->AddExtraMemory(test_memory_stream->memory());
+ memory_list_writer->AddNonOwnedMemory(test_memory_stream->memory());
ASSERT_TRUE(minidump_file_writer.AddStream(std::move(test_memory_stream)));
@@ -305,7 +305,7 @@ TEST(MinidumpMemoryWriter, AddFromSnapshot) {
expect_memory_descriptors[0].Memory.DataSize = 0x1000;
values[0] = 0x01;
- expect_memory_descriptors[1].StartOfMemoryRange = 0x1000;
+ expect_memory_descriptors[1].StartOfMemoryRange = 0x2000;
expect_memory_descriptors[1].Memory.DataSize = 0x2000;
values[1] = 0xf4;
@@ -353,6 +353,298 @@ TEST(MinidumpMemoryWriter, AddFromSnapshot) {
}
}
+TEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) {
+ MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {};
+ uint8_t values[arraysize(expect_memory_descriptors)] = {};
+
+ expect_memory_descriptors[0].StartOfMemoryRange = 0;
+ expect_memory_descriptors[0].Memory.DataSize = 1000;
+ values[0] = 0x01;
+
+ expect_memory_descriptors[1].StartOfMemoryRange = 10000;
+ expect_memory_descriptors[1].Memory.DataSize = 2000;
+ values[1] = 0xf4;
+
+ expect_memory_descriptors[2].StartOfMemoryRange = 0x1111111111111111;
+ expect_memory_descriptors[2].Memory.DataSize = 1024;
+ values[2] = 0x99;
+
+ expect_memory_descriptors[3].StartOfMemoryRange = 0xfedcba9876543210;
+ expect_memory_descriptors[3].Memory.DataSize = 1024;
+ values[3] = 0x88;
+
+ struct {
+ uint64_t base;
+ size_t size;
+ uint8_t value;
+ } snapshots_to_add[] = {
+ // Various overlapping.
+ {0, 500, 0x01},
+ {0, 500, 0x01},
+ {250, 500, 0x01},
+ {600, 400, 0x01},
+
+ // Empty removed.
+ {0, 0, 0xbb},
+ {300, 0, 0xcc},
+ {1000, 0, 0xdd},
+ {12000, 0, 0xee},
+
+ // Abutting.
+ {10000, 500, 0xf4},
+ {10500, 500, 0xf4},
+ {11000, 1000, 0xf4},
+
+ // Large base addresses.
+ { 0xfedcba9876543210, 1024, 0x88 },
+ { 0x1111111111111111, 1024, 0x99 },
+ };
+
+ std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;
+ std::vector<const MemorySnapshot*> memory_snapshots;
+ for (const auto& to_add : snapshots_to_add) {
+ memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());
+ TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();
+ memory_snapshot->SetAddress(to_add.base);
+ memory_snapshot->SetSize(to_add.size);
+ memory_snapshot->SetValue(to_add.value);
+ memory_snapshots.push_back(memory_snapshot);
+ }
+
+ auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
+ memory_list_writer->AddFromSnapshot(memory_snapshots);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(std::move(memory_list_writer));
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetMemoryListStream(string_file.string(), &memory_list, 1));
+
+ ASSERT_EQ(4u, memory_list->NumberOfMemoryRanges);
+
+ for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
+ ExpectMinidumpMemoryDescriptorAndContents(
+ &expect_memory_descriptors[index],
+ &memory_list->MemoryRanges[index],
+ string_file.string(),
+ values[index],
+ index == memory_list->NumberOfMemoryRanges - 1);
+ }
+}
+
+struct TestRange {
+ TestRange(uint64_t base, size_t size) : base(base), size(size) {}
+
+ uint64_t base;
+ size_t size;
+};
+
+// Parses a string spec to build a list of ranges suitable for CoalesceTest().
+std::vector<TestRange> ParseCoalesceSpec(const char* spec) {
+ std::vector<TestRange> result;
+ enum { kNone, kSpace, kDot } state = kNone;
+ const char* range_started_at = nullptr;
+ for (const char* p = spec;; ++p) {
+ EXPECT_TRUE(*p == ' ' || *p == '.' || *p == 0);
+ if (*p == ' ' || *p == 0) {
+ if (state == kDot) {
+ result.push_back(
+ TestRange(range_started_at - spec, p - range_started_at));
+ }
+ state = kSpace;
+ range_started_at = nullptr;
+ } else if (*p == '.') {
+ if (state != kDot) {
+ range_started_at = p;
+ state = kDot;
+ }
+ }
+
+ if (*p == 0)
+ break;
+ }
+
+ return result;
+}
+
+TEST(MinidumpMemoryWriter, CoalesceSpecHelperParse) {
+ const auto empty = ParseCoalesceSpec("");
+ ASSERT_EQ(empty.size(), 0u);
+
+ const auto a = ParseCoalesceSpec("...");
+ ASSERT_EQ(a.size(), 1u);
+ EXPECT_EQ(a[0].base, 0u);
+ EXPECT_EQ(a[0].size, 3u);
+
+ const auto b = ParseCoalesceSpec(" ...");
+ ASSERT_EQ(b.size(), 1u);
+ EXPECT_EQ(b[0].base, 2u);
+ EXPECT_EQ(b[0].size, 3u);
+
+ const auto c = ParseCoalesceSpec(" ... ");
+ ASSERT_EQ(c.size(), 1u);
+ EXPECT_EQ(c[0].base, 2u);
+ EXPECT_EQ(c[0].size, 3u);
+
+ const auto d = ParseCoalesceSpec(" ... ....");
+ ASSERT_EQ(d.size(), 2u);
+ EXPECT_EQ(d[0].base, 2u);
+ EXPECT_EQ(d[0].size, 3u);
+ EXPECT_EQ(d[1].base, 7u);
+ EXPECT_EQ(d[1].size, 4u);
+
+ const auto e = ParseCoalesceSpec(" ... ...... ... ");
+ ASSERT_EQ(e.size(), 3u);
+ EXPECT_EQ(e[0].base, 2u);
+ EXPECT_EQ(e[0].size, 3u);
+ EXPECT_EQ(e[1].base, 7u);
+ EXPECT_EQ(e[1].size, 6u);
+ EXPECT_EQ(e[2].base, 14u);
+ EXPECT_EQ(e[2].size, 3u);
+}
+
+constexpr uint8_t kMemoryValue = 0xcd;
+
+// Builds a coalesce test out of specs of ' ' and '.'. Tests that when the two
+// ranges are added and coalesced, the result is equal to expected.
+void CoalesceTest(const char* r1_spec,
+ const char* r2_spec,
+ const char* expected_spec) {
+ auto r1 = ParseCoalesceSpec(r1_spec);
+ auto r2 = ParseCoalesceSpec(r2_spec);
+ auto expected = ParseCoalesceSpec(expected_spec);
+
+ std::vector<MINIDUMP_MEMORY_DESCRIPTOR> expect_memory_descriptors;
+ for (const auto& range : expected) {
+ MINIDUMP_MEMORY_DESCRIPTOR mmd = {};
+ mmd.StartOfMemoryRange = range.base;
+ mmd.Memory.DataSize = static_cast<uint32_t>(range.size);
+ expect_memory_descriptors.push_back(mmd);
+ }
+
+ std::vector<std::unique_ptr<TestMemorySnapshot>> memory_snapshots_owner;
+ std::vector<const MemorySnapshot*> memory_snapshots;
+
+ const auto add_test_memory_snapshots = [&memory_snapshots_owner,
+ &memory_snapshots](
+ std::vector<TestRange> ranges) {
+ for (const auto& r : ranges) {
+ memory_snapshots_owner.push_back(std::make_unique<TestMemorySnapshot>());
+ TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get();
+ memory_snapshot->SetAddress(r.base);
+ memory_snapshot->SetSize(r.size);
+ memory_snapshot->SetValue(kMemoryValue);
+ memory_snapshots.push_back(memory_snapshot);
+ }
+ };
+ add_test_memory_snapshots(r1);
+ add_test_memory_snapshots(r2);
+
+ auto memory_list_writer = std::make_unique<MinidumpMemoryListWriter>();
+ memory_list_writer->AddFromSnapshot(memory_snapshots);
+
+ MinidumpFileWriter minidump_file_writer;
+ minidump_file_writer.AddStream(std::move(memory_list_writer));
+
+ StringFile string_file;
+ ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file));
+
+ const MINIDUMP_MEMORY_LIST* memory_list = nullptr;
+ ASSERT_NO_FATAL_FAILURE(
+ GetMemoryListStream(string_file.string(), &memory_list, 1));
+
+ ASSERT_EQ(expected.size(), memory_list->NumberOfMemoryRanges);
+
+ for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) {
+ SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index));
+ ExpectMinidumpMemoryDescriptorAndContents(
+ &expect_memory_descriptors[index],
+ &memory_list->MemoryRanges[index],
+ string_file.string(),
+ kMemoryValue,
+ index == memory_list->NumberOfMemoryRanges - 1);
+ }
+}
+
+TEST(MinidumpMemoryWriter, CoalescePairsVariousCases) {
+ // clang-format off
+
+ CoalesceTest(" .........",
+ " .......",
+ /* result */ " ..............");
+
+ CoalesceTest(" .......",
+ " .........",
+ /* result */ " ..............");
+
+ CoalesceTest(" ...",
+ " .........",
+ /* result */ " .........");
+
+ CoalesceTest(" .........",
+ " ......",
+ /* result */ " .........");
+
+ CoalesceTest(" ...",
+ " ........",
+ /* result */ " ........");
+
+ CoalesceTest(" ........",
+ " ...",
+ /* result */ " ........");
+
+ CoalesceTest(" ...",
+ " ........",
+ /* result */ " ........");
+
+ CoalesceTest(" ........",
+ " ...",
+ /* result */ " ........");
+
+ CoalesceTest(" ... ",
+ " ...",
+ /* result */ " ... ...");
+
+ CoalesceTest(" ...",
+ " ... ",
+ /* result */ " ... ...");
+
+ CoalesceTest("...",
+ ".....",
+ /* result */ ".....");
+
+ CoalesceTest("...",
+ " ..",
+ /* result */ ".....");
+
+ CoalesceTest(" .....",
+ " ..",
+ /* result */ " .......");
+
+ CoalesceTest(" ......... ......",
+ " .......",
+ /* result */ " ..................");
+
+ CoalesceTest(" .......",
+ " ......... ......",
+ /* result */ " ..................");
+
+ CoalesceTest(" .....",
+ " ......... ......",
+ /* result */ " ......... ......");
+
+ CoalesceTest(" ......... ....... .... .",
+ " ......... ...... ....",
+ /* result */ " .......................... .......");
+
+ // clang-format on
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
index b8eb7030d9c..382baaf7f6e 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_string_writer_test.cc
@@ -84,12 +84,11 @@ TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) {
const size_t expected_utf16_units_with_nul =
kTestData[index].output_length + 1;
- MINIDUMP_STRING tmp = {0};
+ MINIDUMP_STRING* tmp;
ALLOW_UNUSED_LOCAL(tmp);
const size_t expected_utf16_bytes =
- expected_utf16_units_with_nul * sizeof(tmp.Buffer[0]);
- ASSERT_EQ(string_file.string().size(),
- sizeof(MINIDUMP_STRING) + expected_utf16_bytes);
+ expected_utf16_units_with_nul * sizeof(tmp->Buffer[0]);
+ ASSERT_EQ(string_file.string().size(), sizeof(*tmp) + expected_utf16_bytes);
const MINIDUMP_STRING* minidump_string =
MinidumpStringAtRVA(string_file.string(), 0);
@@ -129,11 +128,11 @@ TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) {
const MINIDUMP_STRING* minidump_string =
MinidumpStringAtRVA(string_file.string(), 0);
EXPECT_TRUE(minidump_string);
- MINIDUMP_STRING tmp = {0};
+ MINIDUMP_STRING* tmp;
ALLOW_UNUSED_LOCAL(tmp);
- EXPECT_EQ(minidump_string->Length,
- string_file.string().size() - sizeof(MINIDUMP_STRING) -
- sizeof(tmp.Buffer[0]));
+ EXPECT_EQ(
+ minidump_string->Length,
+ string_file.string().size() - sizeof(*tmp) - sizeof(tmp->Buffer[0]));
base::string16 output_string =
MinidumpStringAtRVAAsString(string_file.string(), 0);
EXPECT_FALSE(output_string.empty());
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc
index c3d02472a21..99c599c14c9 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_system_info_writer_test.cc
@@ -39,11 +39,11 @@ void GetSystemInfoStream(const std::string& file_contents,
const MINIDUMP_SYSTEM_INFO** system_info,
const MINIDUMP_STRING** csd_version) {
// The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer.
- MINIDUMP_STRING tmp = {0};
+ MINIDUMP_STRING* tmp;
ALLOW_UNUSED_LOCAL(tmp);
- const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp.Buffer[0]);
+ const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp->Buffer[0]);
const size_t kCSDVersionBytesWithNUL =
- kCSDVersionBytes + sizeof(tmp.Buffer[0]);
+ kCSDVersionBytes + sizeof(tmp->Buffer[0]);
constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
constexpr size_t kSystemInfoStreamOffset =
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc
index 4f390ddb7a4..67658e18aad 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer.cc
@@ -173,7 +173,7 @@ void MinidumpThreadListWriter::AddThread(
if (memory_list_writer_) {
SnapshotMinidumpMemoryWriter* stack = thread->Stack();
if (stack) {
- memory_list_writer_->AddExtraMemory(stack);
+ memory_list_writer_->AddNonOwnedMemory(stack);
}
}
diff --git a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
index d9e85aa6594..e95c90447e7 100644
--- a/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
+++ b/chromium/third_party/crashpad/crashpad/minidump/minidump_thread_writer_test.cc
@@ -545,7 +545,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
expect_threads[1].ThreadId = 11;
expect_threads[1].SuspendCount = 12;
expect_threads[1].Priority = 13;
- expect_threads[1].Teb = 0xfedcba9876543210;
+ expect_threads[1].Teb = 0x1111111111111111;
expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType);
context_seeds[1] = 0x40000001;
tebs[1].StartOfMemoryRange = expect_threads[1].Teb;
@@ -554,7 +554,7 @@ void RunInitializeFromSnapshotTest(bool thread_id_collision) {
expect_threads[2].ThreadId = 21;
expect_threads[2].SuspendCount = 22;
expect_threads[2].Priority = 23;
- expect_threads[2].Teb = 0x1111111111111111;
+ expect_threads[2].Teb = 0xfedcba9876543210;
expect_threads[2].Stack.StartOfMemoryRange = 0x3000;
expect_threads[2].Stack.Memory.DataSize = 0x300;
expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType);
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn b/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn
index 665620fd79d..e1d24002e21 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/snapshot/BUILD.gn
@@ -32,6 +32,7 @@ static_library("snapshot") {
"exception_snapshot.h",
"handle_snapshot.cc",
"handle_snapshot.h",
+ "memory_snapshot.cc",
"memory_snapshot.h",
"minidump/minidump_annotation_reader.cc",
"minidump/minidump_annotation_reader.h",
@@ -98,14 +99,10 @@ static_library("snapshot") {
]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [
- "elf/elf_dynamic_array_reader.cc",
- "elf/elf_dynamic_array_reader.h",
- "elf/elf_image_reader.cc",
- "elf/elf_image_reader.h",
- "elf/elf_symbol_table_reader.cc",
- "elf/elf_symbol_table_reader.h",
+ "crashpad_types/image_annotation_reader.cc",
+ "crashpad_types/image_annotation_reader.h",
"linux/cpu_context_linux.cc",
"linux/cpu_context_linux.h",
"linux/debug_rendezvous.cc",
@@ -114,6 +111,8 @@ static_library("snapshot") {
"linux/exception_snapshot_linux.h",
"linux/memory_snapshot_linux.cc",
"linux/memory_snapshot_linux.h",
+ "linux/module_snapshot_linux.cc",
+ "linux/module_snapshot_linux.h",
"linux/process_reader.cc",
"linux/process_reader.h",
"linux/process_snapshot_linux.cc",
@@ -126,6 +125,19 @@ static_library("snapshot") {
]
}
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ sources += [
+ "crashpad_types/crashpad_info_reader.cc",
+ "crashpad_types/crashpad_info_reader.h",
+ "elf/elf_dynamic_array_reader.cc",
+ "elf/elf_dynamic_array_reader.h",
+ "elf/elf_image_reader.cc",
+ "elf/elf_image_reader.h",
+ "elf/elf_symbol_table_reader.cc",
+ "elf/elf_symbol_table_reader.h",
+ ]
+ }
+
if (crashpad_is_win) {
sources += [
"win/capture_memory_delegate_win.cc",
@@ -252,7 +264,7 @@ static_library("test_support") {
config("snapshot_test_link") {
visibility = [ ":*" ]
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
# There’s no way to make the link depend on this file. “inputs” doesn’t have
# the intended effect in a config. https://crbug.com/781858,
# https://crbug.com/796187.
@@ -268,6 +280,7 @@ source_set("snapshot_test") {
sources = [
"cpu_context_test.cc",
+ "memory_snapshot_test.cc",
"minidump/process_snapshot_minidump_test.cc",
]
@@ -283,11 +296,9 @@ source_set("snapshot_test") {
]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [
- "elf/elf_image_reader_test.cc",
- "elf/elf_image_reader_test_note.S",
- "elf/test_exported_symbols.sym",
+ "crashpad_types/image_annotation_reader_test.cc",
"linux/debug_rendezvous_test.cc",
"linux/exception_snapshot_linux_test.cc",
"linux/process_reader_test.cc",
@@ -297,6 +308,15 @@ source_set("snapshot_test") {
sources += [ "crashpad_info_client_options_test.cc" ]
}
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ sources += [
+ "crashpad_types/crashpad_info_reader_test.cc",
+ "elf/elf_image_reader_test.cc",
+ "elf/elf_image_reader_test_note.S",
+ "elf/test_exported_symbols.sym",
+ ]
+ }
+
if (crashpad_is_win) {
sources += [
"api/module_annotations_win_test.cc",
@@ -345,10 +365,14 @@ source_set("snapshot_test") {
]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
libs = [ "dl" ]
}
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ data_deps += [ ":crashpad_snapshot_test_both_dt_hash_styles" ]
+ }
+
if (crashpad_is_win) {
cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union
@@ -396,6 +420,18 @@ loadable_module("crashpad_snapshot_test_module_small") {
]
}
+if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ loadable_module("crashpad_snapshot_test_both_dt_hash_styles") {
+ testonly = true
+ sources = [
+ "hash_types_test.cc",
+ ]
+
+ # This makes `ld` emit both .hash and .gnu.hash sections.
+ ldflags = [ "-Wl,--hash-style=both" ]
+ }
+}
+
if (crashpad_is_mac) {
loadable_module("crashpad_snapshot_test_module_crashy_initializer") {
testonly = true
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h b/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h
index 208e98f275f..6116d4a1bbe 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_architecture.h
@@ -32,6 +32,12 @@ enum CPUArchitecture {
//! \brief x86_64.
kCPUArchitectureX86_64,
+
+ //! \brief 32-bit ARM.
+ kCPUArchitectureARM,
+
+ //! \brief 64-bit ARM.
+ kCPUArchitectureARM64
};
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc
index 5c964480a9b..8d83e638b34 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.cc
@@ -164,6 +164,10 @@ uint64_t CPUContext::InstructionPointer() const {
return x86->eip;
case kCPUArchitectureX86_64:
return x86_64->rip;
+ case kCPUArchitectureARM:
+ return arm->pc;
+ case kCPUArchitectureARM64:
+ return arm64->pc;
default:
NOTREACHED();
return ~0ull;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h
index ba3ac18156a..3160b0eba05 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/cpu_context.h
@@ -18,6 +18,7 @@
#include <stdint.h>
#include "snapshot/cpu_architecture.h"
+#include "util/numeric/int128.h"
namespace crashpad {
@@ -258,6 +259,53 @@ struct CPUContextX86_64 {
uint64_t dr7;
};
+//! \brief A context structure carrying ARM CPU state.
+struct CPUContextARM {
+ uint32_t regs[11];
+ uint32_t fp; // r11
+ uint32_t ip; // r12
+ uint32_t sp; // r13
+ uint32_t lr; // r14
+ uint32_t pc; // r15
+ uint32_t cpsr;
+
+ struct {
+ struct fp_reg {
+ uint32_t sign1 : 1;
+ uint32_t unused : 15;
+ uint32_t sign2 : 1;
+ uint32_t exponent : 14;
+ uint32_t j : 1;
+ uint32_t mantissa1 : 31;
+ uint32_t mantisss0 : 32;
+ } fpregs[8];
+ uint32_t fpsr : 32;
+ uint32_t fpcr : 32;
+ uint8_t type[8];
+ uint32_t init_flag;
+ } fpa_regs;
+
+ struct {
+ uint64_t vfp[32];
+ uint32_t fpscr;
+ } vfp_regs;
+
+ bool have_fpa_regs;
+ bool have_vfp_regs;
+};
+
+//! \brief A context structure carrying ARM64 CPU state.
+struct CPUContextARM64 {
+ uint64_t regs[31];
+ uint64_t sp;
+ uint64_t pc;
+ uint64_t pstate;
+
+ uint128_struct fpsimd[32];
+ uint32_t fpsr;
+ uint32_t fpcr;
+};
+
//! \brief A context structure capable of carrying the context of any supported
//! CPU architecture.
struct CPUContext {
@@ -274,6 +322,8 @@ struct CPUContext {
union {
CPUContextX86* x86;
CPUContextX86_64* x86_64;
+ CPUContextARM* arm;
+ CPUContextARM64* arm64;
};
};
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc
index 1f5a811e44b..d39fada38e8 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_info_size_test_module.cc
@@ -86,7 +86,25 @@ __declspec(allocate("CPADinfo"))
#else // !defined(OS_POSIX) && !defined(OS_WIN)
#error Port
#endif // !defined(OS_POSIX) && !defined(OS_WIN)
-TestCrashpadInfo g_test_crashpad_info = {'CPad', sizeof(TestCrashpadInfo), 1};
+TestCrashpadInfo g_test_crashpad_info = {'CPad',
+ sizeof(TestCrashpadInfo),
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ nullptr,
+ nullptr,
+#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL)
+ nullptr,
+ nullptr,
+#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL
+#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE)
+ {}
+#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE
+};
} // namespace
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc
new file mode 100644
index 00000000000..ade9931bc46
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc
@@ -0,0 +1,187 @@
+// 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 "snapshot/crashpad_types/crashpad_info_reader.h"
+
+#include <type_traits>
+
+#include "build/build_config.h"
+#include "client/crashpad_info.h"
+#include "util/linux/traits.h"
+#include "util/misc/as_underlying_type.h"
+
+namespace crashpad {
+
+namespace {
+
+void UnsetIfNotValidTriState(TriState* value) {
+ switch (AsUnderlyingType(*value)) {
+ case AsUnderlyingType(TriState::kUnset):
+ case AsUnderlyingType(TriState::kEnabled):
+ case AsUnderlyingType(TriState::kDisabled):
+ return;
+ }
+ LOG(WARNING) << "Unsetting invalid TriState " << AsUnderlyingType(*value);
+ *value = TriState::kUnset;
+}
+
+} // namespace
+
+class CrashpadInfoReader::InfoContainer {
+ public:
+ virtual ~InfoContainer() = default;
+
+ virtual bool Read(const ProcessMemoryRange* memory, VMAddress address) = 0;
+
+ protected:
+ InfoContainer() = default;
+};
+
+template <class Traits>
+class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer {
+ public:
+ InfoContainerSpecific() : InfoContainer() {}
+ ~InfoContainerSpecific() override = default;
+
+ bool Read(const ProcessMemoryRange* memory, VMAddress address) override {
+ if (!memory->Read(address,
+ offsetof(decltype(info), size) + sizeof(info.size),
+ &info)) {
+ return false;
+ }
+
+ if (info.signature != CrashpadInfo::kSignature) {
+ LOG(ERROR) << "invalid signature 0x" << std::hex << info.signature;
+ return false;
+ }
+
+ if (!memory->Read(address,
+ std::min(VMSize{info.size}, VMSize{sizeof(info)}),
+ &info)) {
+ return false;
+ }
+
+ if (info.size > sizeof(info)) {
+ LOG(INFO) << "large crashpad info size " << info.size;
+ }
+
+ if (info.version != 1) {
+ LOG(ERROR) << "unexpected version " << info.version;
+ return false;
+ }
+
+ memset(reinterpret_cast<char*>(&info), 0, sizeof(info) - info.size);
+
+ UnsetIfNotValidTriState(&info.crashpad_handler_behavior);
+ UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding);
+ UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory);
+
+ return true;
+ }
+
+ struct {
+ uint32_t signature;
+ uint32_t size;
+ uint32_t version;
+ uint32_t indirectly_referenced_memory_cap;
+ uint32_t padding_0;
+ TriState crashpad_handler_behavior;
+ TriState system_crash_reporter_forwarding;
+ TriState gather_indirectly_referenced_memory;
+ uint8_t padding_1;
+ typename Traits::Address extra_memory_ranges;
+ typename Traits::Address simple_annotations;
+ typename Traits::Address user_data_minidump_stream_head;
+ typename Traits::Address annotations_list;
+ } info;
+
+#if defined(ARCH_CPU_64_BITS)
+#define NATIVE_TRAITS Traits64
+#else
+#define NATIVE_TRAITS Traits32
+#endif
+ static_assert(!std::is_same<Traits, NATIVE_TRAITS>::value ||
+ sizeof(info) == sizeof(CrashpadInfo),
+ "CrashpadInfo size mismtach");
+#undef NATIVE_TRAITS
+};
+
+CrashpadInfoReader::CrashpadInfoReader()
+ : container_(), is_64_bit_(false), initialized_() {}
+
+CrashpadInfoReader::~CrashpadInfoReader() = default;
+
+bool CrashpadInfoReader::Initialize(const ProcessMemoryRange* memory,
+ VMAddress address) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ is_64_bit_ = memory->Is64Bit();
+
+ std::unique_ptr<InfoContainer> new_container;
+ if (is_64_bit_) {
+ new_container = std::make_unique<InfoContainerSpecific<Traits64>>();
+ } else {
+ new_container = std::make_unique<InfoContainerSpecific<Traits32>>();
+ }
+
+ if (!new_container->Read(memory, address)) {
+ return false;
+ }
+ container_ = std::move(new_container);
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+#define GET_MEMBER(name) \
+ (is_64_bit_ \
+ ? reinterpret_cast<InfoContainerSpecific<Traits64>*>(container_.get()) \
+ ->info.name \
+ : reinterpret_cast<InfoContainerSpecific<Traits32>*>(container_.get()) \
+ ->info.name)
+
+#define DEFINE_GETTER(type, method, member) \
+ type CrashpadInfoReader::method() { \
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_); \
+ return GET_MEMBER(member); \
+ }
+
+DEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior);
+
+DEFINE_GETTER(TriState,
+ SystemCrashReporterForwarding,
+ system_crash_reporter_forwarding);
+
+DEFINE_GETTER(TriState,
+ GatherIndirectlyReferencedMemory,
+ gather_indirectly_referenced_memory);
+
+DEFINE_GETTER(uint32_t,
+ IndirectlyReferencedMemoryCap,
+ indirectly_referenced_memory_cap);
+
+DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges);
+
+DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations);
+
+DEFINE_GETTER(VMAddress, AnnotationsList, annotations_list);
+
+DEFINE_GETTER(VMAddress,
+ UserDataMinidumpStreamHead,
+ user_data_minidump_stream_head);
+
+#undef DEFINE_GETTER
+#undef GET_MEMBER
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.h
new file mode 100644
index 00000000000..5f2352efae0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader.h
@@ -0,0 +1,75 @@
+// 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_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_
+#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/macros.h"
+#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/misc/tri_state.h"
+#include "util/process/process_memory_range.h"
+
+namespace crashpad {
+
+//! \brief Reads CrashpadInfo structs from another process via a
+//! ProcessMemoryRange.
+class CrashpadInfoReader {
+ public:
+ CrashpadInfoReader();
+ ~CrashpadInfoReader();
+
+ //! \brief Initializes this object.
+ //!
+ //! This method must be successfully called bfore any other method in this
+ //! class.
+ //!
+ //! \param[in] memory The reader for the remote process.
+ //! \param[in] address The address in the remote process' address space of a
+ //! CrashpadInfo struct.
+ //! \return `true` on success. `false` on failure with a message logged.
+ bool Initialize(const ProcessMemoryRange* memory, VMAddress address);
+
+ //! \{
+ //! \see CrashpadInfo
+ TriState CrashpadHandlerBehavior();
+ TriState SystemCrashReporterForwarding();
+ TriState GatherIndirectlyReferencedMemory();
+ uint32_t IndirectlyReferencedMemoryCap();
+ VMAddress ExtraMemoryRanges();
+ VMAddress SimpleAnnotations();
+ VMAddress AnnotationsList();
+ VMAddress UserDataMinidumpStreamHead();
+ //! \}
+
+ private:
+ class InfoContainer;
+
+ template <typename Traits>
+ class InfoContainerSpecific;
+
+ std::unique_ptr<InfoContainer> container_;
+ bool is_64_bit_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashpadInfoReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc
new file mode 100644
index 00000000000..73564177ab5
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc
@@ -0,0 +1,199 @@
+// 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 "snapshot/crashpad_types/crashpad_info_reader.h"
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "build/build_config.h"
+#include "client/annotation_list.h"
+#include "client/crashpad_info.h"
+#include "client/simple_address_range_bag.h"
+#include "client/simple_string_dictionary.h"
+#include "gtest/gtest.h"
+#include "test/multiprocess_exec.h"
+#include "test/process_type.h"
+#include "util/file/file_io.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/process/process_memory_native.h"
+
+#if defined(OS_FUCHSIA)
+#include <zircon/process.h>
+#endif
+
+namespace crashpad {
+namespace test {
+namespace {
+
+constexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled;
+constexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled;
+constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset;
+
+constexpr uint32_t kIndirectlyReferencedMemoryCap = 42;
+
+class CrashpadInfoTestDataSetup {
+ public:
+ CrashpadInfoTestDataSetup() {
+ CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo();
+
+ info->set_extra_memory_ranges(&extra_memory_);
+ info->set_simple_annotations(&simple_annotations_);
+ info->set_annotations_list(&annotation_list_);
+ info->set_crashpad_handler_behavior(kCrashpadHandlerBehavior);
+ info->set_system_crash_reporter_forwarding(kSystemCrashReporterForwarding);
+ info->set_gather_indirectly_referenced_memory(
+ kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap);
+ }
+
+ void GetAddresses(VMAddress* info_address,
+ VMAddress* extra_memory_address,
+ VMAddress* simple_annotations_address,
+ VMAddress* annotations_list_address) {
+ *info_address = FromPointerCast<VMAddress>(CrashpadInfo::GetCrashpadInfo());
+ *extra_memory_address = FromPointerCast<VMAddress>(&extra_memory_);
+ *simple_annotations_address =
+ FromPointerCast<VMAddress>(&simple_annotations_);
+ *annotations_list_address = FromPointerCast<VMAddress>(&annotation_list_);
+ }
+
+ private:
+ SimpleAddressRangeBag extra_memory_;
+ SimpleStringDictionary simple_annotations_;
+ AnnotationList annotation_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(CrashpadInfoTestDataSetup);
+};
+
+void ExpectCrashpadInfo(ProcessType process,
+ bool is_64_bit,
+ VMAddress info_address,
+ VMAddress extra_memory_address,
+ VMAddress simple_annotations_address,
+ VMAddress annotations_list_address) {
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(process));
+
+ ProcessMemoryRange range;
+ ASSERT_TRUE(range.Initialize(&memory, is_64_bit));
+
+ CrashpadInfoReader reader;
+ ASSERT_TRUE(reader.Initialize(&range, info_address));
+ EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior);
+ EXPECT_EQ(reader.SystemCrashReporterForwarding(),
+ kSystemCrashReporterForwarding);
+ EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(),
+ kGatherIndirectlyReferencedMemory);
+ EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(),
+ kIndirectlyReferencedMemoryCap);
+ EXPECT_EQ(reader.ExtraMemoryRanges(), extra_memory_address);
+ EXPECT_EQ(reader.SimpleAnnotations(), simple_annotations_address);
+ EXPECT_EQ(reader.AnnotationsList(), annotations_list_address);
+}
+
+TEST(CrashpadInfoReader, ReadFromSelf) {
+#if defined(ARCH_CPU_64_BITS)
+ constexpr bool am_64_bit = true;
+#else
+ constexpr bool am_64_bit = false;
+#endif
+
+ CrashpadInfoTestDataSetup test_data_setup;
+ VMAddress info_address;
+ VMAddress extra_memory_address;
+ VMAddress simple_annotations_address;
+ VMAddress annotations_list_address;
+ test_data_setup.GetAddresses(&info_address,
+ &extra_memory_address,
+ &simple_annotations_address,
+ &annotations_list_address);
+ ExpectCrashpadInfo(GetSelfProcess(),
+ am_64_bit,
+ info_address,
+ extra_memory_address,
+ simple_annotations_address,
+ annotations_list_address);
+}
+
+CRASHPAD_CHILD_TEST_MAIN(ReadFromChildTestMain) {
+ CrashpadInfoTestDataSetup test_data_setup;
+ VMAddress info_address;
+ VMAddress extra_memory_address;
+ VMAddress simple_annotations_address;
+ VMAddress annotations_list_address;
+ test_data_setup.GetAddresses(&info_address,
+ &extra_memory_address,
+ &simple_annotations_address,
+ &annotations_list_address);
+
+ FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+ CheckedWriteFile(out, &info_address, sizeof(info_address));
+ CheckedWriteFile(out, &extra_memory_address, sizeof(extra_memory_address));
+ CheckedWriteFile(
+ out, &simple_annotations_address, sizeof(simple_annotations_address));
+ CheckedWriteFile(
+ out, &annotations_list_address, sizeof(annotations_list_address));
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
+}
+
+class ReadFromChildTest : public MultiprocessExec {
+ public:
+ ReadFromChildTest() : MultiprocessExec() {
+ SetChildTestMainFunction("ReadFromChildTestMain");
+ }
+
+ ~ReadFromChildTest() = default;
+
+ private:
+ void MultiprocessParent() {
+#if defined(ARCH_CPU_64_BITS)
+ constexpr bool am_64_bit = true;
+#else
+ constexpr bool am_64_bit = false;
+#endif
+
+ VMAddress info_address;
+ VMAddress extra_memory_address;
+ VMAddress simple_annotations_address;
+ VMAddress annotations_list_address;
+ ASSERT_TRUE(
+ ReadFileExactly(ReadPipeHandle(), &info_address, sizeof(info_address)));
+ ASSERT_TRUE(ReadFileExactly(
+ ReadPipeHandle(), &extra_memory_address, sizeof(extra_memory_address)));
+ ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(),
+ &simple_annotations_address,
+ sizeof(simple_annotations_address)));
+ ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(),
+ &annotations_list_address,
+ sizeof(annotations_list_address)));
+ ExpectCrashpadInfo(ChildProcess(),
+ am_64_bit,
+ info_address,
+ extra_memory_address,
+ simple_annotations_address,
+ annotations_list_address);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest);
+};
+
+TEST(CrashpadInfoReader, ReadFromChild) {
+ ReadFromChildTest test;
+ test.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.cc
new file mode 100644
index 00000000000..bd904979f19
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.cc
@@ -0,0 +1,153 @@
+// 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 "snapshot/crashpad_types/image_annotation_reader.h"
+
+#include <string.h>
+#include <sys/types.h>
+
+#include <algorithm>
+#include <utility>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "client/annotation.h"
+#include "client/annotation_list.h"
+#include "client/simple_string_dictionary.h"
+#include "snapshot/snapshot_constants.h"
+#include "util/linux/traits.h"
+
+namespace crashpad {
+
+namespace process_types {
+
+template <class Traits>
+struct Annotation {
+ typename Traits::Address link_node;
+ typename Traits::Address name;
+ typename Traits::Address value;
+ uint32_t size;
+ uint16_t type;
+};
+
+template <class Traits>
+struct AnnotationList {
+ typename Traits::Address tail_pointer;
+ Annotation<Traits> head;
+ Annotation<Traits> tail;
+};
+
+} // namespace process_types
+
+#if defined(ARCH_CPU_64_BITS)
+#define NATIVE_TRAITS Traits64
+#else
+#define NATIVE_TRAITS Traits32
+#endif // ARCH_CPU_64_BITS
+
+static_assert(sizeof(process_types::Annotation<NATIVE_TRAITS>) ==
+ sizeof(Annotation),
+ "Annotation size mismatch");
+
+static_assert(sizeof(process_types::AnnotationList<NATIVE_TRAITS>) ==
+ sizeof(AnnotationList),
+ "AnnotationList size mismatch");
+
+#undef NATIVE_TRAITS
+
+ImageAnnotationReader::ImageAnnotationReader(const ProcessMemoryRange* memory)
+ : memory_(memory) {}
+
+ImageAnnotationReader::~ImageAnnotationReader() = default;
+
+bool ImageAnnotationReader::SimpleMap(
+ VMAddress address,
+ std::map<std::string, std::string>* annotations) const {
+ std::vector<SimpleStringDictionary::Entry> simple_annotations(
+ SimpleStringDictionary::num_entries);
+
+ if (!memory_->Read(address,
+ simple_annotations.size() * sizeof(simple_annotations[0]),
+ &simple_annotations[0])) {
+ return false;
+ }
+
+ for (const auto& entry : simple_annotations) {
+ size_t key_length = strnlen(entry.key, sizeof(entry.key));
+ if (key_length) {
+ std::string key(entry.key, key_length);
+ std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));
+ if (!annotations->insert(std::make_pair(key, value)).second) {
+ LOG(WARNING) << "duplicate simple annotation " << key << " " << value;
+ }
+ }
+ }
+ return true;
+}
+
+bool ImageAnnotationReader::AnnotationsList(
+ VMAddress address,
+ std::vector<AnnotationSnapshot>* annotations) const {
+ return memory_->Is64Bit()
+ ? ReadAnnotationList<Traits64>(address, annotations)
+ : ReadAnnotationList<Traits32>(address, annotations);
+}
+
+template <class Traits>
+bool ImageAnnotationReader::ReadAnnotationList(
+ VMAddress address,
+ std::vector<AnnotationSnapshot>* annotations) const {
+ process_types::AnnotationList<Traits> annotation_list;
+ if (!memory_->Read(address, sizeof(annotation_list), &annotation_list)) {
+ LOG(ERROR) << "could not read annotation list";
+ return false;
+ }
+
+ process_types::Annotation<Traits> current = annotation_list.head;
+ for (size_t index = 0; current.link_node != annotation_list.tail_pointer &&
+ index < kMaxNumberOfAnnotations;
+ ++index) {
+ if (!memory_->Read(current.link_node, sizeof(current), &current)) {
+ LOG(ERROR) << "could not read annotation at index " << index;
+ return false;
+ }
+
+ if (current.size == 0) {
+ continue;
+ }
+
+ AnnotationSnapshot snapshot;
+ snapshot.type = current.type;
+
+ if (!memory_->ReadCStringSizeLimited(
+ current.name, Annotation::kNameMaxLength, &snapshot.name)) {
+ LOG(WARNING) << "could not read annotation name at index " << index;
+ continue;
+ }
+
+ size_t value_length =
+ std::min(static_cast<size_t>(current.size), Annotation::kValueMaxSize);
+ snapshot.value.resize(value_length);
+ if (!memory_->Read(current.value, value_length, snapshot.value.data())) {
+ LOG(WARNING) << "could not read annotation value at index " << index;
+ continue;
+ }
+
+ annotations->push_back(std::move(snapshot));
+ }
+
+ return true;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.h
new file mode 100644
index 00000000000..e425bef6d7d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader.h
@@ -0,0 +1,76 @@
+// 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_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_
+#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "snapshot/annotation_snapshot.h"
+#include "util/misc/address_types.h"
+#include "util/process/process_memory_range.h"
+
+namespace crashpad {
+
+//! \brief Reads Annotations from another process via a ProcessMemoryRange.
+//!
+//! These annotations are stored for the benefit of crash reporters, and provide
+//! information thought to be potentially useful for crash analysis.
+class ImageAnnotationReader {
+ public:
+ //! \brief Constructs the object.
+ //!
+ //! \param[in] memory A memory reader for the remote process.
+ explicit ImageAnnotationReader(const ProcessMemoryRange* memory);
+
+ ~ImageAnnotationReader();
+
+ //! \brief Reads annotations that are organized as key-value pairs, where all
+ //! keys and values are strings.
+ //!
+ //! \param[in] address The address in the target process' address space of a
+ //! SimpleStringDictionary containing the annotations to read.
+ //! \param[out] annotations The annotations read, valid if this method
+ //! returns `true`.
+ //! \return `true` on success. `false` on failure with a message logged.
+ bool SimpleMap(VMAddress address,
+ std::map<std::string, std::string>* annotations) const;
+
+ //! \brief Reads the module's annotations that are organized as a list of
+ //! typed annotation objects.
+ //!
+ //! \param[in] address The address in the target process' address space of an
+ //! AnnotationList.
+ //! \param[out] annotations The annotations read, valid if this method returns
+ //! `true`.
+ //! \return `true` on success. `false` on failure with a message logged.
+ bool AnnotationsList(VMAddress,
+ std::vector<AnnotationSnapshot>* annotations) const;
+
+ private:
+ template <class Traits>
+ bool ReadAnnotationList(VMAddress address,
+ std::vector<AnnotationSnapshot>* annotations) const;
+
+ const ProcessMemoryRange* memory_; // weak
+
+ DISALLOW_COPY_AND_ASSIGN(ImageAnnotationReader);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc
new file mode 100644
index 00000000000..b0e635ff75b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc
@@ -0,0 +1,164 @@
+// 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 "snapshot/crashpad_types/image_annotation_reader.h"
+
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "client/annotation.h"
+#include "client/annotation_list.h"
+#include "client/simple_string_dictionary.h"
+#include "gtest/gtest.h"
+#include "test/multiprocess.h"
+#include "util/file/file_io.h"
+#include "util/misc/as_underlying_type.h"
+#include "util/misc/from_pointer_cast.h"
+#include "util/process/process_memory_linux.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+void ExpectSimpleMap(const std::map<std::string, std::string>& map,
+ const SimpleStringDictionary& expected_map) {
+ EXPECT_EQ(map.size(), expected_map.GetCount());
+ for (const auto& pair : map) {
+ EXPECT_EQ(pair.second, expected_map.GetValueForKey(pair.first));
+ }
+}
+
+void ExpectAnnotationList(const std::vector<AnnotationSnapshot>& list,
+ AnnotationList& expected_list) {
+ size_t index = 0;
+ for (const Annotation* expected_annotation : expected_list) {
+ const AnnotationSnapshot& annotation = list[index++];
+ EXPECT_EQ(annotation.name, expected_annotation->name());
+ EXPECT_EQ(annotation.type, AsUnderlyingType(expected_annotation->type()));
+ EXPECT_EQ(annotation.value.size(), expected_annotation->size());
+ EXPECT_EQ(memcmp(annotation.value.data(),
+ expected_annotation->value(),
+ std::min(VMSize{annotation.value.size()},
+ VMSize{expected_annotation->size()})),
+ 0);
+ }
+}
+
+class AnnotationTest {
+ public:
+ AnnotationTest()
+ : expected_simple_map_(),
+ test_annotations_(),
+ expected_annotation_list_() {
+ expected_simple_map_.SetKeyValue("key", "value");
+ expected_simple_map_.SetKeyValue("key2", "value2");
+
+ static constexpr char kAnnotationName[] = "test annotation";
+ static constexpr char kAnnotationValue[] = "test annotation value";
+ test_annotations_.push_back(std::make_unique<Annotation>(
+ Annotation::Type::kString,
+ kAnnotationName,
+ reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue))));
+ test_annotations_.back()->SetSize(sizeof(kAnnotationValue));
+ expected_annotation_list_.Add(test_annotations_.back().get());
+
+ static constexpr char kAnnotationName2[] = "test annotation2";
+ static constexpr char kAnnotationValue2[] = "test annotation value2";
+ test_annotations_.push_back(std::make_unique<Annotation>(
+ Annotation::Type::kString,
+ kAnnotationName2,
+ reinterpret_cast<void*>(const_cast<char*>(kAnnotationValue2))));
+ test_annotations_.back()->SetSize(sizeof(kAnnotationValue2));
+ expected_annotation_list_.Add(test_annotations_.back().get());
+ }
+
+ ~AnnotationTest() = default;
+
+ void ExpectAnnotations(pid_t pid, bool is_64_bit) {
+ ProcessMemoryLinux memory;
+ ASSERT_TRUE(memory.Initialize(pid));
+
+ ProcessMemoryRange range;
+ ASSERT_TRUE(range.Initialize(&memory, is_64_bit));
+
+ ImageAnnotationReader reader(&range);
+
+ std::map<std::string, std::string> simple_map;
+ ASSERT_TRUE(reader.SimpleMap(
+ FromPointerCast<VMAddress>(&expected_simple_map_), &simple_map));
+ ExpectSimpleMap(simple_map, expected_simple_map_);
+
+ std::vector<AnnotationSnapshot> annotation_list;
+ ASSERT_TRUE(reader.AnnotationsList(
+ FromPointerCast<VMAddress>(&expected_annotation_list_),
+ &annotation_list));
+ ExpectAnnotationList(annotation_list, expected_annotation_list_);
+ }
+
+ private:
+ SimpleStringDictionary expected_simple_map_;
+ std::vector<std::unique_ptr<Annotation>> test_annotations_;
+ AnnotationList expected_annotation_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(AnnotationTest);
+};
+
+TEST(ImageAnnotationReader, ReadFromSelf) {
+ AnnotationTest test;
+
+#if defined(ARCH_CPU_64_BITS)
+ constexpr bool am_64_bit = true;
+#else
+ constexpr bool am_64_bit = false;
+#endif
+
+ test.ExpectAnnotations(getpid(), am_64_bit);
+}
+
+class ReadFromChildTest : public Multiprocess {
+ public:
+ ReadFromChildTest() : Multiprocess(), annotation_test_() {}
+
+ ~ReadFromChildTest() {}
+
+ private:
+ void MultiprocessParent() {
+#if defined(ARCH_CPU_64_BITS)
+ constexpr bool am_64_bit = true;
+#else
+ constexpr bool am_64_bit = false;
+#endif
+ annotation_test_.ExpectAnnotations(ChildPID(), am_64_bit);
+ }
+
+ void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }
+
+ AnnotationTest annotation_test_;
+
+ DISALLOW_COPY_AND_ASSIGN(ReadFromChildTest);
+};
+
+TEST(ImageAnnotationReader, ReadFromChild) {
+ ReadFromChildTest test;
+ test.Run();
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc
index 681b1b570ea..9a44e12eaa1 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.cc
@@ -16,6 +16,8 @@
#include <elf.h>
+#include <type_traits>
+
#include "util/stdlib/map_insert.h"
namespace crashpad {
@@ -48,8 +50,14 @@ bool Read(const ProcessMemoryRange& memory,
// Skip these entries for now.
break;
default:
+ static_assert(std::is_unsigned<decltype(entry.d_un.d_ptr)>::value,
+ "type must be unsigned");
+ static_assert(static_cast<void*>(&entry.d_un.d_ptr) ==
+ static_cast<void*>(&entry.d_un.d_val) &&
+ sizeof(entry.d_un.d_ptr) == sizeof(entry.d_un.d_val),
+ "d_ptr and d_val must be aliases");
if (!MapInsertOrReplace(
- &local_values, entry.d_tag, entry.d_un.d_val, nullptr)) {
+ &local_values, entry.d_tag, entry.d_un.d_ptr, nullptr)) {
LOG(ERROR) << "duplicate dynamic array entry";
return false;
}
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h
index 36628a0b854..e80a8cbc556 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_dynamic_array_reader.h
@@ -50,13 +50,15 @@ class ElfDynamicArrayReader {
//!
//! \param[in] tag Specifies which value should be retrieved. The possible
//! values for this parameter are the `DT_*` values from `<elf.h>`.
+ //! \param[in] log Specifies whether an error should be logged if \a tag is
+ //! not found.
//! \param[out] value The value, casted to an appropriate type, if found.
//! \return `true` if the value is found.
template <typename V>
- bool GetValue(uint64_t tag, V* value) {
+ bool GetValue(uint64_t tag, bool log, V* value) {
auto iter = values_.find(tag);
if (iter == values_.end()) {
- LOG(ERROR) << "tag not found";
+ LOG_IF(ERROR, log) << "tag not found";
return false;
}
return ReinterpretBytes(iter->second, value);
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc
index b7c9a5f4954..2fc960892a5 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.cc
@@ -519,8 +519,8 @@ bool ElfImageReader::ReadDynamicStringTableAtOffset(VMSize offset,
VMAddress string_table_address;
VMSize string_table_size;
- if (!GetAddressFromDynamicArray(DT_STRTAB, &string_table_address) ||
- !dynamic_array_->GetValue(DT_STRSZ, &string_table_size)) {
+ if (!GetAddressFromDynamicArray(DT_STRTAB, true, &string_table_address) ||
+ !dynamic_array_->GetValue(DT_STRSZ, true, &string_table_size)) {
LOG(ERROR) << "missing string table info";
return false;
}
@@ -542,7 +542,7 @@ bool ElfImageReader::GetDebugAddress(VMAddress* debug) {
if (!InitializeDynamicArray()) {
return false;
}
- return GetAddressFromDynamicArray(DT_DEBUG, debug);
+ return GetAddressFromDynamicArray(DT_DEBUG, true, debug);
}
bool ElfImageReader::InitializeProgramHeaders() {
@@ -609,25 +609,40 @@ bool ElfImageReader::InitializeDynamicSymbolTable() {
}
VMAddress symbol_table_address;
- if (!GetAddressFromDynamicArray(DT_SYMTAB, &symbol_table_address)) {
+ if (!GetAddressFromDynamicArray(DT_SYMTAB, true, &symbol_table_address)) {
LOG(ERROR) << "no symbol table";
return false;
}
- symbol_table_.reset(
- new ElfSymbolTableReader(&memory_, this, symbol_table_address));
+ // Try both DT_HASH and DT_GNU_HASH. They're completely different, but both
+ // circuitously offer a way to find the number of entries in the symbol table.
+ // DT_HASH is specifically checked first, because depending on the linker, the
+ // count maybe be incorrect for zero-export cases. In practice, it is believed
+ // that the zero-export case is probably not particularly useful, so this
+ // incorrect count will only occur in constructed test cases (see
+ // ElfImageReader.DtHashAndDtGnuHashMatch).
+ VMSize number_of_symbol_table_entries;
+ if (!GetNumberOfSymbolEntriesFromDtHash(&number_of_symbol_table_entries) &&
+ !GetNumberOfSymbolEntriesFromDtGnuHash(&number_of_symbol_table_entries)) {
+ LOG(ERROR) << "could not retrieve number of symbol table entries";
+ return false;
+ }
+
+ symbol_table_.reset(new ElfSymbolTableReader(
+ &memory_, this, symbol_table_address, number_of_symbol_table_entries));
symbol_table_initialized_.set_valid();
return true;
}
bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag,
+ bool log,
VMAddress* address) {
- if (!dynamic_array_->GetValue(tag, address)) {
+ if (!dynamic_array_->GetValue(tag, log, address)) {
return false;
}
-#if defined(OS_ANDROID)
- // The GNU loader updates the dynamic array according to the load bias while
- // the Android loader only updates the debug address.
+#if defined(OS_ANDROID) || defined(OS_FUCHSIA)
+ // The GNU loader updates the dynamic array according to the load bias.
+ // The Android and Fuchsia loaders only update the debug address.
if (tag != DT_DEBUG) {
*address += GetLoadBias();
}
@@ -635,6 +650,100 @@ bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag,
return true;
}
+bool ElfImageReader::GetNumberOfSymbolEntriesFromDtHash(
+ VMSize* number_of_symbol_table_entries) {
+ if (!InitializeDynamicArray()) {
+ return false;
+ }
+
+ VMAddress dt_hash_address;
+ if (!GetAddressFromDynamicArray(DT_HASH, false, &dt_hash_address)) {
+ return false;
+ }
+
+ struct {
+ uint32_t nbucket;
+ uint32_t nchain;
+ } header;
+
+ if (!memory_.Read(dt_hash_address, sizeof(header), &header)) {
+ LOG(ERROR) << "failed to read DT_HASH header";
+ return false;
+ }
+
+ *number_of_symbol_table_entries = header.nchain;
+ return true;
+}
+
+bool ElfImageReader::GetNumberOfSymbolEntriesFromDtGnuHash(
+ VMSize* number_of_symbol_table_entries) {
+ if (!InitializeDynamicArray()) {
+ return false;
+ }
+
+ VMAddress dt_gnu_hash_address;
+ if (!GetAddressFromDynamicArray(DT_GNU_HASH, false, &dt_gnu_hash_address)) {
+ return false;
+ }
+
+ // See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and
+ // https://sourceware.org/ml/binutils/2006-10/msg00377.html.
+ struct {
+ uint32_t nbuckets;
+ uint32_t symoffset;
+ uint32_t bloom_size;
+ uint32_t bloom_shift;
+ } header;
+ if (!memory_.Read(dt_gnu_hash_address, sizeof(header), &header)) {
+ LOG(ERROR) << "failed to read DT_GNU_HASH header";
+ return false;
+ }
+
+ std::vector<uint32_t> buckets(header.nbuckets);
+ const size_t kNumBytesForBuckets = sizeof(buckets[0]) * buckets.size();
+ const size_t kWordSize =
+ memory_.Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t);
+ const VMAddress buckets_address =
+ dt_gnu_hash_address + sizeof(header) + (kWordSize * header.bloom_size);
+ if (!memory_.Read(buckets_address, kNumBytesForBuckets, buckets.data())) {
+ LOG(ERROR) << "read buckets";
+ return false;
+ }
+
+ // Locate the chain that handles the largest index bucket.
+ uint32_t last_symbol = 0;
+ for (uint32_t i = 0; i < header.nbuckets; ++i) {
+ last_symbol = std::max(buckets[i], last_symbol);
+ }
+
+ if (last_symbol < header.symoffset) {
+ *number_of_symbol_table_entries = header.symoffset;
+ return true;
+ }
+
+ // Walk the bucket's chain to add the chain length to the total.
+ const VMAddress chains_base_address = buckets_address + kNumBytesForBuckets;
+ for (;;) {
+ uint32_t chain_entry;
+ if (!memory_.Read(chains_base_address + (last_symbol - header.symoffset) *
+ sizeof(chain_entry),
+ sizeof(chain_entry),
+ &chain_entry)) {
+ LOG(ERROR) << "read chain entry";
+ return false;
+ }
+
+ ++last_symbol;
+
+ // If the low bit is set, this entry is the end of the chain.
+ if (chain_entry & 1)
+ break;
+ }
+
+ *number_of_symbol_table_entries = last_symbol;
+ return true;
+}
+
std::unique_ptr<ElfImageReader::NoteReader> ElfImageReader::Notes(
ssize_t max_note_size) {
return std::make_unique<NoteReader>(
@@ -649,4 +758,8 @@ ElfImageReader::NotesWithNameAndType(const std::string& name,
this, &memory_, program_headers_.get(), max_note_size, name, type, true);
}
+const ProcessMemoryRange* ElfImageReader::Memory() const {
+ return &memory_;
+}
+
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h
index b6b5997983b..7549f34679f 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader.h
@@ -202,6 +202,38 @@ class ElfImageReader {
NoteReader::NoteType type,
ssize_t max_note_size);
+ //! \brief Return a ProcessMemoryRange restricted to the range of this image.
+ //!
+ //! The caller does not take ownership of the returned object.
+ const ProcessMemoryRange* Memory() const;
+
+ //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB`
+ //! according to the data in the `DT_HASH` section.
+ //!
+ //! \note Exposed for testing, not normally otherwise useful.
+ //!
+ //! \param[out] number_of_symbol_table_entries The number of entries expected
+ //! in `DT_SYMTAB`.
+ //! \return `true` if a `DT_HASH` section was found, and was read
+ //! successfully, otherwise `false` with an error logged.
+ bool GetNumberOfSymbolEntriesFromDtHash(
+ VMSize* number_of_symbol_table_entries);
+
+ //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB`
+ //! according to the data in the `DT_GNU_HASH` section.
+ //!
+ //! \note Exposed for testing, not normally otherwise useful.
+ //!
+ //! \note Depending on the linker that generated the `DT_GNU_HASH` section,
+ //! this value may not be as expected if there are zero exported symbols.
+ //!
+ //! \param[out] number_of_symbol_table_entries The number of entries expected
+ //! in `DT_SYMTAB`.
+ //! \return `true` if a `DT_GNU_HASH` section was found, and was read
+ //! successfully, otherwise `false` with an error logged.
+ bool GetNumberOfSymbolEntriesFromDtGnuHash(
+ VMSize* number_of_symbol_table_entries);
+
private:
template <typename PhdrType>
class ProgramHeaderTableSpecific;
@@ -209,7 +241,7 @@ class ElfImageReader {
bool InitializeProgramHeaders();
bool InitializeDynamicArray();
bool InitializeDynamicSymbolTable();
- bool GetAddressFromDynamicArray(uint64_t tag, VMAddress* address);
+ bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address);
union {
Elf32_Ehdr header_32_;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc
index e9572be968c..dc13ad6f913 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_image_reader_test.cc
@@ -15,18 +15,37 @@
#include "snapshot/elf/elf_image_reader.h"
#include <dlfcn.h>
+#include <link.h>
#include <unistd.h>
#include "base/logging.h"
#include "build/build_config.h"
#include "gtest/gtest.h"
-#include "test/multiprocess.h"
+#include "test/multiprocess_exec.h"
+#include "test/process_type.h"
+#include "test/scoped_module_handle.h"
+#include "test/test_paths.h"
#include "util/file/file_io.h"
-#include "util/linux/auxiliary_vector.h"
-#include "util/linux/memory_map.h"
#include "util/misc/address_types.h"
#include "util/misc/from_pointer_cast.h"
-#include "util/process/process_memory_linux.h"
+#include "util/process/process_memory_native.h"
+
+#if defined(OS_FUCHSIA)
+
+#include <zircon/syscalls.h>
+
+#include "base/fuchsia/fuchsia_logging.h"
+
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+
+#include "util/linux/auxiliary_vector.h"
+#include "util/linux/memory_map.h"
+
+#else
+
+#error Port.
+
+#endif // OS_FUCHSIA
extern "C" {
__attribute__((visibility("default"))) void
@@ -37,15 +56,51 @@ namespace crashpad {
namespace test {
namespace {
-void LocateExecutable(pid_t pid, bool is_64_bit, VMAddress* elf_address) {
+
+#if defined(OS_FUCHSIA)
+
+void LocateExecutable(ProcessType process,
+ ProcessMemory* memory,
+ bool is_64_bit,
+ VMAddress* elf_address) {
+ uintptr_t debug_address;
+ zx_status_t status = zx_object_get_property(process,
+ ZX_PROP_PROCESS_DEBUG_ADDR,
+ &debug_address,
+ sizeof(debug_address));
+ ASSERT_EQ(status, ZX_OK)
+ << "zx_object_get_property: ZX_PROP_PROCESS_DEBUG_ADDR";
+ // Can be 0 if requested before the loader has loaded anything.
+ EXPECT_NE(debug_address, 0u);
+
+ constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map);
+ uintptr_t map;
+ ASSERT_TRUE(
+ memory->Read(debug_address + k_r_debug_map_offset, sizeof(map), &map))
+ << "read link_map";
+
+ constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr);
+ uintptr_t base;
+ ASSERT_TRUE(memory->Read(map + k_link_map_addr_offset, sizeof(base), &base))
+ << "read base";
+
+ *elf_address = base;
+}
+
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+
+void LocateExecutable(ProcessType process,
+ ProcessMemory* memory,
+ bool is_64_bit,
+ VMAddress* elf_address) {
AuxiliaryVector aux;
- ASSERT_TRUE(aux.Initialize(pid, is_64_bit));
+ ASSERT_TRUE(aux.Initialize(process, is_64_bit));
VMAddress phdrs;
ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs));
MemoryMap memory_map;
- ASSERT_TRUE(memory_map.Initialize(pid));
+ ASSERT_TRUE(memory_map.Initialize(process));
const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping);
const MemoryMap::Mapping* exe_mapping =
@@ -54,6 +109,8 @@ void LocateExecutable(pid_t pid, bool is_64_bit, VMAddress* elf_address) {
*elf_address = exe_mapping->range.Base();
}
+#endif // OS_FUCHSIA
+
void ExpectSymbol(ElfImageReader* reader,
const std::string& symbol_name,
VMAddress expected_symbol_address) {
@@ -67,27 +124,28 @@ void ExpectSymbol(ElfImageReader* reader,
reader->GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size));
}
-void ReadThisExecutableInTarget(pid_t pid) {
+void ReadThisExecutableInTarget(ProcessType process,
+ VMAddress exported_symbol_address) {
#if defined(ARCH_CPU_64_BITS)
constexpr bool am_64_bit = true;
#else
constexpr bool am_64_bit = false;
#endif // ARCH_CPU_64_BITS
- VMAddress elf_address;
- LocateExecutable(pid, am_64_bit, &elf_address);
-
- ProcessMemoryLinux memory;
- ASSERT_TRUE(memory.Initialize(pid));
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(process));
ProcessMemoryRange range;
ASSERT_TRUE(range.Initialize(&memory, am_64_bit));
+ VMAddress elf_address;
+ LocateExecutable(process, &memory, am_64_bit, &elf_address);
+ ASSERT_NO_FATAL_FAILURE();
+
ElfImageReader reader;
ASSERT_TRUE(reader.Initialize(range, elf_address));
- ExpectSymbol(&reader,
- "ElfImageReaderTestExportedSymbol",
- FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol));
+ ExpectSymbol(
+ &reader, "ElfImageReaderTestExportedSymbol", exported_symbol_address);
ElfImageReader::NoteReader::Result result;
std::string note_name;
@@ -121,69 +179,174 @@ void ReadThisExecutableInTarget(pid_t pid) {
ElfImageReader::NoteReader::Result::kNoMoreNotes);
}
-// Assumes that libc is loaded at the same address in this process as in the
-// target, which it is for the fork test below.
-void ReadLibcInTarget(pid_t pid) {
+void ReadLibcInTarget(ProcessType process,
+ VMAddress elf_address,
+ VMAddress getpid_address) {
#if defined(ARCH_CPU_64_BITS)
constexpr bool am_64_bit = true;
#else
constexpr bool am_64_bit = false;
#endif // ARCH_CPU_64_BITS
- Dl_info info;
- ASSERT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info)) << "dladdr:"
- << dlerror();
- VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase);
-
- ProcessMemoryLinux memory;
- ASSERT_TRUE(memory.Initialize(pid));
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(process));
ProcessMemoryRange range;
ASSERT_TRUE(range.Initialize(&memory, am_64_bit));
ElfImageReader reader;
ASSERT_TRUE(reader.Initialize(range, elf_address));
- ExpectSymbol(&reader, "getpid", FromPointerCast<VMAddress>(getpid));
+ ExpectSymbol(&reader, "getpid", getpid_address);
+}
+
+TEST(ElfImageReader, MainExecutableSelf) {
+ ReadThisExecutableInTarget(
+ GetSelfProcess(),
+ FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol));
+}
+
+CRASHPAD_CHILD_TEST_MAIN(ReadExecutableChild) {
+ VMAddress exported_symbol_address =
+ FromPointerCast<VMAddress>(ElfImageReaderTestExportedSymbol);
+ CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
+ &exported_symbol_address,
+ sizeof(exported_symbol_address));
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
}
-class ReadExecutableChildTest : public Multiprocess {
+class ReadExecutableChildTest : public MultiprocessExec {
public:
- ReadExecutableChildTest() : Multiprocess() {}
- ~ReadExecutableChildTest() {}
+ ReadExecutableChildTest() : MultiprocessExec() {}
private:
- void MultiprocessParent() { ReadThisExecutableInTarget(ChildPID()); }
- void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }
+ void MultiprocessParent() {
+ // This read serves two purposes -- on Fuchsia, the loader may have not
+ // filled in debug address as soon as the child process handle is valid, so
+ // this causes a wait at least until the main() of the child, at which point
+ // it will always be valid. Secondarily, the address of the symbol to be
+ // looked up needs to be communicated.
+ VMAddress exported_symbol_address;
+ CheckedReadFileExactly(ReadPipeHandle(),
+ &exported_symbol_address,
+ sizeof(exported_symbol_address));
+ ReadThisExecutableInTarget(ChildProcess(), exported_symbol_address);
+ }
};
-TEST(ElfImageReader, MainExecutableSelf) {
- ReadThisExecutableInTarget(getpid());
-}
-
TEST(ElfImageReader, MainExecutableChild) {
ReadExecutableChildTest test;
+ test.SetChildTestMainFunction("ReadExecutableChild");
test.Run();
}
TEST(ElfImageReader, OneModuleSelf) {
- ReadLibcInTarget(getpid());
+ Dl_info info;
+ ASSERT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info)) << "dladdr:"
+ << dlerror();
+ VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase);
+ ReadLibcInTarget(
+ GetSelfProcess(), elf_address, FromPointerCast<VMAddress>(getpid));
}
-class ReadLibcChildTest : public Multiprocess {
+CRASHPAD_CHILD_TEST_MAIN(ReadLibcChild) {
+ // Get the address of libc (by using getpid() as a representative member),
+ // and also the address of getpid() itself, and write them to the parent, so
+ // it can validate reading this information back out.
+ Dl_info info;
+ EXPECT_TRUE(dladdr(reinterpret_cast<void*>(getpid), &info))
+ << "dladdr:" << dlerror();
+ VMAddress elf_address = FromPointerCast<VMAddress>(info.dli_fbase);
+ VMAddress getpid_address = FromPointerCast<VMAddress>(getpid);
+
+ CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
+ &elf_address,
+ sizeof(elf_address));
+ CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
+ &getpid_address,
+ sizeof(getpid_address));
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
+}
+
+class ReadLibcChildTest : public MultiprocessExec {
public:
- ReadLibcChildTest() : Multiprocess() {}
+ ReadLibcChildTest() : MultiprocessExec() {}
~ReadLibcChildTest() {}
private:
- void MultiprocessParent() { ReadLibcInTarget(ChildPID()); }
- void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); }
+ void MultiprocessParent() {
+ VMAddress elf_address, getpid_address;
+ CheckedReadFileExactly(ReadPipeHandle(), &elf_address, sizeof(elf_address));
+ CheckedReadFileExactly(
+ ReadPipeHandle(), &getpid_address, sizeof(getpid_address));
+ ReadLibcInTarget(ChildProcess(), elf_address, getpid_address);
+ }
};
TEST(ElfImageReader, OneModuleChild) {
ReadLibcChildTest test;
+ test.SetChildTestMainFunction("ReadLibcChild");
test.Run();
}
+#if defined(OS_FUCHSIA)
+
+// crashpad_snapshot_test_both_dt_hash_styles is specially built and forced to
+// include both .hash and .gnu.hash sections. Linux, Android, and Fuchsia have
+// different defaults for which of these sections should be included; this test
+// confirms that we get the same count from both sections.
+//
+// TODO(scottmg): Investigation in https://crrev.com/c/876879 resulted in
+// realizing that ld.bfd does not emit a .gnu.hash that is very useful for this
+// purpose when there's 0 exported entries in the module. This is not likely to
+// be too important, as there's little need to look up non-exported symbols.
+// However, it makes this test not work on Linux, where the default build uses
+// ld.bfd. On Fuchsia, the only linker in use is lld, and it generates the
+// expected .gnu.hash. So, for now, this test is only run on Fuchsia, not Linux.
+//
+// TODO(scottmg): Separately, the location of the ELF on Android needs some
+// work, and then the test could also be enabled there.
+TEST(ElfImageReader, DtHashAndDtGnuHashMatch) {
+ base::FilePath module_path =
+ TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"),
+ FILE_PATH_LITERAL("both_dt_hash_styles"),
+ TestPaths::FileType::kLoadableModule);
+ // TODO(scottmg): Remove this when upstream Fuchsia bug ZX-1619 is resolved.
+ // See also explanation in build/run_tests.py for Fuchsia .so files.
+ module_path = module_path.BaseName();
+ ScopedModuleHandle module(
+ dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
+ ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": "
+ << dlerror();
+
+#if defined(ARCH_CPU_64_BITS)
+ constexpr bool am_64_bit = true;
+#else
+ constexpr bool am_64_bit = false;
+#endif // ARCH_CPU_64_BITS
+
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(GetSelfProcess()));
+ ProcessMemoryRange range;
+ ASSERT_TRUE(range.Initialize(&memory, am_64_bit));
+
+ struct link_map* lm = reinterpret_cast<struct link_map*>(module.get());
+
+ ElfImageReader reader;
+ ASSERT_TRUE(reader.Initialize(range, lm->l_addr));
+
+ VMSize from_dt_hash;
+ ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtHash(&from_dt_hash));
+
+ VMSize from_dt_gnu_hash;
+ ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtGnuHash(&from_dt_gnu_hash));
+
+ EXPECT_EQ(from_dt_hash, from_dt_gnu_hash);
+}
+
+#endif // OS_FUCHSIA
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc
index c6395db75f0..c2c6abf0a81 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.cc
@@ -51,8 +51,12 @@ uint8_t GetVisibility(const Elf64_Sym& sym) {
ElfSymbolTableReader::ElfSymbolTableReader(const ProcessMemoryRange* memory,
ElfImageReader* elf_reader,
- VMAddress address)
- : memory_(memory), elf_reader_(elf_reader), base_address_(address) {}
+ VMAddress address,
+ VMSize num_entries)
+ : memory_(memory),
+ elf_reader_(elf_reader),
+ base_address_(address),
+ num_entries_(num_entries) {}
ElfSymbolTableReader::~ElfSymbolTableReader() {}
@@ -68,9 +72,10 @@ bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name,
VMAddress address = base_address_;
SymEnt entry;
std::string string;
- while (memory_->Read(address, sizeof(entry), &entry) &&
- elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string)) {
- if (string == name) {
+ size_t i = 0;
+ while (i < num_entries_ && memory_->Read(address, sizeof(entry), &entry)) {
+ if (elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string) &&
+ string == name) {
info_out->address = entry.st_value;
info_out->size = entry.st_size;
info_out->shndx = entry.st_shndx;
@@ -79,7 +84,9 @@ bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name,
info_out->visibility = GetVisibility(entry);
return true;
}
+ // TODO(scottmg): This should respect DT_SYMENT if present.
address += sizeof(entry);
+ ++i;
}
return false;
}
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h
index 880915a2b4e..15930372cad 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/elf/elf_symbol_table_reader.h
@@ -61,7 +61,8 @@ class ElfSymbolTableReader {
// lookup.
ElfSymbolTableReader(const ProcessMemoryRange* memory,
ElfImageReader* elf_reader,
- VMAddress address);
+ VMAddress address,
+ VMSize num_entries);
~ElfSymbolTableReader();
//! \brief Lookup information about a symbol.
@@ -78,6 +79,7 @@ class ElfSymbolTableReader {
const ProcessMemoryRange* const memory_; // weak
ElfImageReader* const elf_reader_; // weak
const VMAddress base_address_;
+ const VMSize num_entries_;
DISALLOW_COPY_AND_ASSIGN(ElfSymbolTableReader);
};
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/hash_types_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/hash_types_test.cc
new file mode 100644
index 00000000000..436323b207c
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/hash_types_test.cc
@@ -0,0 +1,17 @@
+// 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.
+
+int main() {
+ return 0;
+}
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc
index 26c65be1a8a..1a6589f8e99 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.cc
@@ -151,9 +151,92 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context,
context->dr6 = 0;
context->dr7 = 0;
}
-#else
-#error Port.
-#endif // ARCH_CPU_X86_FAMILY || DOXYGEN
+
+#elif defined(ARCH_CPU_ARM_FAMILY)
+
+void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context,
+ const FloatContext::f32_t& float_context,
+ CPUContextARM* context) {
+ static_assert(sizeof(context->regs) == sizeof(thread_context.regs),
+ "registers size mismatch");
+ memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));
+ context->fp = thread_context.fp;
+ context->ip = thread_context.ip;
+ context->sp = thread_context.sp;
+ context->lr = thread_context.lr;
+ context->pc = thread_context.pc;
+ context->cpsr = thread_context.cpsr;
+
+ static_assert(sizeof(context->vfp_regs) == sizeof(float_context.vfp),
+ "vfp size mismatch");
+ context->have_vfp_regs = float_context.have_vfp;
+ if (float_context.have_vfp) {
+ memcpy(&context->vfp_regs, &float_context.vfp, sizeof(context->vfp_regs));
+ }
+
+ static_assert(sizeof(context->fpa_regs) == sizeof(float_context.fpregs),
+ "fpregs size mismatch");
+ context->have_fpa_regs = float_context.have_fpregs;
+ if (float_context.have_fpregs) {
+ memcpy(
+ &context->fpa_regs, &float_context.fpregs, sizeof(context->fpa_regs));
+ }
+}
+
+void InitializeCPUContextARM_NoFloatingPoint(
+ const SignalThreadContext32& thread_context,
+ CPUContextARM* context) {
+ static_assert(sizeof(context->regs) == sizeof(thread_context.regs),
+ "registers size mismatch");
+ memcpy(&context->regs, &thread_context.regs, sizeof(context->regs));
+ context->fp = thread_context.fp;
+ context->ip = thread_context.ip;
+ context->sp = thread_context.sp;
+ context->lr = thread_context.lr;
+ context->pc = thread_context.pc;
+ context->cpsr = thread_context.cpsr;
+}
+
+void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context,
+ const FloatContext::f64_t& float_context,
+ CPUContextARM64* context) {
+ InitializeCPUContextARM64_NoFloatingPoint(thread_context, context);
+
+ static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs),
+ "fpsimd context size mismatch");
+ memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd));
+ context->fpsr = float_context.fpsr;
+ context->fpcr = float_context.fpcr;
+}
+
+void InitializeCPUContextARM64_NoFloatingPoint(
+ const ThreadContext::t64_t& thread_context,
+ CPUContextARM64* context) {
+ static_assert(sizeof(context->regs) == sizeof(thread_context.regs),
+ "gpr context size mismtach");
+ memcpy(context->regs, thread_context.regs, sizeof(context->regs));
+ context->sp = thread_context.sp;
+ context->pc = thread_context.pc;
+ context->pstate = thread_context.pstate;
+}
+
+void InitializeCPUContextARM64_OnlyFPSIMD(
+ const SignalFPSIMDContext& float_context,
+ CPUContextARM64* context) {
+ static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs),
+ "fpsimd context size mismatch");
+ memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd));
+ context->fpsr = float_context.fpsr;
+ context->fpcr = float_context.fpcr;
+}
+
+void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context) {
+ memset(context->fpsimd, 0, sizeof(context->fpsimd));
+ context->fpsr = 0;
+ context->fpcr = 0;
+}
+
+#endif // ARCH_CPU_X86_FAMILY
} // namespace internal
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h
index 092762c203b..1ec4a1b4d42 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/cpu_context_linux.h
@@ -68,10 +68,73 @@ void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context,
const SignalFloatContext64& float_context,
CPUContextX86_64* context);
//! \}
-#else
-#error Port. // TODO(jperaza): ARM
+
#endif // ARCH_CPU_X86_FAMILY || DOXYGEN
+#if defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN
+
+//! \brief Initializes a CPUContextARM structure from native context structures
+//! on Linux.
+//!
+//! \param[in] thread_context The native thread context.
+//! \param[in] float_context The native float context.
+//! \param[out] context The CPUContextARM structure to initialize.
+void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context,
+ const FloatContext::f32_t& float_context,
+ CPUContextARM* context);
+
+//! \brief Initializes GPR state in a CPUContextARM from a native signal context
+//! structure on Linux.
+//!
+//! Floating point state is not initialized.
+//!
+//! \param[in] thread_context The native thread context.
+//! \param[out] context The CPUContextARM structure to initialize.
+void InitializeCPUContextARM_NoFloatingPoint(
+ const SignalThreadContext32& thread_context,
+ CPUContextARM* context);
+
+//! \brief Initializes a CPUContextARM64 structure from native context
+//! structures on Linux.
+//!
+//! \param[in] thread_context The native thread context.
+//! \param[in] float_context The native float context.
+//! \param[out] context The CPUContextARM64 structure to initialize.
+void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context,
+ const FloatContext::f64_t& float_context,
+ CPUContextARM64* context);
+
+//! \brief Initializes GPR state in a CPUContextARM64 from a native context
+//! structure on Linux.
+//!
+//! Floating point state is not initialized.
+//!
+//! \param[in] thread_context The native thread context.
+//! \param[out] context The CPUContextARM64 structure to initialize.
+void InitializeCPUContextARM64_NoFloatingPoint(
+ const ThreadContext::t64_t& thread_context,
+ CPUContextARM64* context);
+
+//! \brief Initializes FPSIMD state in a CPUContextARM64 from a native fpsimd
+//! signal context structure on Linux.
+//!
+//! General purpose registers are not initialized.
+//!
+//! \param[in] float_context The native fpsimd context.
+//! \param[out] context The CPUContextARM64 structure to initialize.
+void InitializeCPUContextARM64_OnlyFPSIMD(
+ const SignalFPSIMDContext& float_context,
+ CPUContextARM64* context);
+
+//! \brief Initializes FPSIMD state in a CPUContextARM64 to zero.
+//!
+//! General purpose registers are not initialized.
+//!
+//! \param[out] context The CPUContextARM64 structure to initialize.
+void InitializeCPUContextARM64_ClearFPSIMD(CPUContextARM64* context);
+
+#endif // ARCH_CPU_ARM_FAMILY || DOXYGEN
+
} // namespace internal
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc
index 1fcbced4589..498b1f79413 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.cc
@@ -67,7 +67,10 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(
context_.x86);
} else {
- DCHECK_EQ(ucontext.fprs.magic, 0xffff);
+ if (ucontext.fprs.magic != 0xffff) {
+ LOG(ERROR) << "unexpected magic 0x" << std::hex << ucontext.fprs.magic;
+ return false;
+ }
InitializeCPUContextX86(
ucontext.mcontext.gprs, ucontext.fprs, context_.x86);
}
@@ -91,6 +94,163 @@ bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
ucontext.mcontext.gprs, ucontext.fprs, context_.x86_64);
return true;
}
+
+#elif defined(ARCH_CPU_ARM_FAMILY)
+
+template <>
+bool ExceptionSnapshotLinux::ReadContext<ContextTraits32>(
+ ProcessReader* reader,
+ LinuxVMAddress context_address) {
+ context_.architecture = kCPUArchitectureARM;
+ context_.arm = &context_union_.arm;
+
+ CPUContextARM* dest_context = context_.arm;
+ ProcessMemory* memory = reader->Memory();
+
+ LinuxVMAddress gprs_address =
+ context_address + offsetof(UContext<ContextTraits32>, mcontext32) +
+ offsetof(MContext32, gprs);
+
+ SignalThreadContext32 thread_context;
+ if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) {
+ LOG(ERROR) << "Couldn't read gprs";
+ return false;
+ }
+ InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context);
+
+ dest_context->have_fpa_regs = false;
+
+ LinuxVMAddress reserved_address =
+ context_address + offsetof(UContext<ContextTraits32>, reserved);
+ if ((reserved_address & 7) != 0) {
+ LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address;
+ return false;
+ }
+
+ constexpr VMSize kMaxContextSpace = 1024;
+
+ ProcessMemoryRange range;
+ if (!range.Initialize(memory, false, reserved_address, kMaxContextSpace)) {
+ return false;
+ }
+
+ dest_context->have_vfp_regs = false;
+ do {
+ CoprocessorContextHead head;
+ if (!range.Read(reserved_address, sizeof(head), &head)) {
+ LOG(ERROR) << "missing context terminator";
+ return false;
+ }
+ reserved_address += sizeof(head);
+
+ switch (head.magic) {
+ case VFP_MAGIC:
+ if (head.size != sizeof(SignalVFPContext) + sizeof(head)) {
+ LOG(ERROR) << "unexpected vfp context size " << head.size;
+ return false;
+ }
+ static_assert(
+ sizeof(SignalVFPContext::vfp) == sizeof(dest_context->vfp_regs),
+ "vfp context size mismatch");
+ if (!range.Read(reserved_address + offsetof(SignalVFPContext, vfp),
+ sizeof(dest_context->vfp_regs),
+ &dest_context->vfp_regs)) {
+ LOG(ERROR) << "Couldn't read vfp";
+ return false;
+ }
+ dest_context->have_vfp_regs = true;
+ return true;
+
+ case CRUNCH_MAGIC:
+ case IWMMXT_MAGIC:
+ case DUMMY_MAGIC:
+ reserved_address += head.size - sizeof(head);
+ continue;
+
+ case 0:
+ return true;
+
+ default:
+ LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic;
+ return false;
+ }
+ } while (true);
+}
+
+template <>
+bool ExceptionSnapshotLinux::ReadContext<ContextTraits64>(
+ ProcessReader* reader,
+ LinuxVMAddress context_address) {
+ context_.architecture = kCPUArchitectureARM64;
+ context_.arm64 = &context_union_.arm64;
+
+ CPUContextARM64* dest_context = context_.arm64;
+ ProcessMemory* memory = reader->Memory();
+
+ LinuxVMAddress gprs_address =
+ context_address + offsetof(UContext<ContextTraits64>, mcontext64) +
+ offsetof(MContext64, gprs);
+
+ ThreadContext::t64_t thread_context;
+ if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) {
+ LOG(ERROR) << "Couldn't read gprs";
+ return false;
+ }
+ InitializeCPUContextARM64_NoFloatingPoint(thread_context, dest_context);
+
+ LinuxVMAddress reserved_address =
+ context_address + offsetof(UContext<ContextTraits64>, reserved);
+ if ((reserved_address & 15) != 0) {
+ LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address;
+ return false;
+ }
+
+ constexpr VMSize kMaxContextSpace = 4096;
+
+ ProcessMemoryRange range;
+ if (!range.Initialize(memory, true, reserved_address, kMaxContextSpace)) {
+ return false;
+ }
+
+ do {
+ CoprocessorContextHead head;
+ if (!range.Read(reserved_address, sizeof(head), &head)) {
+ LOG(ERROR) << "missing context terminator";
+ return false;
+ }
+ reserved_address += sizeof(head);
+
+ switch (head.magic) {
+ case FPSIMD_MAGIC:
+ if (head.size != sizeof(SignalFPSIMDContext) + sizeof(head)) {
+ LOG(ERROR) << "unexpected fpsimd context size " << head.size;
+ return false;
+ }
+ SignalFPSIMDContext fpsimd;
+ if (!range.Read(reserved_address, sizeof(fpsimd), &fpsimd)) {
+ LOG(ERROR) << "Couldn't read fpsimd " << head.size;
+ return false;
+ }
+ InitializeCPUContextARM64_OnlyFPSIMD(fpsimd, dest_context);
+ return true;
+
+ case ESR_MAGIC:
+ case EXTRA_MAGIC:
+ reserved_address += head.size - sizeof(head);
+ continue;
+
+ case 0:
+ LOG(WARNING) << "fpsimd not found";
+ InitializeCPUContextARM64_ClearFPSIMD(dest_context);
+ return true;
+
+ default:
+ LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic;
+ return false;
+ }
+ } while (true);
+}
+
#endif // ARCH_CPU_X86_FAMILY
bool ExceptionSnapshotLinux::Initialize(ProcessReader* process_reader,
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h
index a744356d2a5..73949668e7d 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux.h
@@ -73,12 +73,15 @@ class ExceptionSnapshotLinux final : public ExceptionSnapshot {
template <typename Traits>
bool ReadContext(ProcessReader* reader, LinuxVMAddress context_address);
-#if defined(ARCH_CPU_X86_FAMILY)
union {
+#if defined(ARCH_CPU_X86_FAMILY)
CPUContextX86 x86;
CPUContextX86_64 x86_64;
- } context_union_;
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ CPUContextARM arm;
+ CPUContextARM64 arm64;
#endif
+ } context_union_;
CPUContext context_;
std::vector<uint64_t> codes_;
uint64_t thread_id_;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc
index bbccf7d80f9..24f0ef52d54 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/exception_snapshot_linux_test.cc
@@ -26,6 +26,7 @@
#include "gtest/gtest.h"
#include "snapshot/cpu_architecture.h"
#include "snapshot/linux/process_reader.h"
+#include "snapshot/linux/signal_context.h"
#include "sys/syscall.h"
#include "test/errors.h"
#include "test/linux/fake_ptrace_connection.h"
@@ -92,6 +93,176 @@ void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
reinterpret_cast<const char*>(&expected.__fpregs_mem)[byte_offset]);
}
}
+#elif defined(ARCH_CPU_ARMEL)
+// A native ucontext_t on ARM doesn't have enough regspace (yet) to hold all of
+// the different possible coprocessor contexts at once. However, the ABI allows
+// it and the native regspace may be expanded in the future. Append some extra
+// space so this is testable now.
+struct NativeCPUContext {
+ ucontext_t ucontext;
+ char extra[1024];
+};
+
+struct CrunchContext {
+ uint32_t mvdx[16][2];
+ uint32_t mvax[4][3];
+ uint32_t dspsc[2];
+};
+
+struct IWMMXTContext {
+ uint32_t save[38];
+};
+
+struct TestCoprocessorContext {
+ struct {
+ internal::CoprocessorContextHead head;
+ CrunchContext context;
+ } crunch;
+ struct {
+ internal::CoprocessorContextHead head;
+ IWMMXTContext context;
+ } iwmmxt;
+ struct {
+ internal::CoprocessorContextHead head;
+ IWMMXTContext context;
+ } dummy;
+ struct {
+ internal::CoprocessorContextHead head;
+ internal::SignalVFPContext context;
+ } vfp;
+ internal::CoprocessorContextHead terminator;
+};
+
+void InitializeContext(NativeCPUContext* context) {
+ memset(context, 'x', sizeof(*context));
+
+ for (int index = 0; index < (&context->ucontext.uc_mcontext.fault_address -
+ &context->ucontext.uc_mcontext.arm_r0);
+ ++index) {
+ (&context->ucontext.uc_mcontext.arm_r0)[index] = index;
+ }
+
+ static_assert(
+ sizeof(TestCoprocessorContext) <=
+ sizeof(context->ucontext.uc_regspace) + sizeof(context->extra),
+ "Insufficient context space");
+ auto test_context =
+ reinterpret_cast<TestCoprocessorContext*>(context->ucontext.uc_regspace);
+
+ test_context->crunch.head.magic = CRUNCH_MAGIC;
+ test_context->crunch.head.size = sizeof(test_context->crunch);
+ memset(
+ &test_context->crunch.context, 'c', sizeof(test_context->crunch.context));
+
+ test_context->iwmmxt.head.magic = IWMMXT_MAGIC;
+ test_context->iwmmxt.head.size = sizeof(test_context->iwmmxt);
+ memset(
+ &test_context->iwmmxt.context, 'i', sizeof(test_context->iwmmxt.context));
+
+ test_context->dummy.head.magic = DUMMY_MAGIC;
+ test_context->dummy.head.size = sizeof(test_context->dummy);
+ memset(
+ &test_context->dummy.context, 'd', sizeof(test_context->dummy.context));
+
+ test_context->vfp.head.magic = VFP_MAGIC;
+ test_context->vfp.head.size = sizeof(test_context->vfp);
+ memset(&test_context->vfp.context, 'v', sizeof(test_context->vfp.context));
+ for (size_t reg = 0; reg < arraysize(test_context->vfp.context.vfp.fpregs);
+ ++reg) {
+ test_context->vfp.context.vfp.fpregs[reg] = reg;
+ }
+ test_context->vfp.context.vfp.fpscr = 42;
+
+ test_context->terminator.magic = 0;
+ test_context->terminator.size = 0;
+}
+
+void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
+ EXPECT_EQ(actual.architecture, kCPUArchitectureARM);
+
+ EXPECT_EQ(memcmp(actual.arm->regs,
+ &expected.ucontext.uc_mcontext.arm_r0,
+ sizeof(actual.arm->regs)),
+ 0);
+ EXPECT_EQ(actual.arm->fp, expected.ucontext.uc_mcontext.arm_fp);
+ EXPECT_EQ(actual.arm->ip, expected.ucontext.uc_mcontext.arm_ip);
+ EXPECT_EQ(actual.arm->sp, expected.ucontext.uc_mcontext.arm_sp);
+ EXPECT_EQ(actual.arm->lr, expected.ucontext.uc_mcontext.arm_lr);
+ EXPECT_EQ(actual.arm->pc, expected.ucontext.uc_mcontext.arm_pc);
+ EXPECT_EQ(actual.arm->cpsr, expected.ucontext.uc_mcontext.arm_cpsr);
+
+ EXPECT_FALSE(actual.arm->have_fpa_regs);
+
+ EXPECT_TRUE(actual.arm->have_vfp_regs);
+
+ auto test_context = reinterpret_cast<const TestCoprocessorContext*>(
+ expected.ucontext.uc_regspace);
+
+ EXPECT_EQ(memcmp(actual.arm->vfp_regs.vfp,
+ &test_context->vfp.context.vfp,
+ sizeof(actual.arm->vfp_regs.vfp)),
+ 0);
+}
+#elif defined(ARCH_CPU_ARM64)
+using NativeCPUContext = ucontext_t;
+
+struct TestCoprocessorContext {
+ esr_context esr;
+ fpsimd_context fpsimd;
+ _aarch64_ctx terminator;
+};
+
+void InitializeContext(NativeCPUContext* context) {
+ memset(context, 'x', sizeof(*context));
+
+ for (size_t index = 0; index < arraysize(context->uc_mcontext.regs);
+ ++index) {
+ context->uc_mcontext.regs[index] = index;
+ }
+ context->uc_mcontext.sp = 1;
+ context->uc_mcontext.pc = 2;
+ context->uc_mcontext.pstate = 3;
+
+ auto test_context = reinterpret_cast<TestCoprocessorContext*>(
+ context->uc_mcontext.__reserved);
+
+ test_context->esr.head.magic = ESR_MAGIC;
+ test_context->esr.head.size = sizeof(test_context->esr);
+ memset(&test_context->esr.esr, 'e', sizeof(test_context->esr.esr));
+
+ test_context->fpsimd.head.magic = FPSIMD_MAGIC;
+ test_context->fpsimd.head.size = sizeof(test_context->fpsimd);
+ test_context->fpsimd.fpsr = 1;
+ test_context->fpsimd.fpcr = 2;
+ for (size_t reg = 0; reg < arraysize(test_context->fpsimd.vregs); ++reg) {
+ test_context->fpsimd.vregs[reg] = reg;
+ }
+
+ test_context->terminator.magic = 0;
+ test_context->terminator.size = 0;
+}
+
+void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) {
+ EXPECT_EQ(actual.architecture, kCPUArchitectureARM64);
+
+ EXPECT_EQ(memcmp(actual.arm64->regs,
+ expected.uc_mcontext.regs,
+ sizeof(actual.arm64->regs)),
+ 0);
+ EXPECT_EQ(actual.arm64->sp, expected.uc_mcontext.sp);
+ EXPECT_EQ(actual.arm64->pc, expected.uc_mcontext.pc);
+ EXPECT_EQ(actual.arm64->pstate, expected.uc_mcontext.pstate);
+
+ auto test_context = reinterpret_cast<const TestCoprocessorContext*>(
+ expected.uc_mcontext.__reserved);
+
+ EXPECT_EQ(actual.arm64->fpsr, test_context->fpsimd.fpsr);
+ EXPECT_EQ(actual.arm64->fpcr, test_context->fpsimd.fpcr);
+ EXPECT_EQ(memcmp(actual.arm64->fpsimd,
+ &test_context->fpsimd.vregs,
+ sizeof(actual.arm64->fpsimd)),
+ 0);
+}
#else
#error Port.
#endif
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc
index f328fefc6c5..dfe9d330356 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.cc
@@ -64,5 +64,10 @@ bool MemorySnapshotLinux::Read(Delegate* delegate) const {
return delegate->MemorySnapshotDelegateRead(buffer.get(), size_);
}
+const MemorySnapshot* MemorySnapshotLinux::MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const {
+ return MergeWithOtherSnapshotImpl(this, other);
+}
+
} // namespace internal
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h
index 3c7c8391215..8b4bcf8d0ff 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/memory_snapshot_linux.h
@@ -53,8 +53,15 @@ class MemorySnapshotLinux final : public MemorySnapshot {
uint64_t Address() const override;
size_t Size() const override;
bool Read(Delegate* delegate) const override;
+ const MemorySnapshot* MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const override;
private:
+ template <class T>
+ friend const MemorySnapshot* MergeWithOtherSnapshotImpl(
+ const T* self,
+ const MemorySnapshot* other);
+
ProcessReader* process_reader_; // weak
uint64_t address_;
size_t size_;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.cc
new file mode 100644
index 00000000000..0ddbebfa8f0
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.cc
@@ -0,0 +1,188 @@
+// 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 "snapshot/linux/module_snapshot_linux.h"
+
+#include <algorithm>
+
+#include "base/files/file_path.h"
+#include "snapshot/crashpad_types/image_annotation_reader.h"
+
+namespace crashpad {
+namespace internal {
+
+ModuleSnapshotLinux::ModuleSnapshotLinux()
+ : ModuleSnapshot(),
+ name_(),
+ elf_reader_(nullptr),
+ crashpad_info_(),
+ type_(kModuleTypeUnknown),
+ initialized_() {}
+
+ModuleSnapshotLinux::~ModuleSnapshotLinux() = default;
+
+bool ModuleSnapshotLinux::Initialize(
+ const ProcessReader::Module& process_reader_module) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+
+ if (!process_reader_module.elf_reader) {
+ LOG(ERROR) << "no elf reader";
+ return false;
+ }
+
+ name_ = process_reader_module.name;
+ elf_reader_ = process_reader_module.elf_reader;
+ type_ = process_reader_module.type;
+
+ VMAddress info_address;
+ VMSize info_size;
+ if (elf_reader_->GetDynamicSymbol(
+ "g_crashpad_info", &info_address, &info_size)) {
+ ProcessMemoryRange range;
+ if (range.Initialize(*elf_reader_->Memory()) &&
+ range.RestrictRange(info_address, info_size)) {
+ auto info = std::make_unique<CrashpadInfoReader>();
+ if (info->Initialize(&range, info_address)) {
+ crashpad_info_ = std::move(info);
+ }
+ }
+ }
+
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+bool ModuleSnapshotLinux::GetCrashpadOptions(
+ CrashpadInfoClientOptions* options) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ if (!crashpad_info_) {
+ return false;
+ }
+
+ options->crashpad_handler_behavior =
+ crashpad_info_->CrashpadHandlerBehavior();
+ options->system_crash_reporter_forwarding =
+ crashpad_info_->SystemCrashReporterForwarding();
+ options->gather_indirectly_referenced_memory =
+ crashpad_info_->GatherIndirectlyReferencedMemory();
+ options->indirectly_referenced_memory_cap =
+ crashpad_info_->IndirectlyReferencedMemoryCap();
+ return true;
+}
+
+std::string ModuleSnapshotLinux::Name() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return name_;
+}
+
+uint64_t ModuleSnapshotLinux::Address() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return elf_reader_->Address();
+}
+
+uint64_t ModuleSnapshotLinux::Size() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return elf_reader_->Size();
+}
+
+time_t ModuleSnapshotLinux::Timestamp() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return 0;
+}
+
+void ModuleSnapshotLinux::FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *version_0 = 0;
+ *version_1 = 0;
+ *version_2 = 0;
+ *version_3 = 0;
+}
+
+void ModuleSnapshotLinux::SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *version_0 = 0;
+ *version_1 = 0;
+ *version_2 = 0;
+ *version_3 = 0;
+}
+
+ModuleSnapshot::ModuleType ModuleSnapshotLinux::GetModuleType() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return type_;
+}
+
+void ModuleSnapshotLinux::UUIDAndAge(crashpad::UUID* uuid,
+ uint32_t* age) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ *age = 0;
+
+ std::unique_ptr<ElfImageReader::NoteReader> notes =
+ elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64);
+ std::string desc;
+ notes->NextNote(nullptr, nullptr, &desc);
+ desc.insert(desc.end(), 16 - std::min(desc.size(), size_t{16}), '\0');
+ uuid->InitializeFromBytes(reinterpret_cast<const uint8_t*>(&desc[0]));
+}
+
+std::string ModuleSnapshotLinux::DebugFileName() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return base::FilePath(Name()).BaseName().value();
+}
+
+std::vector<std::string> ModuleSnapshotLinux::AnnotationsVector() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::vector<std::string>();
+}
+
+std::map<std::string, std::string> ModuleSnapshotLinux::AnnotationsSimpleMap()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::map<std::string, std::string> annotations;
+ if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) {
+ ImageAnnotationReader reader(elf_reader_->Memory());
+ reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations);
+ }
+ return annotations;
+}
+
+std::vector<AnnotationSnapshot> ModuleSnapshotLinux::AnnotationObjects() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ std::vector<AnnotationSnapshot> annotations;
+ if (crashpad_info_ && crashpad_info_->AnnotationsList()) {
+ ImageAnnotationReader reader(elf_reader_->Memory());
+ reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations);
+ }
+ return annotations;
+}
+
+std::set<CheckedRange<uint64_t>> ModuleSnapshotLinux::ExtraMemoryRanges()
+ const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ return std::set<CheckedRange<uint64_t>>();
+}
+
+std::vector<const UserMinidumpStream*>
+ModuleSnapshotLinux::CustomMinidumpStreams() const {
+ return std::vector<const UserMinidumpStream*>();
+}
+
+} // namespace internal
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.h
new file mode 100644
index 00000000000..b277ff7a66d
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/module_snapshot_linux.h
@@ -0,0 +1,96 @@
+// 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_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_
+#define CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "client/crashpad_info.h"
+#include "snapshot/crashpad_info_client_options.h"
+#include "snapshot/crashpad_types/crashpad_info_reader.h"
+#include "snapshot/elf/elf_image_reader.h"
+#include "snapshot/linux/process_reader.h"
+#include "snapshot/module_snapshot.h"
+#include "util/misc/initialization_state_dcheck.h"
+
+namespace crashpad {
+
+namespace internal {
+
+//! \brief A ModuleSnapshot of a code module (binary image) loaded into a
+//! running (or crashed) process on a Linux system.
+class ModuleSnapshotLinux final : public ModuleSnapshot {
+ public:
+ ModuleSnapshotLinux();
+ ~ModuleSnapshotLinux() override;
+
+ //! \brief Initializes the object.
+ //!
+ //! \param[in] process_reader_module The module within the ProcessReader for
+ //! which the snapshot should be created.
+ //!
+ //! \return `true` if the snapshot could be created, `false` otherwise with
+ //! an appropriate message logged.
+ bool Initialize(const ProcessReader::Module& process_reader_module);
+
+ //! \brief Returns options from the module’s CrashpadInfo structure.
+ //!
+ //! \param[out] options Options set in the module’s CrashpadInfo structure.
+ //! \return `true` if there were options returned. Otherwise `false`.
+ bool GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
+ // ModuleSnapshot:
+
+ std::string Name() const override;
+ uint64_t Address() const override;
+ uint64_t Size() const override;
+ time_t Timestamp() const override;
+ void FileVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ void SourceVersion(uint16_t* version_0,
+ uint16_t* version_1,
+ uint16_t* version_2,
+ uint16_t* version_3) const override;
+ ModuleType GetModuleType() const override;
+ void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override;
+ std::string DebugFileName() const override;
+ std::vector<std::string> AnnotationsVector() const override;
+ std::map<std::string, std::string> AnnotationsSimpleMap() const override;
+ std::vector<AnnotationSnapshot> AnnotationObjects() const override;
+ std::set<CheckedRange<uint64_t>> ExtraMemoryRanges() const override;
+ std::vector<const UserMinidumpStream*> CustomMinidumpStreams() const override;
+
+ private:
+ std::string name_;
+ ElfImageReader* elf_reader_;
+ std::unique_ptr<CrashpadInfoReader> crashpad_info_;
+ ModuleType type_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ModuleSnapshotLinux);
+};
+
+} // namespace internal
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_LINUX_MODULE_SNAPSHOT_LINUX_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc
index c9a5a382521..0f196c63ad1 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.cc
@@ -14,6 +14,7 @@
#include "snapshot/linux/process_reader.h"
+#include <elf.h>
#include <errno.h>
#include <sched.h>
#include <stdio.h>
@@ -26,7 +27,9 @@
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "build/build_config.h"
+#include "snapshot/linux/debug_rendezvous.h"
#include "util/file/directory_reader.h"
+#include "util/linux/auxiliary_vector.h"
#include "util/linux/proc_stat_reader.h"
#include "util/misc/as_underlying_type.h"
@@ -166,14 +169,22 @@ void ProcessReader::Thread::InitializeStack(ProcessReader* reader) {
}
}
+ProcessReader::Module::Module()
+ : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {}
+
+ProcessReader::Module::~Module() = default;
+
ProcessReader::ProcessReader()
: connection_(),
process_info_(),
memory_map_(),
threads_(),
+ modules_(),
+ elf_readers_(),
process_memory_(),
is_64_bit_(false),
initialized_threads_(false),
+ initialized_modules_(false),
initialized_() {}
ProcessReader::~ProcessReader() {}
@@ -250,6 +261,14 @@ const std::vector<ProcessReader::Thread>& ProcessReader::Threads() {
return threads_;
}
+const std::vector<ProcessReader::Module>& ProcessReader::Modules() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ if (!initialized_modules_) {
+ InitializeModules();
+ }
+ return modules_;
+}
+
void ProcessReader::InitializeThreads() {
DCHECK(threads_.empty());
@@ -307,4 +326,78 @@ void ProcessReader::InitializeThreads() {
DCHECK(main_thread_found);
}
+void ProcessReader::InitializeModules() {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ AuxiliaryVector aux;
+ if (!aux.Initialize(ProcessID(), is_64_bit_)) {
+ return;
+ }
+
+ LinuxVMAddress phdrs;
+ if (!aux.GetValue(AT_PHDR, &phdrs)) {
+ return;
+ }
+
+ const MemoryMap::Mapping* exe_mapping;
+ if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) ||
+ !(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) {
+ return;
+ }
+
+ ProcessMemoryRange range;
+ if (!range.Initialize(Memory(), is_64_bit_)) {
+ return;
+ }
+
+ auto exe_reader = std::make_unique<ElfImageReader>();
+ if (!exe_reader->Initialize(range, exe_mapping->range.Base())) {
+ return;
+ }
+
+ LinuxVMAddress debug_address;
+ if (!exe_reader->GetDebugAddress(&debug_address)) {
+ return;
+ }
+
+ DebugRendezvous debug;
+ if (!debug.Initialize(range, debug_address)) {
+ return;
+ }
+
+ Module exe = {};
+ exe.name = !debug.Executable()->name.empty() ? debug.Executable()->name
+ : exe_mapping->name;
+ exe.elf_reader = exe_reader.get();
+ exe.type = ModuleSnapshot::ModuleType::kModuleTypeExecutable;
+
+ modules_.push_back(exe);
+ elf_readers_.push_back(std::move(exe_reader));
+
+ LinuxVMAddress loader_base = 0;
+ aux.GetValue(AT_BASE, &loader_base);
+
+ for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) {
+ const MemoryMap::Mapping* mapping;
+ if (!(mapping = memory_map_.FindMapping(entry.dynamic_array)) ||
+ !(mapping = memory_map_.FindFileMmapStart(*mapping))) {
+ continue;
+ }
+
+ auto elf_reader = std::make_unique<ElfImageReader>();
+ if (!elf_reader->Initialize(range, mapping->range.Base())) {
+ continue;
+ }
+
+ Module module = {};
+ module.name = !entry.name.empty() ? entry.name : mapping->name;
+ module.elf_reader = elf_reader.get();
+ module.type = loader_base && elf_reader->Address() == loader_base
+ ? ModuleSnapshot::kModuleTypeDynamicLoader
+ : ModuleSnapshot::kModuleTypeSharedLibrary;
+ modules_.push_back(module);
+ elf_readers_.push_back(std::move(elf_reader));
+ }
+}
+
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h
index 26d777e46f0..59a3c7efa98 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader.h
@@ -18,9 +18,13 @@
#include <sys/time.h>
#include <sys/types.h>
+#include <memory>
+#include <string>
#include <vector>
#include "base/macros.h"
+#include "snapshot/elf/elf_image_reader.h"
+#include "snapshot/module_snapshot.h"
#include "util/linux/address_types.h"
#include "util/linux/memory_map.h"
#include "util/linux/ptrace_connection.h"
@@ -56,6 +60,27 @@ class ProcessReader {
void InitializeStack(ProcessReader* reader);
};
+ //! \brief Contains information about a module loaded into a process.
+ struct Module {
+ Module();
+ ~Module();
+
+ //! \brief The pathname used to load the module from disk.
+ std::string name;
+
+ //! \brief An image reader for the module.
+ //!
+ //! The lifetime of this ElfImageReader is scoped to the lifetime of the
+ //! ProcessReader that created it.
+ //!
+ //! This field may be `nullptr` if a reader could not be created for the
+ //! module.
+ ElfImageReader* elf_reader;
+
+ //! \brief The module's type.
+ ModuleSnapshot::ModuleType type;
+ };
+
ProcessReader();
~ProcessReader();
@@ -107,16 +132,24 @@ class ProcessReader {
//! index `0`.
const std::vector<Thread>& Threads();
+ //! \return The modules loaded in the process. The first element (at index
+ //! `0`) corresponds to the main executable.
+ const std::vector<Module>& Modules();
+
private:
void InitializeThreads();
+ void InitializeModules();
PtraceConnection* connection_; // weak
ProcessInfo process_info_;
MemoryMap memory_map_;
std::vector<Thread> threads_;
+ std::vector<Module> modules_;
+ std::vector<std::unique_ptr<ElfImageReader>> elf_readers_;
ProcessMemoryLinux process_memory_;
bool is_64_bit_;
bool initialized_threads_;
+ bool initialized_modules_;
InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessReader);
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc
index 4bdee743653..a8190b39969 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_reader_test.cc
@@ -15,6 +15,7 @@
#include "snapshot/linux/process_reader.h"
#include <errno.h>
+#include <link.h>
#include <pthread.h>
#include <sched.h>
#include <stdlib.h>
@@ -42,6 +43,10 @@
#include "util/misc/from_pointer_cast.h"
#include "util/synchronization/semaphore.h"
+#if defined(OS_ANDROID)
+#include <android/api-level.h>
+#endif
+
namespace crashpad {
namespace test {
namespace {
@@ -440,6 +445,95 @@ TEST(ProcessReader, ChildWithSplitStack) {
test.Run();
}
+// Android doesn't provide dl_iterate_phdr on ARM until API 21.
+#if !defined(OS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21
+int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) {
+ SCOPED_TRACE(
+ base::StringPrintf("module %s at 0x%" PRIx64 " phdrs 0x%" PRIx64,
+ info->dlpi_name,
+ LinuxVMAddress{info->dlpi_addr},
+ FromPointerCast<LinuxVMAddress>(info->dlpi_phdr)));
+ auto modules =
+ reinterpret_cast<const std::vector<ProcessReader::Module>*>(data);
+
+ auto phdr_addr = FromPointerCast<LinuxVMAddress>(info->dlpi_phdr);
+
+#if defined(OS_ANDROID)
+ // Bionic includes a null entry.
+ if (!phdr_addr) {
+ EXPECT_EQ(info->dlpi_name, nullptr);
+ EXPECT_EQ(info->dlpi_addr, 0u);
+ EXPECT_EQ(info->dlpi_phnum, 0u);
+ return 0;
+ }
+#endif
+
+ // TODO(jperaza): This can use a range map when one is available.
+ bool found = false;
+ for (const auto& module : *modules) {
+ if (module.elf_reader && phdr_addr >= module.elf_reader->Address() &&
+ phdr_addr < module.elf_reader->Address() + module.elf_reader->Size()) {
+ found = true;
+ break;
+ }
+ }
+ EXPECT_TRUE(found);
+ return 0;
+}
+#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21
+
+void ExpectModulesFromSelf(const std::vector<ProcessReader::Module>& modules) {
+ for (const auto& module : modules) {
+ EXPECT_FALSE(module.name.empty());
+ EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown);
+ }
+
+// Android doesn't provide dl_iterate_phdr on ARM until API 21.
+#if !defined(OS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21
+ EXPECT_EQ(dl_iterate_phdr(
+ ExpectFindModule,
+ reinterpret_cast<void*>(
+ const_cast<std::vector<ProcessReader::Module>*>(&modules))),
+ 0);
+#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21
+}
+
+TEST(ProcessReader, SelfModules) {
+ FakePtraceConnection connection;
+ connection.Initialize(getpid());
+
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(&connection));
+
+ ExpectModulesFromSelf(process_reader.Modules());
+}
+
+class ChildModuleTest : public Multiprocess {
+ public:
+ ChildModuleTest() : Multiprocess() {}
+ ~ChildModuleTest() = default;
+
+ private:
+ void MultiprocessParent() override {
+ DirectPtraceConnection connection;
+ ASSERT_TRUE(connection.Initialize(ChildPID()));
+
+ ProcessReader process_reader;
+ ASSERT_TRUE(process_reader.Initialize(&connection));
+
+ ExpectModulesFromSelf(process_reader.Modules());
+ }
+
+ void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); }
+
+ DISALLOW_COPY_AND_ASSIGN(ChildModuleTest);
+};
+
+TEST(ProcessReader, ChildModules) {
+ ChildModuleTest test;
+ test.Run();
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc
index 346da0859a9..8397ad9ba44 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.cc
@@ -49,6 +49,7 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) {
system_.Initialize(&process_reader_, &snapshot_time_);
InitializeThreads();
+ InitializeModules();
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
@@ -78,6 +79,45 @@ bool ProcessSnapshotLinux::InitializeException(
return true;
}
+void ProcessSnapshotLinux::GetCrashpadOptions(
+ CrashpadInfoClientOptions* options) {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ CrashpadInfoClientOptions local_options;
+
+ for (const auto& module : modules_) {
+ CrashpadInfoClientOptions module_options;
+ if (!module->GetCrashpadOptions(&module_options)) {
+ continue;
+ }
+
+ if (local_options.crashpad_handler_behavior == TriState::kUnset) {
+ local_options.crashpad_handler_behavior =
+ module_options.crashpad_handler_behavior;
+ }
+ if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
+ local_options.system_crash_reporter_forwarding =
+ module_options.system_crash_reporter_forwarding;
+ }
+ if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) {
+ local_options.gather_indirectly_referenced_memory =
+ module_options.gather_indirectly_referenced_memory;
+ local_options.indirectly_referenced_memory_cap =
+ module_options.indirectly_referenced_memory_cap;
+ }
+
+ // If non-default values have been found for all options, the loop can end
+ // early.
+ if (local_options.crashpad_handler_behavior != TriState::kUnset &&
+ local_options.system_crash_reporter_forwarding != TriState::kUnset &&
+ local_options.gather_indirectly_referenced_memory != TriState::kUnset) {
+ break;
+ }
+ }
+
+ *options = local_options;
+}
+
pid_t ProcessSnapshotLinux::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.ProcessID();
@@ -136,9 +176,11 @@ std::vector<const ThreadSnapshot*> ProcessSnapshotLinux::Threads() const {
std::vector<const ModuleSnapshot*> ProcessSnapshotLinux::Modules() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
- // TODO(jperaza): do this.
- LOG(ERROR) << "Not implemented";
- return std::vector<const ModuleSnapshot*>();
+ std::vector<const ModuleSnapshot*> modules;
+ for (const auto& module : modules_) {
+ modules.push_back(module.get());
+ }
+ return modules;
}
std::vector<UnloadedModuleSnapshot> ProcessSnapshotLinux::UnloadedModules()
@@ -182,4 +224,13 @@ void ProcessSnapshotLinux::InitializeThreads() {
}
}
+void ProcessSnapshotLinux::InitializeModules() {
+ for (const ProcessReader::Module& reader_module : process_reader_.Modules()) {
+ auto module = std::make_unique<internal::ModuleSnapshotLinux>();
+ if (module->Initialize(reader_module)) {
+ modules_.push_back(std::move(module));
+ }
+ }
+}
+
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h
index 446ddb54492..aa6964c453a 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/process_snapshot_linux.h
@@ -24,7 +24,9 @@
#include <vector>
#include "base/macros.h"
+#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/linux/exception_snapshot_linux.h"
+#include "snapshot/linux/module_snapshot_linux.h"
#include "snapshot/linux/process_reader.h"
#include "snapshot/linux/system_snapshot_linux.h"
#include "snapshot/linux/thread_snapshot_linux.h"
@@ -86,6 +88,13 @@ class ProcessSnapshotLinux final : public ProcessSnapshot {
annotations_simple_map_ = annotations_simple_map;
}
+ //! \brief Returns options from CrashpadInfo structures found in modules in
+ //! the process.
+ //!
+ //! \param[out] options Options set in CrashpadInfo structures in modules in
+ //! the process.
+ void GetCrashpadOptions(CrashpadInfoClientOptions* options);
+
// ProcessSnapshot:
pid_t ProcessID() const override;
@@ -108,12 +117,14 @@ class ProcessSnapshotLinux final : public ProcessSnapshot {
private:
void InitializeThreads();
+ void InitializeModules();
std::map<std::string, std::string> annotations_simple_map_;
timeval snapshot_time_;
UUID report_id_;
UUID client_id_;
std::vector<std::unique_ptr<internal::ThreadSnapshotLinux>> threads_;
+ std::vector<std::unique_ptr<internal::ModuleSnapshotLinux>> modules_;
std::unique_ptr<internal::ExceptionSnapshotLinux> exception_;
internal::SystemSnapshotLinux system_;
ProcessReader process_reader_;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h
index d8d5f69dce0..0476fdfd4ba 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/signal_context.h
@@ -12,13 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_
-#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_
+#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_
+#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_
+#include <signal.h>
#include <stdint.h>
#include <sys/types.h>
+#include <type_traits>
+
#include "build/build_config.h"
+#include "util/linux/thread_info.h"
#include "util/linux/traits.h"
namespace crashpad {
@@ -85,6 +89,35 @@ struct Siginfo {
};
};
+template <typename Traits>
+struct SignalStack {
+ typename Traits::Address stack_pointer;
+ uint32_t flags;
+ typename Traits::UInteger32_64Only padding;
+ typename Traits::Size size;
+};
+
+template <typename Traits, typename Enable = void>
+struct Sigset {};
+
+template <typename Traits>
+struct Sigset<
+ Traits,
+ typename std::enable_if<std::is_base_of<Traits32, Traits>::value>::type> {
+ uint64_t val;
+};
+
+template <typename Traits>
+struct Sigset<
+ Traits,
+ typename std::enable_if<std::is_base_of<Traits64, Traits>::value>::type> {
+#if defined(OS_ANDROID)
+ uint64_t val;
+#else
+ typename Traits::ULong val[16];
+#endif // OS_ANDROID
+};
+
#if defined(ARCH_CPU_X86_FAMILY)
struct SignalThreadContext32 {
@@ -167,43 +200,105 @@ struct MContext {
};
template <typename Traits>
-struct SignalStack {
- typename Traits::Address stack_pointer;
- uint32_t flags;
- typename Traits::UInteger32_64Only padding;
- typename Traits::Size size;
+struct UContext {
+ typename Traits::ULong flags;
+ typename Traits::Address link;
+ SignalStack<Traits> stack;
+ MContext<Traits> mcontext;
+ Sigset<Traits> sigmask;
+ typename Traits::FloatContext fprs;
};
-template <typename Traits>
-struct Sigset {};
+#elif defined(ARCH_CPU_ARM_FAMILY)
-template <>
-struct Sigset<ContextTraits32> {
- uint64_t val;
+struct CoprocessorContextHead {
+ uint32_t magic;
+ uint32_t size;
};
-#if defined(OS_ANDROID)
-template <>
-struct Sigset<ContextTraits64> {
- uint64_t val;
+struct SignalFPSIMDContext {
+ uint32_t fpsr;
+ uint32_t fpcr;
+ uint128_struct vregs[32];
};
-#else
-template <>
-struct Sigset<ContextTraits64> {
- ContextTraits64::ULong val[16];
+
+struct SignalVFPContext {
+ FloatContext::f32_t::vfp_t vfp;
+ struct vfp_exc {
+ uint32_t fpexc;
+ uint32_t fpinst;
+ uint32_t fpinst2;
+ } vfp_exc;
+ uint32_t padding;
+};
+
+struct SignalThreadContext32 {
+ uint32_t regs[11];
+ uint32_t fp;
+ uint32_t ip;
+ uint32_t sp;
+ uint32_t lr;
+ uint32_t pc;
+ uint32_t cpsr;
+};
+
+using SignalThreadContext64 = ThreadContext::t64_t;
+
+struct MContext32 {
+ uint32_t trap_no;
+ uint32_t error_code;
+ uint32_t oldmask;
+ SignalThreadContext32 gprs;
+ uint32_t fault_address;
+};
+
+struct MContext64 {
+ uint64_t fault_address;
+ SignalThreadContext64 gprs;
+};
+
+struct ContextTraits32 : public Traits32 {
+ using MContext32 = MContext32;
+ using MContext64 = Nothing;
+};
+
+struct ContextTraits64 : public Traits64 {
+ using MContext32 = Nothing;
+ using MContext64 = MContext64;
};
-#endif // OS_ANDROID
template <typename Traits>
struct UContext {
typename Traits::ULong flags;
typename Traits::Address link;
SignalStack<Traits> stack;
- MContext<Traits> mcontext;
+ typename Traits::MContext32 mcontext32;
Sigset<Traits> sigmask;
- typename Traits::FloatContext fprs;
+ char padding[128 - sizeof(sigmask)];
+ typename Traits::Char_64Only padding2[8];
+ typename Traits::MContext64 mcontext64;
+ typename Traits::Char_64Only padding3[8];
+ char reserved[0];
};
+#if defined(ARCH_CPU_ARMEL)
+static_assert(offsetof(UContext<ContextTraits32>, mcontext32) ==
+ offsetof(ucontext_t, uc_mcontext),
+ "context offset mismatch");
+static_assert(offsetof(UContext<ContextTraits32>, reserved) ==
+ offsetof(ucontext_t, uc_regspace),
+ "regspace offset mismatch");
+
+#elif defined(ARCH_CPU_ARM64)
+static_assert(offsetof(UContext<ContextTraits64>, mcontext64) ==
+ offsetof(ucontext_t, uc_mcontext),
+ "context offset mismtach");
+static_assert(offsetof(UContext<ContextTraits64>, reserved) ==
+ offsetof(ucontext_t, uc_mcontext) +
+ offsetof(mcontext_t, __reserved),
+ "reserved space offset mismtach");
+#endif
+
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
@@ -213,4 +308,4 @@ struct UContext {
} // namespace internal
} // namespace crashpad
-#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_LINUX_H_
+#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc
index f3ed99beb58..c9c643834b7 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/system_snapshot_linux.cc
@@ -197,6 +197,9 @@ CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const {
#if defined(ARCH_CPU_X86_FAMILY)
return process_reader_->Is64Bit() ? kCPUArchitectureX86_64
: kCPUArchitectureX86;
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ return process_reader_->Is64Bit() ? kCPUArchitectureARM64
+ : kCPUArchitectureARM;
#else
#error port to your architecture
#endif
@@ -206,6 +209,9 @@ uint32_t SystemSnapshotLinux::CPURevision() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.Revision();
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
+ return 0;
#else
#error port to your architecture
#endif
@@ -220,6 +226,9 @@ std::string SystemSnapshotLinux::CPUVendor() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.Vendor();
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
+ return std::string();
#else
#error port to your architecture
#endif
@@ -264,7 +273,12 @@ uint64_t SystemSnapshotLinux::CPUX86Features() const {
uint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.ExtendedFeatures();
+#else
+ NOTREACHED();
+ return 0;
+#endif
}
uint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const {
@@ -340,7 +354,14 @@ std::string SystemSnapshotLinux::MachineDescription() const {
bool SystemSnapshotLinux::NXEnabled() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+#if defined(ARCH_CPU_X86_FAMILY)
return cpuid_.NXEnabled();
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30
+ return false;
+#else
+#error Port.
+#endif // ARCH_CPU_X86_FAMILY
}
void SystemSnapshotLinux::TimeZone(DaylightSavingTimeStatus* dst_status,
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc
index dcfe5c40dd8..f465a59c331 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.cc
@@ -56,6 +56,20 @@ bool ThreadSnapshotLinux::Initialize(
thread.thread_info.float_context.f32,
context_.x86);
}
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ if (process_reader->Is64Bit()) {
+ context_.architecture = kCPUArchitectureARM64;
+ context_.arm64 = &context_union_.arm64;
+ InitializeCPUContextARM64(thread.thread_info.thread_context.t64,
+ thread.thread_info.float_context.f64,
+ context_.arm64);
+ } else {
+ context_.architecture = kCPUArchitectureARM;
+ context_.arm = &context_union_.arm;
+ InitializeCPUContextARM(thread.thread_info.thread_context.t32,
+ thread.thread_info.float_context.f32,
+ context_.arm);
+ }
#else
#error Port.
#endif
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h
index e0ce7adb5e2..1ba291dc464 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/linux/thread_snapshot_linux.h
@@ -58,14 +58,17 @@ class ThreadSnapshotLinux final : public ThreadSnapshot {
std::vector<const MemorySnapshot*> ExtraMemory() const override;
private:
-#if defined(ARCH_CPU_X86_FAMILY)
union {
+#if defined(ARCH_CPU_X86_FAMILY)
CPUContextX86 x86;
CPUContextX86_64 x86_64;
- } context_union_;
+#elif defined(ARCH_CPU_ARM_FAMILY)
+ CPUContextARM arm;
+ CPUContextARM64 arm64;
#else
#error Port.
#endif // ARCH_CPU_X86_FAMILY
+ } context_union_;
CPUContext context_;
MemorySnapshotLinux stack_;
LinuxVMAddress thread_specific_data_address_;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc
index adc77520c24..ec1d460831a 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.cc
@@ -66,5 +66,10 @@ bool MemorySnapshotMac::Read(Delegate* delegate) const {
return delegate->MemorySnapshotDelegateRead(buffer.get(), size_);
}
+const MemorySnapshot* MemorySnapshotMac::MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const {
+ return MergeWithOtherSnapshotImpl(this, other);
+}
+
} // namespace internal
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h
index bbc39de346d..f06fbf478c8 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/mac/memory_snapshot_mac.h
@@ -52,8 +52,15 @@ class MemorySnapshotMac final : public MemorySnapshot {
uint64_t Address() const override;
size_t Size() const override;
bool Read(Delegate* delegate) const override;
+ const MemorySnapshot* MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const override;
private:
+ template <class T>
+ friend const MemorySnapshot* MergeWithOtherSnapshotImpl(
+ const T* self,
+ const MemorySnapshot* other);
+
ProcessReader* process_reader_; // weak
uint64_t address_;
uint64_t size_;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.cc b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.cc
new file mode 100644
index 00000000000..a4c8b038c1b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.cc
@@ -0,0 +1,95 @@
+// 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 "snapshot/memory_snapshot.h"
+
+#include <algorithm>
+
+#include "base/format_macros.h"
+#include "base/strings/stringprintf.h"
+#include "util/numeric/checked_range.h"
+
+namespace crashpad {
+namespace {
+
+bool DetermineMergedRangeImpl(bool log,
+ const MemorySnapshot* a,
+ const MemorySnapshot* b,
+ CheckedRange<uint64_t, size_t>* merged) {
+ if (a->Size() == 0) {
+ LOG_IF(ERROR, log) << base::StringPrintf(
+ "invalid empty range at 0x%" PRIx64, a->Address());
+ return false;
+ }
+
+ if (b->Size() == 0) {
+ LOG_IF(ERROR, log) << base::StringPrintf(
+ "invalid empty range at 0x%" PRIx64, b->Address());
+ return false;
+ }
+
+ CheckedRange<uint64_t, size_t> range_a(a->Address(), a->Size());
+ if (!range_a.IsValid()) {
+ LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64
+ ", size %" PRIuS,
+ range_a.base(),
+ range_a.size());
+ return false;
+ }
+
+ CheckedRange<uint64_t, size_t> range_b(b->Address(), b->Size());
+ if (!range_b.IsValid()) {
+ LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64
+ ", size %" PRIuS,
+ range_b.base(),
+ range_b.size());
+ return false;
+ }
+
+ if (!range_a.OverlapsRange(range_b) && range_a.end() != range_b.base() &&
+ range_b.end() != range_a.base()) {
+ LOG_IF(ERROR, log) << base::StringPrintf(
+ "ranges not overlapping or abutting: (0x%" PRIx64 ", size %" PRIuS
+ ") and (0x%" PRIx64 ", size %" PRIuS ")",
+ range_a.base(),
+ range_a.size(),
+ range_b.base(),
+ range_b.size());
+ return false;
+ }
+
+ if (merged) {
+ uint64_t base = std::min(range_a.base(), range_b.base());
+ uint64_t end = std::max(range_a.end(), range_b.end());
+ size_t size = static_cast<size_t>(end - base);
+ merged->SetRange(base, size);
+ }
+ return true;
+}
+
+} // namespace
+
+bool LoggingDetermineMergedRange(const MemorySnapshot* a,
+ const MemorySnapshot* b,
+ CheckedRange<uint64_t, size_t>* merged) {
+ return DetermineMergedRangeImpl(true, a, b, merged);
+}
+
+bool DetermineMergedRange(const MemorySnapshot* a,
+ const MemorySnapshot* b,
+ CheckedRange<uint64_t, size_t>* merged) {
+ return DetermineMergedRangeImpl(false, a, b, merged);
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h
index 47f8443e617..9e274a0e338 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot.h
@@ -18,6 +18,10 @@
#include <stdint.h>
#include <sys/types.h>
+#include <memory>
+
+#include "util/numeric/checked_range.h"
+
namespace crashpad {
//! \brief An abstract interface to a snapshot representing a region of memory
@@ -70,8 +74,68 @@ class MemorySnapshot {
//! Delegate::MemorySnapshotDelegateRead(), which should be `true` on
//! success and `false` on failure.
virtual bool Read(Delegate* delegate) const = 0;
+
+ //! \brief Creates a new MemorySnapshot based on merging this one with \a
+ //! other.
+ //!
+ //! The ranges described by the two snapshots must either overlap or abut, and
+ //! must be of the same concrete type.
+ //!
+ //! \return A newly allocated MemorySnapshot representing the merged range, or
+ //! `nullptr` with an error logged.
+ virtual const MemorySnapshot* MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const = 0;
};
+//! \brief Given two memory snapshots, checks if they're overlapping or
+//! abutting, and if so, returns the result of merging the two ranges.
+//!
+//! This function is useful to implement
+//! MemorySnapshot::MergeWithOtherSnapshot().
+//!
+//! \param[in] a The first range. Must have Size() > 0.
+//! \param[in] b The second range. Must have Size() > 0.
+//! \param[out] merged The resulting merged range. May be `nullptr` if only a
+//! characterization of the ranges is desired.
+//!
+//! \return `true` if the input ranges overlap or abut, with \a merged filled
+//! out, otherwise, `false` with an error logged if \a log is `true`.
+bool LoggingDetermineMergedRange(const MemorySnapshot* a,
+ const MemorySnapshot* b,
+ CheckedRange<uint64_t, size_t>* merged);
+
+//! \brief The same as LoggingDetermineMergedRange but with no errors logged.
+//!
+//! \sa LoggingDetermineMergedRange
+bool DetermineMergedRange(const MemorySnapshot* a,
+ const MemorySnapshot* b,
+ CheckedRange<uint64_t, size_t>* merged);
+
+namespace internal {
+
+//! \brief A standard implementation of MemorySnapshot::MergeWithOtherSnapshot()
+//! for concrete MemorySnapshot implementations that use a
+//! `process_reader_`.
+template <class T>
+const MemorySnapshot* MergeWithOtherSnapshotImpl(const T* self,
+ const MemorySnapshot* other) {
+ const T* other_as_memory_snapshot_concrete =
+ reinterpret_cast<const T*>(other);
+ if (self->process_reader_ !=
+ other_as_memory_snapshot_concrete->process_reader_) {
+ LOG(ERROR) << "different process_reader_ for snapshots";
+ return nullptr;
+ }
+ CheckedRange<uint64_t, size_t> merged(0, 0);
+ if (!LoggingDetermineMergedRange(self, other, &merged))
+ return nullptr;
+
+ std::unique_ptr<T> result(new T());
+ result->Initialize(self->process_reader_, merged.base(), merged.size());
+ return result.release();
+}
+
+} // namespace internal
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot_test.cc b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot_test.cc
new file mode 100644
index 00000000000..91e847fb26b
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/snapshot/memory_snapshot_test.cc
@@ -0,0 +1,152 @@
+// 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 "snapshot/memory_snapshot.h"
+
+#include "base/macros.h"
+#include "gtest/gtest.h"
+#include "snapshot/test/test_memory_snapshot.h"
+
+namespace crashpad {
+namespace test {
+namespace {
+
+TEST(DetermineMergedRange, NonOverlapping) {
+ TestMemorySnapshot a;
+ TestMemorySnapshot b;
+ a.SetAddress(0);
+ a.SetSize(100);
+ b.SetAddress(200);
+ b.SetSize(50);
+ CheckedRange<uint64_t, size_t> range(0, 0);
+ EXPECT_FALSE(DetermineMergedRange(&a, &b, &range));
+ EXPECT_FALSE(DetermineMergedRange(&b, &a, &range));
+
+ a.SetSize(199);
+ EXPECT_FALSE(DetermineMergedRange(&a, &b, &range));
+}
+
+TEST(DetermineMergedRange, Empty) {
+ TestMemorySnapshot a;
+ TestMemorySnapshot b;
+ a.SetAddress(100);
+ a.SetSize(0);
+ b.SetAddress(200);
+ b.SetSize(20);
+
+ CheckedRange<uint64_t, size_t> range(0, 0);
+ // Empty are invalid.
+ EXPECT_FALSE(DetermineMergedRange(&a, &b, &range));
+ EXPECT_FALSE(DetermineMergedRange(&b, &a, &range));
+ EXPECT_FALSE(DetermineMergedRange(&a, &a, &range));
+}
+
+TEST(DetermineMergedRange, Abutting) {
+ TestMemorySnapshot a;
+ TestMemorySnapshot b;
+
+ a.SetAddress(0);
+ a.SetSize(10);
+ b.SetAddress(10);
+ b.SetSize(20);
+
+ CheckedRange<uint64_t, size_t> range(0, 0);
+ EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));
+ EXPECT_EQ(0u, range.base());
+ EXPECT_EQ(30u, range.size());
+
+ EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));
+ EXPECT_EQ(0u, range.base());
+ EXPECT_EQ(30u, range.size());
+}
+
+TEST(DetermineMergedRange, TypicalOverlapping) {
+ TestMemorySnapshot a;
+ TestMemorySnapshot b;
+
+ a.SetAddress(10);
+ a.SetSize(100);
+ b.SetAddress(50);
+ b.SetSize(100);
+
+ CheckedRange<uint64_t, size_t> range(0, 0);
+ EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));
+ EXPECT_EQ(10u, range.base());
+ EXPECT_EQ(140u, range.size());
+
+ EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));
+ EXPECT_EQ(10u, range.base());
+ EXPECT_EQ(140u, range.size());
+}
+
+TEST(DetermineMergedRange, OneFullyInsideAnother) {
+ TestMemorySnapshot a;
+ TestMemorySnapshot b;
+
+ a.SetAddress(20);
+ a.SetSize(100);
+ b.SetAddress(5);
+ b.SetSize(200);
+
+ CheckedRange<uint64_t, size_t> range(0, 0);
+ EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));
+ EXPECT_EQ(5u, range.base());
+ EXPECT_EQ(200u, range.size());
+
+ EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));
+ EXPECT_EQ(5u, range.base());
+ EXPECT_EQ(200u, range.size());
+}
+
+TEST(DetermineMergedRange, SameStart) {
+ TestMemorySnapshot a;
+ TestMemorySnapshot b;
+
+ a.SetAddress(5);
+ a.SetSize(100);
+ b.SetAddress(5);
+ b.SetSize(50);
+
+ CheckedRange<uint64_t, size_t> range(0, 0);
+ EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));
+ EXPECT_EQ(5u, range.base());
+ EXPECT_EQ(100u, range.size());
+
+ EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));
+ EXPECT_EQ(5u, range.base());
+ EXPECT_EQ(100u, range.size());
+}
+
+TEST(DetermineMergedRange, SameEnd) {
+ TestMemorySnapshot a;
+ TestMemorySnapshot b;
+
+ a.SetAddress(5);
+ a.SetSize(100);
+ b.SetAddress(70);
+ b.SetSize(35);
+
+ CheckedRange<uint64_t, size_t> range(0, 0);
+ EXPECT_TRUE(DetermineMergedRange(&a, &b, &range));
+ EXPECT_EQ(5u, range.base());
+ EXPECT_EQ(100u, range.size());
+
+ EXPECT_TRUE(DetermineMergedRange(&b, &a, &range));
+ EXPECT_EQ(5u, range.base());
+ EXPECT_EQ(100u, range.size());
+}
+
+} // namespace
+} // namespace test
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp b/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp
index d2fc91f5af6..da192341e9c 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp
+++ b/chromium/third_party/crashpad/crashpad/snapshot/snapshot.gyp
@@ -39,6 +39,10 @@
'cpu_context.h',
'crashpad_info_client_options.cc',
'crashpad_info_client_options.h',
+ 'crashpad_types/crashpad_info_reader.cc',
+ 'crashpad_types/crashpad_info_reader.h',
+ 'crashpad_types/image_annotation_reader.cc',
+ 'crashpad_types/image_annotation_reader.h',
'elf/elf_dynamic_array_reader.cc',
'elf/elf_dynamic_array_reader.h',
'elf/elf_image_reader.cc',
@@ -56,6 +60,8 @@
'linux/exception_snapshot_linux.h',
'linux/memory_snapshot_linux.cc',
'linux/memory_snapshot_linux.h',
+ 'linux/module_snapshot_linux.cc',
+ 'linux/module_snapshot_linux.h',
'linux/process_reader.cc',
'linux/process_reader.h',
'linux/process_snapshot_linux.cc',
@@ -102,6 +108,7 @@
'mac/system_snapshot_mac.h',
'mac/thread_snapshot_mac.cc',
'mac/thread_snapshot_mac.h',
+ 'memory_snapshot.cc',
'memory_snapshot.h',
'minidump/minidump_annotation_reader.cc',
'minidump/minidump_annotation_reader.h',
@@ -171,6 +178,7 @@
}, { # else: OS!="linux" and OS!="android"
'sources/': [
['exclude', '^elf/'],
+ ['exclude', '^crashpad_types/'],
],
}],
['target_arch!="ia32" and target_arch!="x64"', {
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp b/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp
index 1e32b8e1c29..277bf6e8eb4 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp
+++ b/chromium/third_party/crashpad/crashpad/snapshot/snapshot_test.gyp
@@ -52,6 +52,8 @@
'target_name': 'crashpad_snapshot_test',
'type': 'executable',
'dependencies': [
+ 'crashpad_snapshot_test_both_dt_hash_styles',
+ 'crashpad_snapshot_test_lib',
'crashpad_snapshot_test_module',
'crashpad_snapshot_test_module_large',
'crashpad_snapshot_test_module_small',
@@ -71,7 +73,10 @@
'sources': [
'api/module_annotations_win_test.cc',
'cpu_context_test.cc',
+ 'memory_snapshot_test.cc',
'crashpad_info_client_options_test.cc',
+ 'crashpad_types/crashpad_info_reader_test.cc',
+ 'crashpad_types/image_annotation_reader_test.cc',
'elf/elf_image_reader_test.cc',
'elf/elf_image_reader_test_note.S',
'linux/debug_rendezvous_test.cc',
@@ -139,6 +144,7 @@
}, { # else: OS!="linux" and OS!="android"
'sources/': [
['exclude', '^elf/'],
+ ['exclude', '^crashpad_types/'],
],
}],
],
@@ -190,6 +196,18 @@
'crashpad_info_size_test_module.cc',
],
},
+ {
+ 'target_name': 'crashpad_snapshot_test_both_dt_hash_styles',
+ 'type': 'executable',
+ 'sources': [
+ 'hash_types_test.cc',
+ ],
+
+ 'ldflags': [
+ # This makes `ld` emit both .hash and .gnu.hash sections.
+ '-Wl,--hash-style=both',
+ ],
+ },
],
'conditions': [
['OS=="mac"', {
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc
index 666cb93693b..17e8ac117a8 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.cc
@@ -16,7 +16,6 @@
#include "snapshot/win/memory_snapshot_win.h"
-
namespace crashpad {
namespace internal {
@@ -67,5 +66,10 @@ bool MemorySnapshotWin::Read(Delegate* delegate) const {
return delegate->MemorySnapshotDelegateRead(buffer.get(), size_);
}
+const MemorySnapshot* MemorySnapshotWin::MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const {
+ return MergeWithOtherSnapshotImpl(this, other);
+}
+
} // namespace internal
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h
index 04d118426f2..ebc878b81ab 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/win/memory_snapshot_win.h
@@ -52,8 +52,15 @@ class MemorySnapshotWin final : public MemorySnapshot {
uint64_t Address() const override;
size_t Size() const override;
bool Read(Delegate* delegate) const override;
+ const MemorySnapshot* MergeWithOtherSnapshot(
+ const MemorySnapshot* other) const override;
private:
+ template <class T>
+ friend const MemorySnapshot* MergeWithOtherSnapshotImpl(
+ const T* self,
+ const MemorySnapshot* other);
+
ProcessReaderWin* process_reader_; // weak
uint64_t address_;
size_t size_;
diff --git a/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h b/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h
index 0fd02cfdb71..b6782afb4f9 100644
--- a/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h
+++ b/chromium/third_party/crashpad/crashpad/snapshot/x86/cpuid_reader.h
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#ifndef CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_
+#define CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_
+
#include <stdint.h>
#include <string>
@@ -61,3 +64,5 @@ class CpuidReader {
} // namespace internal
} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_
diff --git a/chromium/third_party/crashpad/crashpad/test/BUILD.gn b/chromium/third_party/crashpad/crashpad/test/BUILD.gn
index b8e11d00a21..f7541749624 100644
--- a/chromium/third_party/crashpad/crashpad/test/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/test/BUILD.gn
@@ -32,7 +32,10 @@ static_library("test") {
"main_arguments.cc",
"main_arguments.h",
"multiprocess.h",
+ "multiprocess_exec.cc",
"multiprocess_exec.h",
+ "process_type.cc",
+ "process_type.h",
"scoped_module_handle.cc",
"scoped_module_handle.h",
"scoped_temp_dir.cc",
@@ -42,13 +45,13 @@ static_library("test") {
]
if (crashpad_is_posix) {
- sources += [
- "multiprocess_posix.cc",
- "scoped_temp_dir_posix.cc",
- ]
+ sources += [ "scoped_temp_dir_posix.cc" ]
if (!crashpad_is_fuchsia) {
- sources += [ "multiprocess_exec_posix.cc" ]
+ sources += [
+ "multiprocess_exec_posix.cc",
+ "multiprocess_posix.cc",
+ ]
}
}
@@ -65,7 +68,7 @@ static_library("test") {
]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/fake_ptrace_connection.cc",
"linux/fake_ptrace_connection.h",
@@ -91,6 +94,11 @@ static_library("test") {
]
}
+ if (crashpad_is_fuchsia) {
+ sources += [ "multiprocess_exec_fuchsia.cc" ]
+ libs = [ "launchpad" ]
+ }
+
public_configs = [ "..:crashpad_config" ]
configs += [ "../build:crashpad_is_in_chromium" ]
@@ -121,6 +129,7 @@ source_set("test_test") {
sources = [
"hex_string_test.cc",
"main_arguments_test.cc",
+ "multiprocess_exec_test.cc",
"scoped_temp_dir_test.cc",
"test_paths_test.cc",
]
@@ -141,11 +150,6 @@ source_set("test_test") {
}
if (!crashpad_is_fuchsia) {
- sources += [
- # TODO(scottmg): A MultiprocessExecFuchsia is probably desirable, but
- # hasn't been implemented yet.
- "multiprocess_exec_test.cc",
- ]
}
deps = [
@@ -166,6 +170,10 @@ executable("crashpad_test_test_multiprocess_exec_test_child") {
sources = [
"multiprocess_exec_test_child.cc",
]
+
+ deps = [
+ "../third_party/mini_chromium:base",
+ ]
}
static_library("gmock_main") {
diff --git a/chromium/third_party/crashpad/crashpad/test/test.gyp b/chromium/third_party/crashpad/crashpad/test/test.gyp
index ad88e0367a3..a3721efddbd 100644
--- a/chromium/third_party/crashpad/crashpad/test/test.gyp
+++ b/chromium/third_party/crashpad/crashpad/test/test.gyp
@@ -58,10 +58,13 @@
'main_arguments.cc',
'main_arguments.h',
'multiprocess.h',
+ 'multiprocess_exec.cc',
'multiprocess_exec.h',
'multiprocess_exec_posix.cc',
'multiprocess_exec_win.cc',
'multiprocess_posix.cc',
+ 'process_type.cc',
+ 'process_type.h',
'scoped_module_handle.cc',
'scoped_module_handle.h',
'scoped_temp_dir.cc',
diff --git a/chromium/third_party/crashpad/crashpad/test/test_test.gyp b/chromium/third_party/crashpad/crashpad/test/test_test.gyp
index a31650da46a..3c6d7066662 100644
--- a/chromium/third_party/crashpad/crashpad/test/test_test.gyp
+++ b/chromium/third_party/crashpad/crashpad/test/test_test.gyp
@@ -48,6 +48,9 @@
{
'target_name': 'crashpad_test_test_multiprocess_exec_test_child',
'type': 'executable',
+ 'dependencies': [
+ '../third_party/mini_chromium/mini_chromium.gyp:base',
+ ],
'sources': [
'multiprocess_exec_test_child.cc',
],
diff --git a/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn b/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn
index d2702315c9c..1a026119a9a 100644
--- a/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/third_party/gtest/BUILD.gn
@@ -32,13 +32,14 @@ if (crashpad_is_in_chromium) {
group("gtest") {
testonly = true
public_deps = [
- "//third_party/gtest",
+ "//third_party/googletest:gtest",
]
}
group("gmock") {
testonly = true
- # TODO(scottmg): Fuchsia doesn't have a third_party/gmock, and has a
- # pre-gmock-integration gtest.
+ public_deps = [
+ "//third_party/googletest:gmock",
+ ]
}
} else if (crashpad_is_standalone) {
config("gtest_private_config") {
diff --git a/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn
index 9ab8178012d..e11d011181d 100644
--- a/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/third_party/mini_chromium/BUILD.gn
@@ -19,13 +19,9 @@ group("base") {
public_deps = [
"//base",
]
- } else if (crashpad_is_in_fuchsia) {
+ } else if (crashpad_is_standalone || crashpad_is_in_fuchsia) {
public_deps = [
- "//third_party/mini_chromium/base",
- ]
- } else if (crashpad_is_standalone) {
- public_deps = [
- "//third_party/mini_chromium/mini_chromium/base",
+ "mini_chromium/base",
]
}
}
diff --git a/chromium/third_party/crashpad/crashpad/util/BUILD.gn b/chromium/third_party/crashpad/crashpad/util/BUILD.gn
index 9a4970fd862..13a5da80c90 100644
--- a/chromium/third_party/crashpad/crashpad/util/BUILD.gn
+++ b/chromium/third_party/crashpad/crashpad/util/BUILD.gn
@@ -241,6 +241,10 @@ static_library("util") {
}
if (crashpad_is_linux) {
+ sources += [ "net/http_transport_libcurl.cc" ]
+ }
+
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/address_types.h",
"linux/auxiliary_vector.cc",
@@ -269,10 +273,17 @@ static_library("util") {
"linux/thread_info.h",
"linux/traits.h",
"misc/paths_linux.cc",
- "net/http_transport_libcurl.cc",
"posix/process_info_linux.cc",
"process/process_memory_linux.cc",
"process/process_memory_linux.h",
+ ]
+ }
+
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ sources += [
+ "process/process_memory.cc",
+ "process/process_memory.h",
+ "process/process_memory_native.h",
# TODO: Port to all platforms.
"process/process_memory_range.cc",
@@ -361,6 +372,8 @@ static_library("util") {
sources += [
"misc/paths_fuchsia.cc",
"net/http_transport_fuchsia.cc",
+ "process/process_memory_fuchsia.cc",
+ "process/process_memory_fuchsia.h",
]
}
@@ -450,11 +463,16 @@ source_set("util_test") {
"thread/worker_thread_test.cc",
]
- if (!crashpad_is_fuchsia) {
- # TODO(scottmg): This requires an implementation of MultiprocessExec for
- # testing, and a solution to http_transport_test_server.py -- either a port
- # to non-Python, or method of forwarding those requests back to the builder
+ if (!crashpad_is_android && !crashpad_is_fuchsia) {
+ # Android and Fuchsia will each require an HTTPTransport implementation
+ # (libcurl isn’t in either’s SDK) and a solution to
+ # http_transport_test_server.py, because Python isn’t available on either.
+ # The latter could be ported to non-Python, or the test server could run on
+ # the build host with a method to forward requests from the device to the
# host.
+ #
+ # TODO(scottmg): Fuchsia will also require an implementation of
+ # MultiprocessExec for testing.
sources += [ "net/http_transport_test.cc" ]
}
@@ -493,7 +511,7 @@ source_set("util_test") {
]
}
- if (crashpad_is_linux) {
+ if (crashpad_is_linux || crashpad_is_android) {
sources += [
"linux/auxiliary_vector_test.cc",
"linux/memory_map_test.cc",
@@ -501,7 +519,11 @@ source_set("util_test") {
"linux/ptrace_broker_test.cc",
"linux/ptracer_test.cc",
"linux/scoped_ptrace_attach_test.cc",
+ ]
+ }
+ if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) {
+ sources += [
# TODO: Port to all platforms.
"process/process_memory_range_test.cc",
"process/process_memory_test.cc",
diff --git a/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h b/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h
index 4208be67f43..94424dd44d3 100644
--- a/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h
+++ b/chromium/third_party/crashpad/crashpad/util/linux/thread_info.h
@@ -176,7 +176,7 @@ union FloatContext {
} fpregs;
// Reflects user_vfp in sys/user.h.
- struct vfp {
+ struct vfp_t {
uint64_t fpregs[32];
uint32_t fpscr;
} vfp;
diff --git a/chromium/third_party/crashpad/crashpad/util/linux/traits.h b/chromium/third_party/crashpad/crashpad/util/linux/traits.h
index ca9576814f6..dc5463d8c6e 100644
--- a/chromium/third_party/crashpad/crashpad/util/linux/traits.h
+++ b/chromium/third_party/crashpad/crashpad/util/linux/traits.h
@@ -26,6 +26,7 @@ struct Traits32 {
using ULong = uint32_t;
using Clock = Long;
using Size = uint32_t;
+ using Char_64Only = Nothing;
using ULong_32Only = ULong;
using ULong_64Only = Nothing;
using UInteger32_64Only = Nothing;
@@ -38,6 +39,7 @@ struct Traits64 {
using ULong = uint64_t;
using Clock = Long;
using Size = uint64_t;
+ using Char_64Only = char;
using ULong_32Only = Nothing;
using ULong_64Only = ULong;
using UInteger32_64Only = uint32_t;
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/address_types.h b/chromium/third_party/crashpad/crashpad/util/misc/address_types.h
index bfce35fcfff..870938d35e0 100644
--- a/chromium/third_party/crashpad/crashpad/util/misc/address_types.h
+++ b/chromium/third_party/crashpad/crashpad/util/misc/address_types.h
@@ -27,6 +27,8 @@
#include "util/win/address_types.h"
#elif defined(OS_LINUX) || defined(OS_ANDROID)
#include "util/linux/address_types.h"
+#elif defined(OS_FUCHSIA)
+#include <zircon/types.h>
#else
#error "Unhandled OS type"
#endif
@@ -58,6 +60,11 @@ using VMSize = WinVMSize;
using VMAddress = LinuxVMAddress;
using VMSize = LinuxVMSize;
+#elif defined(OS_FUCHSIA)
+
+using VMAddress = zx_vaddr_t;
+using VMSize = size_t;
+
#endif
//! \brief Type used to represent an offset from a VMAddress, potentially
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h b/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h
index 8afd0ef3462..ba673ae5c29 100644
--- a/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h
+++ b/chromium/third_party/crashpad/crashpad/util/misc/as_underlying_type.h
@@ -24,7 +24,8 @@ namespace crashpad {
//! \param[in] from The value to be casted.
//! \return \a from casted to its underlying type.
template <typename From>
-typename std::underlying_type<From>::type AsUnderlyingType(From from) {
+constexpr typename std::underlying_type<From>::type AsUnderlyingType(
+ From from) {
return static_cast<typename std::underlying_type<From>::type>(from);
}
diff --git a/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc b/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc
index 8baa0d9f1b6..09084483b74 100644
--- a/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc
+++ b/chromium/third_party/crashpad/crashpad/util/misc/paths_fuchsia.cc
@@ -26,9 +26,8 @@ namespace crashpad {
bool Paths::Executable(base::FilePath* path) {
// Assume the environment has been set up following
// https://fuchsia.googlesource.com/docs/+/master/namespaces.md#typical-directory-structure
- // . The actual executable name is not known, but it's conceptually in this
- // location.
- *path = base::FilePath("/pkg/bin/unknown");
+ // .
+ *path = base::FilePath("/pkg/bin/app");
return true;
}
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory.cc
new file mode 100644
index 00000000000..6bc0010194a
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory.cc
@@ -0,0 +1,78 @@
+// 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 "util/process/process_memory.h"
+
+#include "base/logging.h"
+
+namespace crashpad {
+
+bool ProcessMemory::Read(VMAddress address, size_t size, void* buffer) const {
+ char* buffer_c = static_cast<char*>(buffer);
+ while (size > 0) {
+ ssize_t bytes_read = ReadUpTo(address, size, buffer_c);
+ if (bytes_read < 0) {
+ return false;
+ }
+ if (bytes_read == 0) {
+ LOG(ERROR) << "short read";
+ return false;
+ }
+ DCHECK_LE(static_cast<size_t>(bytes_read), size);
+ size -= bytes_read;
+ address += bytes_read;
+ buffer_c += bytes_read;
+ }
+ return true;
+}
+
+bool ProcessMemory::ReadCStringInternal(VMAddress address,
+ bool has_size,
+ size_t size,
+ std::string* string) const {
+ string->clear();
+
+ char buffer[4096];
+ do {
+ size_t read_size;
+ if (has_size) {
+ read_size = std::min(sizeof(buffer), size);
+ } else {
+ read_size = sizeof(buffer);
+ }
+
+ ssize_t bytes_read = ReadUpTo(address, read_size, buffer);
+ if (bytes_read < 0) {
+ return false;
+ }
+ if (bytes_read == 0) {
+ break;
+ }
+
+ char* nul = static_cast<char*>(memchr(buffer, '\0', bytes_read));
+ if (nul != nullptr) {
+ string->append(buffer, nul - buffer);
+ return true;
+ }
+ string->append(buffer, bytes_read);
+
+ address += bytes_read;
+ size -= bytes_read;
+ } while (!has_size || size > 0);
+
+ LOG(ERROR) << "unterminated string";
+ return false;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory.h
index c046fc6e789..34561718aa5 100644
--- a/chromium/third_party/crashpad/crashpad/util/process/process_memory.h
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory.h
@@ -40,7 +40,7 @@ class ProcessMemory {
//!
//! \return `true` on success, with \a buffer filled appropriately. `false` on
//! failure, with a message logged.
- virtual bool Read(VMAddress address, size_t size, void* buffer) const = 0;
+ bool Read(VMAddress address, size_t size, void* buffer) const;
//! \brief Reads a `NUL`-terminated C string from the target process into a
//! string in the current process.
@@ -83,6 +83,22 @@ class ProcessMemory {
~ProcessMemory() = default;
private:
+ //! \brief Copies memory from the target process into a caller-provided buffer
+ //! in the current process, up to a maximum number of bytes.
+ //!
+ //! \param[in] address The address, in the target process' address space, of
+ //! the memory region to copy.
+ //! \param[in] size The maximum size, in bytes, of the memory region to copy.
+ //! \a buffer must be at least this size.
+ //! \param[out] buffer The buffer into which the contents of the other
+ //! process' memory will be copied.
+ //!
+ //! \return the number of bytes copied, 0 if there is no more data to read, or
+ //! -1 on failure with a message logged.
+ virtual ssize_t ReadUpTo(VMAddress address,
+ size_t size,
+ void* buffer) const = 0;
+
//! \brief Reads a `NUL`-terminated C string from the target process into a
//! string in the current process.
//!
@@ -102,7 +118,7 @@ class ProcessMemory {
virtual bool ReadCStringInternal(VMAddress address,
bool has_size,
size_t size,
- std::string* string) const = 0;
+ std::string* string) const;
};
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.cc
new file mode 100644
index 00000000000..212e1c6ffe6
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.cc
@@ -0,0 +1,56 @@
+// 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 "util/process/process_memory_fuchsia.h"
+
+#include <zircon/syscalls.h>
+
+#include <limits>
+
+#include "base/logging.h"
+#include "base/fuchsia/fuchsia_logging.h"
+
+namespace crashpad {
+
+ProcessMemoryFuchsia::ProcessMemoryFuchsia()
+ : ProcessMemory(), process_(), initialized_() {}
+
+ProcessMemoryFuchsia::~ProcessMemoryFuchsia() {}
+
+bool ProcessMemoryFuchsia::Initialize(zx_handle_t process) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
+ process_ = process;
+ INITIALIZATION_STATE_SET_VALID(initialized_);
+ return true;
+}
+
+ssize_t ProcessMemoryFuchsia::ReadUpTo(VMAddress address,
+ size_t size,
+ void* buffer) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+ DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()});
+
+ size_t actual;
+ zx_status_t status =
+ zx_process_read_memory(process_, address, buffer, size, &actual);
+
+ if (status != ZX_OK) {
+ ZX_LOG(ERROR, status) << "zx_process_read_memory";
+ return -1;
+ }
+
+ return actual;
+}
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.h
new file mode 100644
index 00000000000..e6cdd3e8c15
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_fuchsia.h
@@ -0,0 +1,56 @@
+// 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_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_
+#define CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_
+
+#include <zircon/types.h>
+
+#include <string>
+
+#include "base/macros.h"
+#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
+#include "util/process/process_memory.h"
+
+namespace crashpad {
+
+//! \brief Accesses the memory of another Fuchsia process.
+class ProcessMemoryFuchsia final : public ProcessMemory {
+ public:
+ ProcessMemoryFuchsia();
+ ~ProcessMemoryFuchsia();
+
+ //! \brief Initializes this object to read the memory of a process by handle.
+ //!
+ //! This method must be called successfully prior to calling any other method
+ //! in this class.
+ //!
+ //! \param[in] process The handle to the target process.
+ //!
+ //! \return `true` on success, `false` on failure with a message logged.
+ bool Initialize(zx_handle_t process);
+
+ private:
+ ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
+
+ zx_handle_t process_;
+ InitializationStateDcheck initialized_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryFuchsia);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_PROCESS_PROCESS_MEMORY_FUCHSIA_H_
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc
index f294ee34ff5..1fc3c05f1ce 100644
--- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.cc
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <algorithm>
+#include <limits>
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
@@ -27,11 +28,12 @@
namespace crashpad {
ProcessMemoryLinux::ProcessMemoryLinux()
- : ProcessMemory(), mem_fd_(), pid_(-1) {}
+ : ProcessMemory(), mem_fd_(), pid_(-1), initialized_() {}
ProcessMemoryLinux::~ProcessMemoryLinux() {}
bool ProcessMemoryLinux::Initialize(pid_t pid) {
+ INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
pid_ = pid;
char path[32];
snprintf(path, sizeof(path), "/proc/%d/mem", pid_);
@@ -40,75 +42,23 @@ bool ProcessMemoryLinux::Initialize(pid_t pid) {
PLOG(ERROR) << "open";
return false;
}
+ INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
-bool ProcessMemoryLinux::Read(VMAddress address,
- size_t size,
- void* buffer) const {
+ssize_t ProcessMemoryLinux::ReadUpTo(VMAddress address,
+ size_t size,
+ void* buffer) const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
DCHECK(mem_fd_.is_valid());
+ DCHECK_LE(size, size_t{std::numeric_limits<ssize_t>::max()});
- char* buffer_c = static_cast<char*>(buffer);
- while (size > 0) {
- ssize_t bytes_read =
- HANDLE_EINTR(pread64(mem_fd_.get(), buffer_c, size, address));
- if (bytes_read < 0) {
- PLOG(ERROR) << "pread64";
- return false;
- }
- if (bytes_read == 0) {
- LOG(ERROR) << "unexpected eof";
- return false;
- }
- DCHECK_LE(static_cast<size_t>(bytes_read), size);
- size -= bytes_read;
- address += bytes_read;
- buffer_c += bytes_read;
+ ssize_t bytes_read =
+ HANDLE_EINTR(pread64(mem_fd_.get(), buffer, size, address));
+ if (bytes_read < 0) {
+ PLOG(ERROR) << "pread64";
}
- return true;
-}
-
-bool ProcessMemoryLinux::ReadCStringInternal(VMAddress address,
- bool has_size,
- size_t size,
- std::string* string) const {
- DCHECK(mem_fd_.is_valid());
-
- string->clear();
-
- char buffer[4096];
- do {
- size_t read_size;
- if (has_size) {
- read_size = std::min(sizeof(buffer), size);
- } else {
- read_size = sizeof(buffer);
- }
- ssize_t bytes_read;
- bytes_read =
- HANDLE_EINTR(pread64(mem_fd_.get(), buffer, read_size, address));
- if (bytes_read < 0) {
- PLOG(ERROR) << "pread64";
- return false;
- }
- if (bytes_read == 0) {
- break;
- }
- DCHECK_LE(static_cast<size_t>(bytes_read), read_size);
-
- char* nul = static_cast<char*>(memchr(buffer, '\0', bytes_read));
- if (nul != nullptr) {
- string->append(buffer, nul - buffer);
- return true;
- }
- string->append(buffer, bytes_read);
-
- address += bytes_read;
- size -= bytes_read;
- } while (!has_size || size > 0);
-
- LOG(ERROR) << "unterminated string";
- return false;
+ return bytes_read;
}
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h
index e03e251f0ff..4fe008bb295 100644
--- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_linux.h
@@ -22,6 +22,7 @@
#include "base/files/scoped_file.h"
#include "base/macros.h"
#include "util/misc/address_types.h"
+#include "util/misc/initialization_state_dcheck.h"
#include "util/process/process_memory.h"
namespace crashpad {
@@ -43,16 +44,12 @@ class ProcessMemoryLinux final : public ProcessMemory {
//! \return `true` on success, `false` on failure with a message logged.
bool Initialize(pid_t pid);
- bool Read(VMAddress address, size_t size, void* buffer) const override;
-
private:
- bool ReadCStringInternal(VMAddress address,
- bool has_size,
- size_t size,
- std::string* string) const override;
+ ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) const override;
base::ScopedFD mem_fd_;
pid_t pid_;
+ InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryLinux);
};
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_native.h b/chromium/third_party/crashpad/crashpad/util/process/process_memory_native.h
new file mode 100644
index 00000000000..19fa805a278
--- /dev/null
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_native.h
@@ -0,0 +1,34 @@
+// 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 "build/build_config.h"
+
+#if defined(OS_FUCHSIA)
+#include "util/process/process_memory_fuchsia.h"
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+#include "util/process/process_memory_linux.h"
+#endif
+
+namespace crashpad {
+
+#if defined(OS_FUCHSIA) || DOXYGEN
+//! \brief Alias for platform-specific native implementation of ProcessMemory.
+using ProcessMemoryNative = ProcessMemoryFuchsia;
+#elif defined(OS_LINUX) || defined(OS_ANDROID)
+using ProcessMemoryNative = ProcessMemoryLinux;
+#else
+#error Port.
+#endif
+
+} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc
index c4cab7e0dde..1c785068628 100644
--- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_range_test.cc
@@ -22,7 +22,14 @@
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "util/misc/from_pointer_cast.h"
+
+#if defined(OS_FUCHSIA)
+#include <zircon/process.h>
+
+#include "util/process/process_memory_fuchsia.h"
+#else
#include "util/process/process_memory_linux.h"
+#endif
namespace crashpad {
namespace test {
@@ -34,6 +41,11 @@ struct TestObject {
} kTestObject = {"string1", "string2"};
TEST(ProcessMemoryRange, Basic) {
+#if defined(OS_FUCHSIA)
+ ProcessMemoryFuchsia memory;
+ ASSERT_TRUE(memory.Initialize(zx_process_self()));
+ constexpr bool is_64_bit = true;
+#else
pid_t pid = getpid();
#if defined(ARCH_CPU_64_BITS)
constexpr bool is_64_bit = true;
@@ -43,6 +55,7 @@ TEST(ProcessMemoryRange, Basic) {
ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(pid));
+#endif // OS_FUCHSIA
ProcessMemoryRange range;
ASSERT_TRUE(range.Initialize(&memory, is_64_bit));
diff --git a/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc b/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc
index 5f631237759..5827638f9ee 100644
--- a/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc
+++ b/chromium/third_party/crashpad/crashpad/util/process/process_memory_test.cc
@@ -23,92 +23,117 @@
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/multiprocess.h"
+#include "test/multiprocess_exec.h"
+#include "test/process_type.h"
#include "util/file/file_io.h"
#include "util/misc/from_pointer_cast.h"
#include "util/posix/scoped_mmap.h"
-#include "util/process/process_memory_linux.h"
+#include "util/process/process_memory_native.h"
namespace crashpad {
namespace test {
namespace {
-class TargetProcessTest : public Multiprocess {
- public:
- TargetProcessTest() : Multiprocess() {}
- ~TargetProcessTest() {}
-
- void RunAgainstSelf() { DoTest(getpid()); }
-
- void RunAgainstForked() { Run(); }
+void DoChildReadTestSetup(size_t* region_size,
+ std::unique_ptr<char[]>* region) {
+ *region_size = 4 * getpagesize();
+ region->reset(new char[*region_size]);
+ for (size_t index = 0; index < *region_size; ++index) {
+ (*region)[index] = index % 256;
+ }
+}
- private:
- void MultiprocessParent() override { DoTest(ChildPID()); }
+CRASHPAD_CHILD_TEST_MAIN(ReadTestChild) {
+ size_t region_size;
+ std::unique_ptr<char[]> region;
+ DoChildReadTestSetup(&region_size, &region);
+ FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+ CheckedWriteFile(out, &region_size, sizeof(region_size));
+ VMAddress address = FromPointerCast<VMAddress>(region.get());
+ CheckedWriteFile(out, &address, sizeof(address));
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
+}
- void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); }
+class ReadTest : public MultiprocessExec {
+ public:
+ ReadTest() : MultiprocessExec() {
+ SetChildTestMainFunction("ReadTestChild");
+ }
- virtual void DoTest(pid_t pid) = 0;
+ void RunAgainstSelf() {
+ size_t region_size;
+ std::unique_ptr<char[]> region;
+ DoChildReadTestSetup(&region_size, &region);
+ DoTest(GetSelfProcess(),
+ region_size,
+ FromPointerCast<VMAddress>(region.get()));
+ }
- DISALLOW_COPY_AND_ASSIGN(TargetProcessTest);
-};
+ void RunAgainstChild() { Run(); }
-class ReadTest : public TargetProcessTest {
- public:
- ReadTest()
- : TargetProcessTest(),
- page_size_(getpagesize()),
- region_size_(4 * page_size_),
- region_(new char[region_size_]) {
- for (size_t index = 0; index < region_size_; ++index) {
- region_[index] = index % 256;
- }
+ private:
+ void MultiprocessParent() override {
+ size_t region_size;
+ VMAddress region;
+ ASSERT_TRUE(
+ ReadFileExactly(ReadPipeHandle(), &region_size, sizeof(region_size)));
+ ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &region, sizeof(region)));
+ DoTest(ChildProcess(), region_size, region);
}
- private:
- void DoTest(pid_t pid) override {
- ProcessMemoryLinux memory;
- ASSERT_TRUE(memory.Initialize(pid));
+ void DoTest(ProcessType process, size_t region_size, VMAddress address) {
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(process));
- VMAddress address = FromPointerCast<VMAddress>(region_.get());
- std::unique_ptr<char[]> result(new char[region_size_]);
+ std::unique_ptr<char[]> result(new char[region_size]);
// Ensure that the entire region can be read.
- ASSERT_TRUE(memory.Read(address, region_size_, result.get()));
- EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_), 0);
+ ASSERT_TRUE(memory.Read(address, region_size, result.get()));
+ for (size_t i = 0; i < region_size; ++i) {
+ EXPECT_EQ(result[i], static_cast<char>(i % 256));
+ }
// Ensure that a read of length 0 succeeds and doesn’t touch the result.
- memset(result.get(), '\0', region_size_);
+ memset(result.get(), '\0', region_size);
ASSERT_TRUE(memory.Read(address, 0, result.get()));
- for (size_t i = 0; i < region_size_; ++i) {
+ for (size_t i = 0; i < region_size; ++i) {
EXPECT_EQ(result[i], 0);
}
// Ensure that a read starting at an unaligned address works.
- ASSERT_TRUE(memory.Read(address + 1, region_size_ - 1, result.get()));
- EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 1), 0);
+ ASSERT_TRUE(memory.Read(address + 1, region_size - 1, result.get()));
+ for (size_t i = 0; i < region_size - 1; ++i) {
+ EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256));
+ }
// Ensure that a read ending at an unaligned address works.
- ASSERT_TRUE(memory.Read(address, region_size_ - 1, result.get()));
- EXPECT_EQ(memcmp(region_.get(), result.get(), region_size_ - 1), 0);
+ ASSERT_TRUE(memory.Read(address, region_size - 1, result.get()));
+ for (size_t i = 0; i < region_size - 1; ++i) {
+ EXPECT_EQ(result[i], static_cast<char>(i % 256));
+ }
// Ensure that a read starting and ending at unaligned addresses works.
- ASSERT_TRUE(memory.Read(address + 1, region_size_ - 2, result.get()));
- EXPECT_EQ(memcmp(region_.get() + 1, result.get(), region_size_ - 2), 0);
+ ASSERT_TRUE(memory.Read(address + 1, region_size - 2, result.get()));
+ for (size_t i = 0; i < region_size - 2; ++i) {
+ EXPECT_EQ(result[i], static_cast<char>((i + 1) % 256));
+ }
// Ensure that a read of exactly one page works.
- ASSERT_TRUE(memory.Read(address + page_size_, page_size_, result.get()));
- EXPECT_EQ(memcmp(region_.get() + page_size_, result.get(), page_size_), 0);
+ size_t page_size = getpagesize();
+ ASSERT_GE(region_size, page_size + page_size);
+ ASSERT_TRUE(memory.Read(address + page_size, page_size, result.get()));
+ for (size_t i = 0; i < page_size; ++i) {
+ EXPECT_EQ(result[i], static_cast<char>((i + page_size) % 256));
+ }
// Ensure that reading exactly a single byte works.
result[1] = 'J';
ASSERT_TRUE(memory.Read(address + 2, 1, result.get()));
- EXPECT_EQ(result[0], region_[2]);
+ EXPECT_EQ(result[0], 2);
EXPECT_EQ(result[1], 'J');
}
- const size_t page_size_;
- const size_t region_size_;
- std::unique_ptr<char[]> region_;
-
DISALLOW_COPY_AND_ASSIGN(ReadTest);
};
@@ -117,96 +142,161 @@ TEST(ProcessMemory, ReadSelf) {
test.RunAgainstSelf();
}
-TEST(ProcessMemory, ReadForked) {
+TEST(ProcessMemory, ReadChild) {
ReadTest test;
- test.RunAgainstForked();
+ test.RunAgainstChild();
}
-bool ReadCString(const ProcessMemory& memory,
- const char* pointer,
- std::string* result) {
- return memory.ReadCString(FromPointerCast<VMAddress>(pointer), result);
+constexpr char kConstCharEmpty[] = "";
+constexpr char kConstCharShort[] = "A short const char[]";
+
+#define SHORT_LOCAL_STRING "A short local variable char[]"
+
+std::string MakeLongString() {
+ std::string long_string;
+ const size_t kStringLongSize = 4 * getpagesize();
+ for (size_t index = 0; index < kStringLongSize; ++index) {
+ long_string.push_back((index % 255) + 1);
+ }
+ EXPECT_EQ(long_string.size(), kStringLongSize);
+ return long_string;
}
-bool ReadCStringSizeLimited(const ProcessMemory& memory,
- const char* pointer,
- size_t size,
- std::string* result) {
- return memory.ReadCStringSizeLimited(
- FromPointerCast<VMAddress>(pointer), size, result);
+void DoChildCStringReadTestSetup(const char** const_empty,
+ const char** const_short,
+ const char** local_empty,
+ const char** local_short,
+ std::string* long_string) {
+ *const_empty = kConstCharEmpty;
+ *const_short = kConstCharShort;
+ *local_empty = "";
+ *local_short = SHORT_LOCAL_STRING;
+ *long_string = MakeLongString();
}
-constexpr char kConstCharEmpty[] = "";
-constexpr char kConstCharShort[] = "A short const char[]";
+CRASHPAD_CHILD_TEST_MAIN(ReadCStringTestChild) {
+ const char* const_empty;
+ const char* const_short;
+ const char* local_empty;
+ const char* local_short;
+ std::string long_string;
+ DoChildCStringReadTestSetup(
+ &const_empty, &const_short, &local_empty, &local_short, &long_string);
+ const auto write_address = [](const char* p) {
+ VMAddress address = FromPointerCast<VMAddress>(p);
+ CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput),
+ &address,
+ sizeof(address));
+ };
+ write_address(const_empty);
+ write_address(const_short);
+ write_address(local_empty);
+ write_address(local_short);
+ write_address(long_string.c_str());
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
+}
-class ReadCStringTest : public TargetProcessTest {
+class ReadCStringTest : public MultiprocessExec {
public:
ReadCStringTest(bool limit_size)
- : TargetProcessTest(),
- member_char_empty_(""),
- member_char_short_("A short member char[]"),
- limit_size_(limit_size) {
- const size_t kStringLongSize = 4 * getpagesize();
- for (size_t index = 0; index < kStringLongSize; ++index) {
- string_long_.push_back((index % 255) + 1);
- }
- EXPECT_EQ(string_long_.size(), kStringLongSize);
+ : MultiprocessExec(), limit_size_(limit_size) {
+ SetChildTestMainFunction("ReadCStringTestChild");
}
+ void RunAgainstSelf() {
+ const char* const_empty;
+ const char* const_short;
+ const char* local_empty;
+ const char* local_short;
+ std::string long_string;
+ DoChildCStringReadTestSetup(
+ &const_empty, &const_short, &local_empty, &local_short, &long_string);
+ DoTest(GetSelfProcess(),
+ FromPointerCast<VMAddress>(const_empty),
+ FromPointerCast<VMAddress>(const_short),
+ FromPointerCast<VMAddress>(local_empty),
+ FromPointerCast<VMAddress>(local_short),
+ FromPointerCast<VMAddress>(long_string.c_str()));
+ }
+ void RunAgainstChild() { Run(); }
+
private:
- void DoTest(pid_t pid) override {
- ProcessMemoryLinux memory;
- ASSERT_TRUE(memory.Initialize(pid));
+ void MultiprocessParent() override {
+#define DECLARE_AND_READ_ADDRESS(name) \
+ VMAddress name; \
+ ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &name, sizeof(name)));
+ DECLARE_AND_READ_ADDRESS(const_empty_address);
+ DECLARE_AND_READ_ADDRESS(const_short_address);
+ DECLARE_AND_READ_ADDRESS(local_empty_address);
+ DECLARE_AND_READ_ADDRESS(local_short_address);
+ DECLARE_AND_READ_ADDRESS(long_string_address);
+#undef DECLARE_AND_READ_ADDRESS
+
+ DoTest(ChildProcess(),
+ const_empty_address,
+ const_short_address,
+ local_empty_address,
+ local_short_address,
+ long_string_address);
+ }
+
+ void DoTest(ProcessType process,
+ VMAddress const_empty_address,
+ VMAddress const_short_address,
+ VMAddress local_empty_address,
+ VMAddress local_short_address,
+ VMAddress long_string_address) {
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(process));
std::string result;
if (limit_size_) {
- ASSERT_TRUE(ReadCStringSizeLimited(
- memory, kConstCharEmpty, arraysize(kConstCharEmpty), &result));
+ ASSERT_TRUE(memory.ReadCStringSizeLimited(
+ const_empty_address, arraysize(kConstCharEmpty), &result));
EXPECT_EQ(result, kConstCharEmpty);
- ASSERT_TRUE(ReadCStringSizeLimited(
- memory, kConstCharShort, arraysize(kConstCharShort), &result));
+ ASSERT_TRUE(memory.ReadCStringSizeLimited(
+ const_short_address, arraysize(kConstCharShort), &result));
EXPECT_EQ(result, kConstCharShort);
- EXPECT_FALSE(ReadCStringSizeLimited(
- memory, kConstCharShort, arraysize(kConstCharShort) - 1, &result));
-
- ASSERT_TRUE(ReadCStringSizeLimited(
- memory, member_char_empty_, strlen(member_char_empty_) + 1, &result));
- EXPECT_EQ(result, member_char_empty_);
-
- ASSERT_TRUE(ReadCStringSizeLimited(
- memory, member_char_short_, strlen(member_char_short_) + 1, &result));
- EXPECT_EQ(result, member_char_short_);
- EXPECT_FALSE(ReadCStringSizeLimited(
- memory, member_char_short_, strlen(member_char_short_), &result));
-
- ASSERT_TRUE(ReadCStringSizeLimited(
- memory, string_long_.c_str(), string_long_.size() + 1, &result));
- EXPECT_EQ(result, string_long_);
- EXPECT_FALSE(ReadCStringSizeLimited(
- memory, string_long_.c_str(), string_long_.size(), &result));
+ EXPECT_FALSE(memory.ReadCStringSizeLimited(
+ const_short_address, arraysize(kConstCharShort) - 1, &result));
+
+ ASSERT_TRUE(
+ memory.ReadCStringSizeLimited(local_empty_address, 1, &result));
+ EXPECT_EQ(result, "");
+
+ ASSERT_TRUE(memory.ReadCStringSizeLimited(
+ local_short_address, strlen(SHORT_LOCAL_STRING) + 1, &result));
+ EXPECT_EQ(result, SHORT_LOCAL_STRING);
+ EXPECT_FALSE(memory.ReadCStringSizeLimited(
+ local_short_address, strlen(SHORT_LOCAL_STRING), &result));
+
+ std::string long_string_for_comparison = MakeLongString();
+ ASSERT_TRUE(memory.ReadCStringSizeLimited(
+ long_string_address, long_string_for_comparison.size() + 1, &result));
+ EXPECT_EQ(result, long_string_for_comparison);
+ EXPECT_FALSE(memory.ReadCStringSizeLimited(
+ long_string_address, long_string_for_comparison.size(), &result));
} else {
- ASSERT_TRUE(ReadCString(memory, kConstCharEmpty, &result));
+ ASSERT_TRUE(memory.ReadCString(const_empty_address, &result));
EXPECT_EQ(result, kConstCharEmpty);
- ASSERT_TRUE(ReadCString(memory, kConstCharShort, &result));
+ ASSERT_TRUE(memory.ReadCString(const_short_address, &result));
EXPECT_EQ(result, kConstCharShort);
- ASSERT_TRUE(ReadCString(memory, member_char_empty_, &result));
- EXPECT_EQ(result, member_char_empty_);
+ ASSERT_TRUE(memory.ReadCString(local_empty_address, &result));
+ EXPECT_EQ(result, "");
- ASSERT_TRUE(ReadCString(memory, member_char_short_, &result));
- EXPECT_EQ(result, member_char_short_);
+ ASSERT_TRUE(memory.ReadCString(local_short_address, &result));
+ EXPECT_EQ(result, SHORT_LOCAL_STRING);
- ASSERT_TRUE(ReadCString(memory, string_long_.c_str(), &result));
- EXPECT_EQ(result, string_long_);
+ ASSERT_TRUE(memory.ReadCString(long_string_address, &result));
+ EXPECT_EQ(result, MakeLongString());
}
}
- std::string string_long_;
- const char* member_char_empty_;
- const char* member_char_short_;
const bool limit_size_;
DISALLOW_COPY_AND_ASSIGN(ReadCStringTest);
@@ -217,9 +307,9 @@ TEST(ProcessMemory, ReadCStringSelf) {
test.RunAgainstSelf();
}
-TEST(ProcessMemory, ReadCStringForked) {
+TEST(ProcessMemory, ReadCStringChild) {
ReadCStringTest test(/* limit_size= */ false);
- test.RunAgainstForked();
+ test.RunAgainstChild();
}
TEST(ProcessMemory, ReadCStringSizeLimitedSelf) {
@@ -227,56 +317,96 @@ TEST(ProcessMemory, ReadCStringSizeLimitedSelf) {
test.RunAgainstSelf();
}
-TEST(ProcessMemory, ReadCStringSizeLimitedForked) {
+TEST(ProcessMemory, ReadCStringSizeLimitedChild) {
ReadCStringTest test(/* limit_size= */ true);
- test.RunAgainstForked();
+ test.RunAgainstChild();
}
-class ReadUnmappedTest : public TargetProcessTest {
- public:
- ReadUnmappedTest()
- : TargetProcessTest(),
- page_size_(getpagesize()),
- region_size_(2 * page_size_),
- result_(new char[region_size_]) {
- if (!pages_.ResetMmap(nullptr,
- region_size_,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1,
- 0)) {
- ADD_FAILURE();
- return;
- }
+void DoReadUnmappedChildMainSetup(ScopedMmap* pages,
+ VMAddress* address,
+ size_t* page_size,
+ size_t* region_size) {
+ *page_size = getpagesize();
+ *region_size = 2 * (*page_size);
+ if (!pages->ResetMmap(nullptr,
+ *region_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0)) {
+ ADD_FAILURE();
+ return;
+ }
- char* region = pages_.addr_as<char*>();
- for (size_t index = 0; index < region_size_; ++index) {
- region[index] = index % 256;
- }
+ *address = pages->addr_as<VMAddress>();
- EXPECT_TRUE(pages_.ResetAddrLen(region, page_size_));
+ char* region = pages->addr_as<char*>();
+ for (size_t index = 0; index < *region_size; ++index) {
+ region[index] = index % 256;
}
- private:
- void DoTest(pid_t pid) override {
- ProcessMemoryLinux memory;
- ASSERT_TRUE(memory.Initialize(pid));
+ EXPECT_TRUE(pages->ResetAddrLen(region, *page_size));
+}
- VMAddress page_addr1 = pages_.addr_as<VMAddress>();
- VMAddress page_addr2 = page_addr1 + page_size_;
+CRASHPAD_CHILD_TEST_MAIN(ReadUnmappedChildMain) {
+ ScopedMmap pages;
+ VMAddress address;
+ size_t page_size, region_size;
+ DoReadUnmappedChildMainSetup(&pages, &address, &page_size, &region_size);
+ FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+ CheckedWriteFile(out, &address, sizeof(address));
+ CheckedWriteFile(out, &page_size, sizeof(page_size));
+ CheckedWriteFile(out, &region_size, sizeof(region_size));
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
+}
+
+class ReadUnmappedTest : public MultiprocessExec {
+ public:
+ ReadUnmappedTest() : MultiprocessExec() {
+ SetChildTestMainFunction("ReadUnmappedChildMain");
+ }
+
+ void RunAgainstSelf() {
+ ScopedMmap pages;
+ VMAddress address;
+ size_t page_size, region_size;
+ DoReadUnmappedChildMainSetup(&pages, &address, &page_size, &region_size);
+ DoTest(GetSelfProcess(), address, page_size, region_size);
+ }
- EXPECT_TRUE(memory.Read(page_addr1, page_size_, result_.get()));
- EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result_.get()));
+ void RunAgainstChild() { Run(); }
- EXPECT_FALSE(memory.Read(page_addr1, region_size_, result_.get()));
- EXPECT_FALSE(memory.Read(page_addr2, page_size_, result_.get()));
- EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result_.get()));
+ private:
+ void MultiprocessParent() override {
+ VMAddress address;
+ size_t page_size, region_size;
+ ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &address, sizeof(address)));
+ ASSERT_TRUE(
+ ReadFileExactly(ReadPipeHandle(), &page_size, sizeof(page_size)));
+ ASSERT_TRUE(
+ ReadFileExactly(ReadPipeHandle(), &region_size, sizeof(region_size)));
+ DoTest(ChildProcess(), address, page_size, region_size);
}
- ScopedMmap pages_;
- const size_t page_size_;
- const size_t region_size_;
- std::unique_ptr<char[]> result_;
+ void DoTest(ProcessType process,
+ VMAddress address,
+ size_t page_size,
+ size_t region_size) {
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(process));
+
+ VMAddress page_addr1 = address;
+ VMAddress page_addr2 = page_addr1 + page_size;
+
+ std::unique_ptr<char[]> result(new char[region_size]);
+ EXPECT_TRUE(memory.Read(page_addr1, page_size, result.get()));
+ EXPECT_TRUE(memory.Read(page_addr2 - 1, 1, result.get()));
+
+ EXPECT_FALSE(memory.Read(page_addr1, region_size, result.get()));
+ EXPECT_FALSE(memory.Read(page_addr2, page_size, result.get()));
+ EXPECT_FALSE(memory.Read(page_addr2 - 1, 2, result.get()));
+ }
DISALLOW_COPY_AND_ASSIGN(ReadUnmappedTest);
};
@@ -287,90 +417,158 @@ TEST(ProcessMemory, ReadUnmappedSelf) {
test.RunAgainstSelf();
}
-TEST(ProcessMemory, ReadUnmappedForked) {
+TEST(ProcessMemory, ReadUnmappedChild) {
ReadUnmappedTest test;
ASSERT_FALSE(testing::Test::HasFailure());
- test.RunAgainstForked();
+ test.RunAgainstChild();
}
-class ReadCStringUnmappedTest : public TargetProcessTest {
+constexpr size_t kChildProcessStringLength = 10;
+
+class StringDataInChildProcess {
public:
- ReadCStringUnmappedTest(bool limit_size)
- : TargetProcessTest(),
- page_size_(getpagesize()),
- region_size_(2 * page_size_),
- limit_size_(limit_size) {
- if (!pages_.ResetMmap(nullptr,
- region_size_,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1,
- 0)) {
- ADD_FAILURE();
- return;
- }
+ // This constructor only makes sense in the child process.
+ explicit StringDataInChildProcess(const char* cstring)
+ : address_(FromPointerCast<VMAddress>(cstring)) {
+ memcpy(expected_value_, cstring, kChildProcessStringLength + 1);
+ }
- char* region = pages_.addr_as<char*>();
- for (size_t index = 0; index < region_size_; ++index) {
- region[index] = 1 + index % 255;
- }
+ void Write(FileHandle out) {
+ CheckedWriteFile(out, &address_, sizeof(address_));
+ CheckedWriteFile(out, &expected_value_, sizeof(expected_value_));
+ }
+
+ static StringDataInChildProcess Read(FileHandle in) {
+ StringDataInChildProcess str;
+ EXPECT_TRUE(ReadFileExactly(in, &str.address_, sizeof(str.address_)));
+ EXPECT_TRUE(
+ ReadFileExactly(in, &str.expected_value_, sizeof(str.expected_value_)));
+ return str;
+ }
+
+ VMAddress address() const { return address_; }
+ std::string expected_value() const { return expected_value_; }
- // A string at the start of the mapped region
- string1_ = region;
- string1_[expected_length_] = '\0';
+ private:
+ StringDataInChildProcess() : address_(0), expected_value_() {}
- // A string near the end of the mapped region
- string2_ = region + page_size_ - expected_length_ * 2;
- string2_[expected_length_] = '\0';
+ VMAddress address_;
+ char expected_value_[kChildProcessStringLength + 1];
+};
+
+void DoCStringUnmappedTestSetup(
+ ScopedMmap* pages,
+ std::vector<StringDataInChildProcess>* strings) {
+ const size_t page_size = getpagesize();
+ const size_t region_size = 2 * page_size;
+ if (!pages->ResetMmap(nullptr,
+ region_size,
+ PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS,
+ -1,
+ 0)) {
+ ADD_FAILURE();
+ return;
+ }
+
+ char* region = pages->addr_as<char*>();
+ for (size_t index = 0; index < region_size; ++index) {
+ region[index] = 1 + index % 255;
+ }
+
+ // A string at the start of the mapped region
+ char* string1 = region;
+ string1[kChildProcessStringLength] = '\0';
+
+ // A string near the end of the mapped region
+ char* string2 = region + page_size - kChildProcessStringLength * 2;
+ string2[kChildProcessStringLength] = '\0';
+
+ // A string that crosses from the mapped into the unmapped region
+ char* string3 = region + page_size - kChildProcessStringLength + 1;
+ string3[kChildProcessStringLength] = '\0';
+
+ // A string entirely in the unmapped region
+ char* string4 = region + page_size + 10;
+ string4[kChildProcessStringLength] = '\0';
+
+ strings->push_back(StringDataInChildProcess(string1));
+ strings->push_back(StringDataInChildProcess(string2));
+ strings->push_back(StringDataInChildProcess(string3));
+ strings->push_back(StringDataInChildProcess(string4));
- // A string that crosses from the mapped into the unmapped region
- string3_ = region + page_size_ - expected_length_ + 1;
- string3_[expected_length_] = '\0';
+ EXPECT_TRUE(pages->ResetAddrLen(region, page_size));
+}
- // A string entirely in the unmapped region
- string4_ = region + page_size_ + 10;
- string4_[expected_length_] = '\0';
+CRASHPAD_CHILD_TEST_MAIN(ReadCStringUnmappedChildMain) {
+ ScopedMmap pages;
+ std::vector<StringDataInChildProcess> strings;
+ DoCStringUnmappedTestSetup(&pages, &strings);
+ FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
+ strings[0].Write(out);
+ strings[1].Write(out);
+ strings[2].Write(out);
+ strings[3].Write(out);
+ CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput));
+ return 0;
+}
- result_.reserve(expected_length_ + 1);
+class ReadCStringUnmappedTest : public MultiprocessExec {
+ public:
+ ReadCStringUnmappedTest(bool limit_size)
+ : MultiprocessExec(), limit_size_(limit_size) {
+ SetChildTestMainFunction("ReadCStringUnmappedChildMain");
+ }
- EXPECT_TRUE(pages_.ResetAddrLen(region, page_size_));
+ void RunAgainstSelf() {
+ ScopedMmap pages;
+ std::vector<StringDataInChildProcess> strings;
+ DoCStringUnmappedTestSetup(&pages, &strings);
+ DoTest(GetSelfProcess(), strings);
}
+ void RunAgainstChild() { Run(); }
+
private:
- void DoTest(pid_t pid) {
- ProcessMemoryLinux memory;
- ASSERT_TRUE(memory.Initialize(pid));
+ void MultiprocessParent() override {
+ std::vector<StringDataInChildProcess> strings;
+ strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
+ strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
+ strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
+ strings.push_back(StringDataInChildProcess::Read(ReadPipeHandle()));
+ ASSERT_NO_FATAL_FAILURE();
+ DoTest(ChildProcess(), strings);
+ }
+
+ void DoTest(ProcessType process,
+ const std::vector<StringDataInChildProcess>& strings) {
+ ProcessMemoryNative memory;
+ ASSERT_TRUE(memory.Initialize(process));
+
+ std::string result;
+ result.reserve(kChildProcessStringLength + 1);
if (limit_size_) {
- ASSERT_TRUE(ReadCStringSizeLimited(
- memory, string1_, expected_length_ + 1, &result_));
- EXPECT_EQ(result_, string1_);
- ASSERT_TRUE(ReadCStringSizeLimited(
- memory, string2_, expected_length_ + 1, &result_));
- EXPECT_EQ(result_, string2_);
- EXPECT_FALSE(ReadCStringSizeLimited(
- memory, string3_, expected_length_ + 1, &result_));
- EXPECT_FALSE(ReadCStringSizeLimited(
- memory, string4_, expected_length_ + 1, &result_));
+ ASSERT_TRUE(memory.ReadCStringSizeLimited(
+ strings[0].address(), kChildProcessStringLength + 1, &result));
+ EXPECT_EQ(result, strings[0].expected_value());
+ ASSERT_TRUE(memory.ReadCStringSizeLimited(
+ strings[1].address(), kChildProcessStringLength + 1, &result));
+ EXPECT_EQ(result, strings[1].expected_value());
+ EXPECT_FALSE(memory.ReadCStringSizeLimited(
+ strings[2].address(), kChildProcessStringLength + 1, &result));
+ EXPECT_FALSE(memory.ReadCStringSizeLimited(
+ strings[3].address(), kChildProcessStringLength + 1, &result));
} else {
- ASSERT_TRUE(ReadCString(memory, string1_, &result_));
- EXPECT_EQ(result_, string1_);
- ASSERT_TRUE(ReadCString(memory, string2_, &result_));
- EXPECT_EQ(result_, string2_);
- EXPECT_FALSE(ReadCString(memory, string3_, &result_));
- EXPECT_FALSE(ReadCString(memory, string4_, &result_));
+ ASSERT_TRUE(memory.ReadCString(strings[0].address(), &result));
+ EXPECT_EQ(result, strings[0].expected_value());
+ ASSERT_TRUE(memory.ReadCString(strings[1].address(), &result));
+ EXPECT_EQ(result, strings[1].expected_value());
+ EXPECT_FALSE(memory.ReadCString(strings[2].address(), &result));
+ EXPECT_FALSE(memory.ReadCString(strings[3].address(), &result));
}
}
- std::string result_;
- ScopedMmap pages_;
- const size_t page_size_;
- const size_t region_size_;
- static const size_t expected_length_ = 10;
- char* string1_;
- char* string2_;
- char* string3_;
- char* string4_;
const bool limit_size_;
DISALLOW_COPY_AND_ASSIGN(ReadCStringUnmappedTest);
@@ -382,10 +580,10 @@ TEST(ProcessMemory, ReadCStringUnmappedSelf) {
test.RunAgainstSelf();
}
-TEST(ProcessMemory, ReadCStringUnmappedForked) {
+TEST(ProcessMemory, ReadCStringUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ false);
ASSERT_FALSE(testing::Test::HasFailure());
- test.RunAgainstForked();
+ test.RunAgainstChild();
}
TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) {
@@ -394,10 +592,10 @@ TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedSelf) {
test.RunAgainstSelf();
}
-TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedForked) {
+TEST(ProcessMemory, ReadCStringSizeLimitedUnmappedChild) {
ReadCStringUnmappedTest test(/* limit_size= */ true);
ASSERT_FALSE(testing::Test::HasFailure());
- test.RunAgainstForked();
+ test.RunAgainstChild();
}
} // namespace
diff --git a/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc b/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc
index 9aedb2999bd..797a3ac4f8e 100644
--- a/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc
+++ b/chromium/third_party/crashpad/crashpad/util/stdlib/aligned_allocator.cc
@@ -18,7 +18,7 @@
#include "build/build_config.h"
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER)
#include <stdlib.h>
#elif defined(OS_WIN)
#include <malloc.h>
@@ -31,7 +31,7 @@ namespace {
// library to do so. This works even if C++ exceptions are disabled, causing
// program termination if uncaught.
void ThrowBadAlloc() {
-#if defined(OS_POSIX)
+#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER)
// This works with both libc++ and libstdc++.
std::__throw_bad_alloc();
#elif defined(OS_WIN)
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h
index 49479639941..dc3000b09e1 100644
--- a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore.h
@@ -23,6 +23,9 @@
#include <dispatch/dispatch.h>
#elif defined(OS_WIN)
#include <windows.h>
+#elif defined(OS_ANDROID)
+#include <condition_variable>
+#include <mutex>
#else
#include <semaphore.h>
#endif
@@ -77,6 +80,10 @@ class Semaphore {
dispatch_semaphore_t semaphore_;
#elif defined(OS_WIN)
HANDLE semaphore_;
+#elif defined(OS_ANDROID)
+ std::condition_variable cv_;
+ std::mutex mutex_;
+ int value_;
#else
sem_t semaphore_;
#endif
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc
index 59ed71f2c50..3875f3f4336 100644
--- a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_posix.cc
@@ -18,13 +18,51 @@
#include <math.h>
#include <time.h>
+#include <chrono>
+
#include "base/logging.h"
#include "base/posix/eintr_wrapper.h"
#include "util/misc/time.h"
namespace crashpad {
-#if !defined(OS_MACOSX)
+#if defined(OS_ANDROID)
+
+Semaphore::Semaphore(int value) : cv_(), mutex_(), value_(value) {}
+
+Semaphore::~Semaphore() = default;
+
+void Semaphore::Wait() {
+ std::unique_lock<std::mutex> lock(mutex_);
+ cv_.wait(lock, [this] { return this->value_ > 0; });
+ --value_;
+}
+
+bool Semaphore::TimedWait(double seconds) {
+ DCHECK_GE(seconds, 0.0);
+
+ if (isinf(seconds)) {
+ Wait();
+ return true;
+ }
+
+ std::unique_lock<std::mutex> lock(mutex_);
+ if (!cv_.wait_for(lock, std::chrono::duration<double>(seconds), [this] {
+ return this->value_ > 0;
+ })) {
+ return false;
+ }
+ --value_;
+ return true;
+}
+
+void Semaphore::Signal() {
+ std::lock_guard<std::mutex> lock(mutex_);
+ ++value_;
+ cv_.notify_one();
+}
+
+#elif !defined(OS_MACOSX)
Semaphore::Semaphore(int value) {
PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init";
@@ -65,6 +103,6 @@ void Semaphore::Signal() {
PCHECK(sem_post(&semaphore_) == 0) << "sem_post";
}
-#endif
+#endif // OS_ANDROID
} // namespace crashpad
diff --git a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
index e1f3648c3b9..10f7546d6bd 100644
--- a/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
+++ b/chromium/third_party/crashpad/crashpad/util/synchronization/semaphore_test.cc
@@ -41,7 +41,10 @@ TEST(Semaphore, TimedWait) {
TEST(Semaphore, TimedWaitTimeout) {
Semaphore semaphore(0);
- EXPECT_FALSE(semaphore.TimedWait(0.01)); // 10ms
+ semaphore.Signal();
+ constexpr double kTenMs = 0.01;
+ EXPECT_TRUE(semaphore.TimedWait(kTenMs));
+ EXPECT_FALSE(semaphore.TimedWait(kTenMs));
}
TEST(Semaphore, TimedWaitInfinite_0) {
diff --git a/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
index 60be08b5b33..60c824834ac 100644
--- a/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
+++ b/chromium/third_party/crashpad/crashpad/util/thread/thread_log_messages.cc
@@ -46,10 +46,6 @@ class ThreadLogMessagesMaster {
private:
ThreadLogMessagesMaster() {
- DCHECK(!tls_.initialized());
- tls_.Initialize(nullptr);
- DCHECK(tls_.initialized());
-
DCHECK(!logging::GetLogMessageHandler());
logging::SetLogMessageHandler(LogMessageHandler);
}
@@ -62,7 +58,7 @@ class ThreadLogMessagesMaster {
size_t message_start,
const std::string& string) {
std::vector<std::string>* log_messages =
- reinterpret_cast<std::vector<std::string>*>(tls_.Get());
+ reinterpret_cast<std::vector<std::string>*>(GetInstance()->tls_.Get());
if (log_messages) {
log_messages->push_back(string);
}
@@ -72,15 +68,11 @@ class ThreadLogMessagesMaster {
return false;
}
- static base::ThreadLocalStorage::StaticSlot tls_;
+ base::ThreadLocalStorage::Slot tls_;
DISALLOW_COPY_AND_ASSIGN(ThreadLogMessagesMaster);
};
-// static
-base::ThreadLocalStorage::StaticSlot ThreadLogMessagesMaster::tls_
- = TLS_INITIALIZER;
-
} // namespace
ThreadLogMessages::ThreadLogMessages() : log_messages_() {
diff --git a/chromium/third_party/crashpad/crashpad/util/util.gyp b/chromium/third_party/crashpad/crashpad/util/util.gyp
index 43b6e32b608..0000ee2d0d2 100644
--- a/chromium/third_party/crashpad/crashpad/util/util.gyp
+++ b/chromium/third_party/crashpad/crashpad/util/util.gyp
@@ -199,9 +199,11 @@
'posix/signals.h',
'posix/symbolic_constants_posix.cc',
'posix/symbolic_constants_posix.h',
+ 'process/process_memory.cc',
'process/process_memory.h',
'process/process_memory_linux.cc',
'process/process_memory_linux.h',
+ 'process/process_memory_native.h',
'process/process_memory_range.cc',
'process/process_memory_range.h',
'stdlib/aligned_allocator.cc',
diff --git a/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h b/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h
index a2a88b77444..dad04056912 100644
--- a/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h
+++ b/chromium/third_party/crashpad/crashpad/util/win/nt_internals.h
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#ifndef CRASHPAD_UTIL_WIN_NT_INTERNALS_H_
+#define CRASHPAD_UTIL_WIN_NT_INTERNALS_H_
+
#include <windows.h>
#include <winternl.h>
@@ -92,3 +95,5 @@ void RtlGetUnloadEventTraceEx(ULONG** element_size,
void** event_trace);
} // namespace crashpad
+
+#endif // CRASHPAD_UTIL_WIN_NT_INTERNALS_H_