Merge master 42b57efa554a into doc

This commit is contained in:
Mark Mentovai 2018-07-31 22:57:53 -04:00
commit 07e174cd8d
33 changed files with 872 additions and 295 deletions

205
DEPS
View File

@ -37,6 +37,90 @@ deps = {
'crashpad/third_party/zlib/zlib': 'crashpad/third_party/zlib/zlib':
Var('chromium_git') + '/chromium/src/third_party/zlib@' + Var('chromium_git') + '/chromium/src/third_party/zlib@' +
'13dc246a58e4b72104d35f9b1809af95221ebda7', '13dc246a58e4b72104d35f9b1809af95221ebda7',
# CIPD packages below.
'crashpad/third_party/linux/clang/linux-amd64': {
'packages': [
{
'package': 'fuchsia/clang/linux-amd64',
'version': 'goma',
},
],
'condition': 'checkout_linux and pull_linux_clang',
'dep_type': 'cipd'
},
'crashpad/third_party/linux/clang/mac-amd64': {
'packages': [
{
'package': 'fuchsia/clang/mac-amd64',
'version': 'goma',
},
],
'condition': 'checkout_fuchsia and host_os == "mac"',
'dep_type': 'cipd'
},
'crashpad/third_party/fuchsia/clang/linux-amd64': {
'packages': [
{
'package': 'fuchsia/clang/linux-amd64',
'version': 'goma',
},
],
'condition': 'checkout_fuchsia and host_os == "linux"',
'dep_type': 'cipd'
},
'crashpad/third_party/fuchsia/qemu/mac-amd64': {
'packages': [
{
'package': 'fuchsia/qemu/mac-amd64',
'version': 'latest'
},
],
'condition': 'checkout_fuchsia and host_os == "mac"',
'dep_type': 'cipd'
},
'crashpad/third_party/fuchsia/qemu/linux-amd64': {
'packages': [
{
'package': 'fuchsia/qemu/linux-amd64',
'version': 'latest'
},
],
'condition': 'checkout_fuchsia and host_os == "linux"',
'dep_type': 'cipd'
},
'crashpad/third_party/fuchsia/sdk/linux-amd64': {
# The SDK is keyed to the host system because it contains build tools.
# Currently, linux-amd64 is the only SDK published (see
# https://chrome-infra-packages.appspot.com/#/?path=fuchsia/sdk).
# As long as this is the case, use that SDK package
# even on other build hosts.
# The sysroot (containing headers and libraries) and other components are
# related to the target and should be functional with an appropriate
# toolchain that runs on the build host (fuchsia_clang, above).
'packages': [
{
'package': 'fuchsia/sdk/linux-amd64',
'version': 'latest'
},
],
'condition': 'checkout_fuchsia',
'dep_type': 'cipd'
},
'crashpad/third_party/win/toolchain': {
# This package is only updated when the solution in .gclient includes an
# entry like:
# "custom_vars": { "pull_win_toolchain": True }
# This is because the contained bits are not redistributable.
'packages': [
{
'package': 'chrome_internal/third_party/sdk/windows',
'version': 'uploaded:2018-06-13'
},
],
'condition': 'checkout_win and pull_win_toolchain',
'dep_type': 'cipd'
},
} }
hooks = [ hooks = [
@ -118,28 +202,6 @@ hooks = [
'buildtools/win/gn.exe.sha1', 'buildtools/win/gn.exe.sha1',
], ],
}, },
{
# This uses “cipd install” so that mac-amd64 and linux-amd64 can coexist
# peacefully. “cipd ensure” would remove the macOS package when running on a
# Linux build host and vice-versa. https://crbug.com/789364. This package is
# only updated when the solution in .gclient includes an entry like:
# "custom_vars": { "pull_linux_clang": True }
# The ref used is "goma". This is like "latest", but is considered a more
# stable latest by the Fuchsia toolchain team.
'name': 'clang_linux',
'pattern': '.',
'condition': 'checkout_linux and pull_linux_clang',
'action': [
'cipd',
'install',
# sic, using Fuchsia team's generic build of clang for linux-amd64 to
# build for linux-amd64 target too.
'fuchsia/clang/linux-amd64',
'goma',
'-root', 'crashpad/third_party/linux/clang/linux-amd64',
'-log-level', 'info',
],
},
{ {
# If using a local clang ("pull_linux_clang" above), also pull down a # If using a local clang ("pull_linux_clang" above), also pull down a
# sysroot. # sysroot.
@ -150,105 +212,6 @@ hooks = [
'crashpad/build/install_linux_sysroot.py', 'crashpad/build/install_linux_sysroot.py',
], ],
}, },
{
# Same rationale for using "install" rather than "ensure" as for first clang
# package. https://crbug.com/789364.
# Same rationale for using "goma" instead of "latest" as clang_linux above.
'name': 'fuchsia_clang_mac',
'pattern': '.',
'condition': 'checkout_fuchsia and host_os == "mac"',
'action': [
'cipd',
'install',
'fuchsia/clang/mac-amd64',
'goma',
'-root', 'crashpad/third_party/fuchsia/clang/mac-amd64',
'-log-level', 'info',
],
},
{
# Same rationale for using "install" rather than "ensure" as for first clang
# package. https://crbug.com/789364.
# Same rationale for using "goma" instead of "latest" as clang_linux above.
'name': 'fuchsia_clang_linux',
'pattern': '.',
'condition': 'checkout_fuchsia and host_os == "linux"',
'action': [
'cipd',
'install',
'fuchsia/clang/linux-amd64',
'goma',
'-root', 'crashpad/third_party/fuchsia/clang/linux-amd64',
'-log-level', 'info',
],
},
{
# Same rationale for using "install" rather than "ensure" as for clang
# packages. https://crbug.com/789364.
'name': 'fuchsia_qemu_mac',
'pattern': '.',
'condition': 'checkout_fuchsia and host_os == "mac"',
'action': [
'cipd',
'install',
'fuchsia/qemu/mac-amd64',
'latest',
'-root', 'crashpad/third_party/fuchsia/qemu/mac-amd64',
'-log-level', 'info',
],
},
{
# Same rationale for using "install" rather than "ensure" as for clang
# packages. https://crbug.com/789364.
'name': 'fuchsia_qemu_linux',
'pattern': '.',
'condition': 'checkout_fuchsia and host_os == "linux"',
'action': [
'cipd',
'install',
'fuchsia/qemu/linux-amd64',
'latest',
'-root', 'crashpad/third_party/fuchsia/qemu/linux-amd64',
'-log-level', 'info',
],
},
{
# The SDK is keyed to the host system because it contains build tools.
# Currently, linux-amd64 is the only SDK published (see
# https://chrome-infra-packages.appspot.com/#/?path=fuchsia/sdk). As long as
# this is the case, use that SDK package even on other build hosts. The
# sysroot (containing headers and libraries) and other components are
# related to the target and should be functional with an appropriate
# toolchain that runs on the build host (fuchsia_clang, above).
'name': 'fuchsia_sdk',
'pattern': '.',
'condition': 'checkout_fuchsia',
'action': [
'cipd',
'install',
'fuchsia/sdk/linux-amd64',
'latest',
'-root', 'crashpad/third_party/fuchsia/sdk/linux-amd64',
'-log-level', 'info',
],
},
{
'name': 'toolchain_win',
'pattern': '.',
# This package is only updated when the solution in .gclient includes an
# entry like:
# "custom_vars": { "pull_win_toolchain": True }
# This is because the contained bits are not redistributable.
'condition': 'checkout_win and pull_win_toolchain',
'action': [
'cipd',
'install',
'chrome_internal/third_party/sdk/windows',
'uploaded:2018-06-13',
'-root', 'crashpad/third_party/win/toolchain',
'-log-level', 'info',
],
},
{ {
'name': 'gyp', 'name': 'gyp',
'pattern': '\.gypi?$', 'pattern': '\.gypi?$',

View File

@ -69,7 +69,8 @@ CrashReportDatabase::UploadReport::UploadReport()
reader_(std::make_unique<FileReader>()), reader_(std::make_unique<FileReader>()),
database_(nullptr), database_(nullptr),
attachment_readers_(), attachment_readers_(),
attachment_map_() {} attachment_map_(),
report_metrics_(false) {}
CrashReportDatabase::UploadReport::~UploadReport() { CrashReportDatabase::UploadReport::~UploadReport() {
if (database_) { if (database_) {

View File

@ -178,6 +178,7 @@ class CrashReportDatabase {
CrashReportDatabase* database_; CrashReportDatabase* database_;
std::vector<std::unique_ptr<FileReader>> attachment_readers_; std::vector<std::unique_ptr<FileReader>> attachment_readers_;
std::map<std::string, FileReader*> attachment_map_; std::map<std::string, FileReader*> attachment_map_;
bool report_metrics_;
DISALLOW_COPY_AND_ASSIGN(UploadReport); DISALLOW_COPY_AND_ASSIGN(UploadReport);
}; };
@ -326,11 +327,16 @@ class CrashReportDatabase {
//! \param[in] uuid The unique identifier for the crash report record. //! \param[in] uuid The unique identifier for the crash report record.
//! \param[out] report A crash report record for the report to be uploaded. //! \param[out] report A crash report record for the report to be uploaded.
//! Only valid if this returns #kNoError. //! Only valid if this returns #kNoError.
//! \param[in] report_metrics If `false`, metrics will not be recorded for
//! this upload attempt when RecordUploadComplete() is called or \a report
//! is destroyed. Metadata for the upload attempt will still be recorded
//! in the database.
//! //!
//! \return The operation status code. //! \return The operation status code.
virtual OperationStatus GetReportForUploading( virtual OperationStatus GetReportForUploading(
const UUID& uuid, const UUID& uuid,
std::unique_ptr<const UploadReport>* report) = 0; std::unique_ptr<const UploadReport>* report,
bool report_metrics = true) = 0;
//! \brief Records a successful upload for a report and updates the last //! \brief Records a successful upload for a report and updates the last
//! upload attempt time as returned by //! upload attempt time as returned by

View File

@ -180,7 +180,8 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase {
OperationStatus GetCompletedReports(std::vector<Report>* reports) override; OperationStatus GetCompletedReports(std::vector<Report>* reports) override;
OperationStatus GetReportForUploading( OperationStatus GetReportForUploading(
const UUID& uuid, const UUID& uuid,
std::unique_ptr<const UploadReport>* report) override; std::unique_ptr<const UploadReport>* report,
bool report_metrics) override;
OperationStatus SkipReportUpload(const UUID& uuid, OperationStatus SkipReportUpload(const UUID& uuid,
Metrics::CrashSkippedReason reason) override; Metrics::CrashSkippedReason reason) override;
OperationStatus DeleteReport(const UUID& uuid) override; OperationStatus DeleteReport(const UUID& uuid) override;
@ -455,7 +456,8 @@ OperationStatus CrashReportDatabaseGeneric::GetCompletedReports(
OperationStatus CrashReportDatabaseGeneric::GetReportForUploading( OperationStatus CrashReportDatabaseGeneric::GetReportForUploading(
const UUID& uuid, const UUID& uuid,
std::unique_ptr<const UploadReport>* report) { std::unique_ptr<const UploadReport>* report,
bool report_metrics) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
auto upload_report = std::make_unique<LockfileUploadReport>(); auto upload_report = std::make_unique<LockfileUploadReport>();
@ -470,6 +472,7 @@ OperationStatus CrashReportDatabaseGeneric::GetReportForUploading(
if (!upload_report->Initialize(path, this)) { if (!upload_report->Initialize(path, this)) {
return kFileSystemError; return kFileSystemError;
} }
upload_report->report_metrics_ = report_metrics;
report->reset(upload_report.release()); report->reset(upload_report.release());
return kNoError; return kNoError;
@ -609,7 +612,9 @@ OperationStatus CrashReportDatabaseGeneric::RecordUploadAttempt(
const std::string& id) { const std::string& id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
Metrics::CrashUploadAttempted(successful); if (report->report_metrics_) {
Metrics::CrashUploadAttempted(successful);
}
time_t now = time(nullptr); time_t now = time(nullptr);
report->id = id; report->id = id;

View File

@ -141,7 +141,8 @@ class CrashReportDatabaseMac : public CrashReportDatabase {
OperationStatus GetCompletedReports(std::vector<Report>* reports) override; OperationStatus GetCompletedReports(std::vector<Report>* reports) override;
OperationStatus GetReportForUploading( OperationStatus GetReportForUploading(
const UUID& uuid, const UUID& uuid,
std::unique_ptr<const UploadReport>* report) override; std::unique_ptr<const UploadReport>* report,
bool report_metrics) override;
OperationStatus SkipReportUpload(const UUID& uuid, OperationStatus SkipReportUpload(const UUID& uuid,
Metrics::CrashSkippedReason reason) override; Metrics::CrashSkippedReason reason) override;
OperationStatus DeleteReport(const UUID& uuid) override; OperationStatus DeleteReport(const UUID& uuid) override;
@ -422,7 +423,8 @@ CrashReportDatabaseMac::GetCompletedReports(
CrashReportDatabase::OperationStatus CrashReportDatabase::OperationStatus
CrashReportDatabaseMac::GetReportForUploading( CrashReportDatabaseMac::GetReportForUploading(
const UUID& uuid, const UUID& uuid,
std::unique_ptr<const UploadReport>* report) { std::unique_ptr<const UploadReport>* report,
bool report_metrics) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
auto upload_report = std::make_unique<UploadReportMac>(); auto upload_report = std::make_unique<UploadReportMac>();
@ -444,6 +446,7 @@ CrashReportDatabaseMac::GetReportForUploading(
upload_report->database_ = this; upload_report->database_ = this;
upload_report->lock_fd.reset(lock.release()); upload_report->lock_fd.reset(lock.release());
upload_report->report_metrics_ = report_metrics;
report->reset(upload_report.release()); report->reset(upload_report.release());
return kNoError; return kNoError;
} }
@ -454,7 +457,9 @@ CrashReportDatabaseMac::RecordUploadAttempt(UploadReport* report,
const std::string& id) { const std::string& id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
Metrics::CrashUploadAttempted(successful); if (report->report_metrics_) {
Metrics::CrashUploadAttempted(successful);
}
DCHECK(report); DCHECK(report);
DCHECK(successful || id.empty()); DCHECK(successful || id.empty());

View File

@ -594,7 +594,8 @@ class CrashReportDatabaseWin : public CrashReportDatabase {
OperationStatus GetCompletedReports(std::vector<Report>* reports) override; OperationStatus GetCompletedReports(std::vector<Report>* reports) override;
OperationStatus GetReportForUploading( OperationStatus GetReportForUploading(
const UUID& uuid, const UUID& uuid,
std::unique_ptr<const UploadReport>* report) override; std::unique_ptr<const UploadReport>* report,
bool report_metrics) override;
OperationStatus SkipReportUpload(const UUID& uuid, OperationStatus SkipReportUpload(const UUID& uuid,
Metrics::CrashSkippedReason reason) override; Metrics::CrashSkippedReason reason) override;
OperationStatus DeleteReport(const UUID& uuid) override; OperationStatus DeleteReport(const UUID& uuid) override;
@ -731,7 +732,8 @@ OperationStatus CrashReportDatabaseWin::GetCompletedReports(
OperationStatus CrashReportDatabaseWin::GetReportForUploading( OperationStatus CrashReportDatabaseWin::GetReportForUploading(
const UUID& uuid, const UUID& uuid,
std::unique_ptr<const UploadReport>* report) { std::unique_ptr<const UploadReport>* report,
bool report_metrics) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::unique_ptr<Metadata> metadata(AcquireMetadata()); std::unique_ptr<Metadata> metadata(AcquireMetadata());
@ -749,6 +751,7 @@ OperationStatus CrashReportDatabaseWin::GetReportForUploading(
if (!upload_report->Initialize(upload_report->file_path, this)) { if (!upload_report->Initialize(upload_report->file_path, this)) {
return kFileSystemError; return kFileSystemError;
} }
upload_report->report_metrics_ = report_metrics;
report->reset(upload_report.release()); report->reset(upload_report.release());
} }
@ -761,7 +764,9 @@ OperationStatus CrashReportDatabaseWin::RecordUploadAttempt(
const std::string& id) { const std::string& id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
Metrics::CrashUploadAttempted(successful); if (report->report_metrics_) {
Metrics::CrashUploadAttempted(successful);
}
std::unique_ptr<Metadata> metadata(AcquireMetadata()); std::unique_ptr<Metadata> metadata(AcquireMetadata());
if (!metadata) if (!metadata)

View File

@ -41,9 +41,10 @@ class MockDatabase : public CrashReportDatabase {
MOCK_METHOD2(LookUpCrashReport, OperationStatus(const UUID&, Report*)); MOCK_METHOD2(LookUpCrashReport, OperationStatus(const UUID&, Report*));
MOCK_METHOD1(GetPendingReports, OperationStatus(std::vector<Report>*)); MOCK_METHOD1(GetPendingReports, OperationStatus(std::vector<Report>*));
MOCK_METHOD1(GetCompletedReports, OperationStatus(std::vector<Report>*)); MOCK_METHOD1(GetCompletedReports, OperationStatus(std::vector<Report>*));
MOCK_METHOD2(GetReportForUploading, MOCK_METHOD3(GetReportForUploading,
OperationStatus(const UUID&, OperationStatus(const UUID&,
std::unique_ptr<const UploadReport>*)); std::unique_ptr<const UploadReport>*,
bool report_metrics));
MOCK_METHOD3(RecordUploadAttempt, MOCK_METHOD3(RecordUploadAttempt,
OperationStatus(UploadReport*, bool, const std::string&)); OperationStatus(UploadReport*, bool, const std::string&));
MOCK_METHOD2(SkipReportUpload, MOCK_METHOD2(SkipReportUpload,

View File

@ -115,8 +115,7 @@ TEST(SimpleStringDictionary, CopyAndAssign) {
// Add a bunch of values to the dictionary, remove some entries in the middle, // Add a bunch of values to the dictionary, remove some entries in the middle,
// and then add more. // and then add more.
TEST(SimpleStringDictionary, Iterator) { TEST(SimpleStringDictionary, Iterator) {
SimpleStringDictionary* dict = new SimpleStringDictionary; SimpleStringDictionary dict;
ASSERT_TRUE(dict);
char key[SimpleStringDictionary::key_size]; char key[SimpleStringDictionary::key_size];
char value[SimpleStringDictionary::value_size]; char value[SimpleStringDictionary::value_size];
@ -135,32 +134,32 @@ TEST(SimpleStringDictionary, Iterator) {
for (int i = 0; i < kPartitionIndex; ++i) { for (int i = 0; i < kPartitionIndex; ++i) {
sprintf(key, "key%d", i); sprintf(key, "key%d", i);
sprintf(value, "value%d", i); sprintf(value, "value%d", i);
dict->SetKeyValue(key, value); dict.SetKeyValue(key, value);
} }
expected_dictionary_size = kPartitionIndex; expected_dictionary_size = kPartitionIndex;
// set a couple of the keys twice (with the same value) - should be nop // set a couple of the keys twice (with the same value) - should be nop
dict->SetKeyValue("key2", "value2"); dict.SetKeyValue("key2", "value2");
dict->SetKeyValue("key4", "value4"); dict.SetKeyValue("key4", "value4");
dict->SetKeyValue("key15", "value15"); dict.SetKeyValue("key15", "value15");
// Remove some random elements in the middle // Remove some random elements in the middle
dict->RemoveKey("key7"); dict.RemoveKey("key7");
dict->RemoveKey("key18"); dict.RemoveKey("key18");
dict->RemoveKey("key23"); dict.RemoveKey("key23");
dict->RemoveKey("key31"); dict.RemoveKey("key31");
expected_dictionary_size -= 4; // we just removed four key/value pairs expected_dictionary_size -= 4; // we just removed four key/value pairs
// Set some more key/value pairs like key59/value59, key60/value60, ... // Set some more key/value pairs like key59/value59, key60/value60, ...
for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) {
sprintf(key, "key%d", i); sprintf(key, "key%d", i);
sprintf(value, "value%d", i); sprintf(value, "value%d", i);
dict->SetKeyValue(key, value); dict.SetKeyValue(key, value);
} }
expected_dictionary_size += kDictionaryCapacity - kPartitionIndex; expected_dictionary_size += kDictionaryCapacity - kPartitionIndex;
// Now create an iterator on the dictionary // Now create an iterator on the dictionary
SimpleStringDictionary::Iterator iter(*dict); SimpleStringDictionary::Iterator iter(dict);
// We then verify that it iterates through exactly the number of key/value // We then verify that it iterates through exactly the number of key/value
// pairs we expect, and that they match one-for-one with what we would expect. // pairs we expect, and that they match one-for-one with what we would expect.

View File

@ -26,7 +26,7 @@
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static_cast<__ptrace_request>(25); static_cast<__ptrace_request>(25);
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
#elif defined(__arm__) || defined(__arm64__) #elif defined(__arm__) || defined(__aarch64__)
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static_cast<__ptrace_request>(22); static_cast<__ptrace_request>(22);
#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA #define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA
@ -42,7 +42,7 @@ static constexpr __ptrace_request PTRACE_GET_THREAD_AREA_3264 =
// https://sourceware.org/bugzilla/show_bug.cgi?id=22433 // https://sourceware.org/bugzilla/show_bug.cgi?id=22433
#if !defined(PTRACE_GETVFPREGS) && !defined(PT_GETVFPREGS) && \ #if !defined(PTRACE_GETVFPREGS) && !defined(PT_GETVFPREGS) && \
defined(__GLIBC__) && (defined(__arm__) || defined(__arm64__)) defined(__GLIBC__) && (defined(__arm__) || defined(__aarch64__))
static constexpr __ptrace_request PTRACE_GETVFPREGS = static constexpr __ptrace_request PTRACE_GETVFPREGS =
static_cast<__ptrace_request>(27); static_cast<__ptrace_request>(27);
#define PTRACE_GETVFPREGS PTRACE_GETVFPREGS #define PTRACE_GETVFPREGS PTRACE_GETVFPREGS

View File

@ -20,7 +20,7 @@
#include <features.h> #include <features.h>
// glibc for 64-bit ARM uses different names for these structs prior to 2.20. // glibc for 64-bit ARM uses different names for these structs prior to 2.20.
#if defined(__arm64__) && defined(__GLIBC__) #if defined(__aarch64__) && defined(__GLIBC__)
#if !__GLIBC_PREREQ(2, 20) #if !__GLIBC_PREREQ(2, 20)
using user_regs_struct = user_pt_regs; using user_regs_struct = user_pt_regs;
using user_fpsimd_struct = user_fpsimd_state; using user_fpsimd_struct = user_fpsimd_state;

View File

@ -165,7 +165,7 @@ crashpad_executable("crashpad_handler") {
# installer will ignore files not named like a shared object, so give the # installer will ignore files not named like a shared object, so give the
# handler executable an acceptable name. # handler executable an acceptable name.
if (crashpad_is_android) { if (crashpad_is_android) {
copy("crashpad_handler_module") { copy("crashpad_handler_named_as_so") {
deps = [ deps = [
":crashpad_handler", ":crashpad_handler",
] ]

View File

@ -227,6 +227,10 @@ void CrashReportUploadThread::ProcessPendingReport(
database_->RecordUploadComplete(std::move(upload_report), response_body); database_->RecordUploadComplete(std::move(upload_report), response_body);
break; break;
case UploadResult::kPermanentFailure: case UploadResult::kPermanentFailure:
upload_report.reset();
database_->SkipReportUpload(
report.uuid, Metrics::CrashSkippedReason::kPrepareForUploadFailed);
break;
case UploadResult::kRetry: case UploadResult::kRetry:
upload_report.reset(); upload_report.reset();

View File

@ -33,10 +33,19 @@ verifiers {
try_job { try_job {
buckets { buckets {
name: "master.client.crashpad" name: "master.client.crashpad"
builders { name: "crashpad_try_mac_dbg" }
builders { name: "crashpad_try_mac_rel" }
builders { name: "crashpad_try_win_dbg" } builders { name: "crashpad_try_win_dbg" }
builders { name: "crashpad_try_win_rel" } builders { name: "crashpad_try_win_rel" }
# https://crbug.com/743139 - disabled until we can move these to swarming,
# at which point we can just remove them.
#builders { name: "crashpad_try_win_x86_dbg" }
#builders { name: "crashpad_try_win_x86_rel" }
}
buckets {
name: "luci.crashpad.try"
builders { name: "crashpad_try_mac_dbg" }
builders { name: "crashpad_try_mac_rel" }
builders { name: "crashpad_try_win_dbg" experiment_percentage: 100 }
builders { name: "crashpad_try_win_rel" experiment_percentage: 100 }
builders { name: "crashpad_try_linux_dbg" } builders { name: "crashpad_try_linux_dbg" }
builders { name: "crashpad_try_linux_rel" } builders { name: "crashpad_try_linux_rel" }
builders { name: "crashpad_try_fuchsia_arm64_dbg" } builders { name: "crashpad_try_fuchsia_arm64_dbg" }

View File

@ -19,7 +19,7 @@
#define PACKAGE_COPYRIGHT \ #define PACKAGE_COPYRIGHT \
"Copyright " PACKAGE_COPYRIGHT_YEAR " " PACKAGE_COPYRIGHT_OWNER "Copyright " PACKAGE_COPYRIGHT_YEAR " " PACKAGE_COPYRIGHT_OWNER
#define PACKAGE_COPYRIGHT_OWNER "The Crashpad Authors" #define PACKAGE_COPYRIGHT_OWNER "The Crashpad Authors"
#define PACKAGE_COPYRIGHT_YEAR "2017" #define PACKAGE_COPYRIGHT_YEAR "2018"
#define PACKAGE_NAME "Crashpad" #define PACKAGE_NAME "Crashpad"
#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION #define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION
#define PACKAGE_TARNAME "crashpad" #define PACKAGE_TARNAME "crashpad"

View File

@ -31,12 +31,14 @@ class ElfImageReader::ProgramHeaderTable {
public: public:
virtual ~ProgramHeaderTable() {} virtual ~ProgramHeaderTable() {}
virtual bool VerifyLoadSegments() const = 0; virtual bool VerifyLoadSegments(bool verbose) const = 0;
virtual size_t Size() const = 0; virtual size_t Size() const = 0;
virtual bool GetDynamicSegment(VMAddress* address, VMSize* size) const = 0; virtual bool GetDynamicSegment(VMAddress* address, VMSize* size) const = 0;
virtual bool GetPreferredElfHeaderAddress(VMAddress* address) const = 0; virtual bool GetPreferredElfHeaderAddress(VMAddress* address,
bool verbose) const = 0;
virtual bool GetPreferredLoadedMemoryRange(VMAddress* address, virtual bool GetPreferredLoadedMemoryRange(VMAddress* address,
VMSize* size) const = 0; VMSize* size,
bool verbose) const = 0;
// Locate the next PT_NOTE segment starting at segment index start_index. If a // Locate the next PT_NOTE segment starting at segment index start_index. If a
// PT_NOTE segment is found, start_index is set to the next index after the // PT_NOTE segment is found, start_index is set to the next index after the
@ -58,14 +60,15 @@ class ElfImageReader::ProgramHeaderTableSpecific
bool Initialize(const ProcessMemoryRange& memory, bool Initialize(const ProcessMemoryRange& memory,
VMAddress address, VMAddress address,
VMSize num_segments) { VMSize num_segments,
bool verbose) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
table_.resize(num_segments); table_.resize(num_segments);
if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) { if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) {
return false; return false;
} }
if (!VerifyLoadSegments()) { if (!VerifyLoadSegments(verbose)) {
return false; return false;
} }
@ -73,7 +76,7 @@ class ElfImageReader::ProgramHeaderTableSpecific
return true; return true;
} }
bool VerifyLoadSegments() const override { bool VerifyLoadSegments(bool verbose) const override {
constexpr bool is_64_bit = std::is_same<PhdrType, Elf64_Phdr>::value; constexpr bool is_64_bit = std::is_same<PhdrType, Elf64_Phdr>::value;
VMAddress last_vaddr; VMAddress last_vaddr;
bool load_found = false; bool load_found = false;
@ -83,12 +86,12 @@ class ElfImageReader::ProgramHeaderTableSpecific
is_64_bit, header.p_vaddr, header.p_memsz); is_64_bit, header.p_vaddr, header.p_memsz);
if (!load_range.IsValid()) { if (!load_range.IsValid()) {
LOG(ERROR) << "bad load range"; LOG_IF(ERROR, verbose) << "bad load range";
return false; return false;
} }
if (load_found && header.p_vaddr <= last_vaddr) { if (load_found && header.p_vaddr <= last_vaddr) {
LOG(ERROR) << "out of order load segments"; LOG_IF(ERROR, verbose) << "out of order load segments";
return false; return false;
} }
load_found = true; load_found = true;
@ -100,7 +103,8 @@ class ElfImageReader::ProgramHeaderTableSpecific
size_t Size() const override { return sizeof(PhdrType) * table_.size(); } size_t Size() const override { return sizeof(PhdrType) * table_.size(); }
bool GetPreferredElfHeaderAddress(VMAddress* address) const override { bool GetPreferredElfHeaderAddress(VMAddress* address,
bool verbose) const override {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
for (const auto& header : table_) { for (const auto& header : table_) {
if (header.p_type == PT_LOAD && header.p_offset == 0) { if (header.p_type == PT_LOAD && header.p_offset == 0) {
@ -108,12 +112,13 @@ class ElfImageReader::ProgramHeaderTableSpecific
return true; return true;
} }
} }
LOG(ERROR) << "no preferred header address"; LOG_IF(ERROR, verbose) << "no preferred header address";
return false; return false;
} }
bool GetPreferredLoadedMemoryRange(VMAddress* base, bool GetPreferredLoadedMemoryRange(VMAddress* base,
VMSize* size) const override { VMSize* size,
bool verbose) const override {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
VMAddress preferred_base = 0; VMAddress preferred_base = 0;
@ -133,7 +138,7 @@ class ElfImageReader::ProgramHeaderTableSpecific
*size = preferred_end - preferred_base; *size = preferred_end - preferred_base;
return true; return true;
} }
LOG(ERROR) << "no load segments"; LOG_IF(ERROR, verbose) << "no load segments";
return false; return false;
} }
@ -340,7 +345,8 @@ ElfImageReader::ElfImageReader()
ElfImageReader::~ElfImageReader() {} ElfImageReader::~ElfImageReader() {}
bool ElfImageReader::Initialize(const ProcessMemoryRange& memory, bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
VMAddress address) { VMAddress address,
bool verbose) {
INITIALIZATION_STATE_SET_INITIALIZING(initialized_); INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
ehdr_address_ = address; ehdr_address_ = address;
if (!memory_.Initialize(memory)) { if (!memory_.Initialize(memory)) {
@ -354,13 +360,13 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 ||
e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) { e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
LOG(ERROR) << "Incorrect ELF magic number"; LOG_IF(ERROR, verbose) << "Incorrect ELF magic number";
return false; return false;
} }
if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) && if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) &&
!(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) { !(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) {
LOG(ERROR) << "unexpected bitness"; LOG_IF(ERROR, verbose) << "unexpected bitness";
return false; return false;
} }
@ -370,12 +376,12 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
constexpr uint8_t expected_encoding = ELFDATA2MSB; constexpr uint8_t expected_encoding = ELFDATA2MSB;
#endif #endif
if (e_ident[EI_DATA] != expected_encoding) { if (e_ident[EI_DATA] != expected_encoding) {
LOG(ERROR) << "unexpected encoding"; LOG_IF(ERROR, verbose) << "unexpected encoding";
return false; return false;
} }
if (e_ident[EI_VERSION] != EV_CURRENT) { if (e_ident[EI_VERSION] != EV_CURRENT) {
LOG(ERROR) << "unexpected version"; LOG_IF(ERROR, verbose) << "unexpected version";
return false; return false;
} }
@ -388,15 +394,15 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
#define VERIFY_HEADER(header) \ #define VERIFY_HEADER(header) \
do { \ do { \
if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \ if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \
LOG(ERROR) << "unexpected image type"; \ LOG_IF(ERROR, verbose) << "unexpected image type"; \
return false; \ return false; \
} \ } \
if (header.e_version != EV_CURRENT) { \ if (header.e_version != EV_CURRENT) { \
LOG(ERROR) << "unexpected version"; \ LOG_IF(ERROR, verbose) << "unexpected version"; \
return false; \ return false; \
} \ } \
if (header.e_ehsize != sizeof(header)) { \ if (header.e_ehsize != sizeof(header)) { \
LOG(ERROR) << "unexpected header size"; \ LOG_IF(ERROR, verbose) << "unexpected header size"; \
return false; \ return false; \
} \ } \
} while (false); } while (false);
@ -407,21 +413,21 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
VERIFY_HEADER(header_32_); VERIFY_HEADER(header_32_);
} }
if (!InitializeProgramHeaders()) { if (!InitializeProgramHeaders(verbose)) {
return false; return false;
} }
VMAddress preferred_ehdr_address; VMAddress preferred_ehdr_address;
if (!program_headers_.get()->GetPreferredElfHeaderAddress( if (!program_headers_.get()->GetPreferredElfHeaderAddress(
&preferred_ehdr_address)) { &preferred_ehdr_address, verbose)) {
return false; return false;
} }
load_bias_ = ehdr_address_ - preferred_ehdr_address; load_bias_ = ehdr_address_ - preferred_ehdr_address;
VMAddress base_address; VMAddress base_address;
VMSize loaded_size; VMSize loaded_size;
if (!program_headers_.get()->GetPreferredLoadedMemoryRange(&base_address, if (!program_headers_.get()->GetPreferredLoadedMemoryRange(
&loaded_size)) { &base_address, &loaded_size, verbose)) {
return false; return false;
} }
base_address += load_bias_; base_address += load_bias_;
@ -443,12 +449,12 @@ bool ElfImageReader::Initialize(const ProcessMemoryRange& memory,
CheckedVMAddressRange range(memory_.Is64Bit(), base_address, loaded_size); CheckedVMAddressRange range(memory_.Is64Bit(), base_address, loaded_size);
if (!range.ContainsRange( if (!range.ContainsRange(
CheckedVMAddressRange(memory_.Is64Bit(), ehdr_address_, ehdr_size))) { CheckedVMAddressRange(memory_.Is64Bit(), ehdr_address_, ehdr_size))) {
LOG(ERROR) << "ehdr out of range"; LOG_IF(ERROR, verbose) << "ehdr out of range";
return false; return false;
} }
if (!range.ContainsRange(CheckedVMAddressRange( if (!range.ContainsRange(CheckedVMAddressRange(
memory.Is64Bit(), phdr_address, program_headers_->Size()))) { memory.Is64Bit(), phdr_address, program_headers_->Size()))) {
LOG(ERROR) << "phdrs out of range"; LOG_IF(ERROR, verbose) << "phdrs out of range";
return false; return false;
} }
@ -545,19 +551,40 @@ bool ElfImageReader::GetDebugAddress(VMAddress* debug) {
return GetAddressFromDynamicArray(DT_DEBUG, true, debug); return GetAddressFromDynamicArray(DT_DEBUG, true, debug);
} }
bool ElfImageReader::InitializeProgramHeaders() { bool ElfImageReader::GetDynamicArrayAddress(VMAddress* address) {
#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header) \ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
do { \ VMAddress dyn_segment_address;
if (header.e_phentsize != sizeof(PhdrType)) { \ VMSize dyn_segment_size;
LOG(ERROR) << "unexpected phdr size"; \ if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address,
return false; \ &dyn_segment_size)) {
} \ LOG(ERROR) << "no dynamic segment";
auto phdrs = new ProgramHeaderTableSpecific<PhdrType>(); \ return false;
program_headers_.reset(phdrs); \ }
if (!phdrs->Initialize( \ *address = dyn_segment_address + GetLoadBias();
memory_, ehdr_address_ + header.e_phoff, header.e_phnum)) { \ return true;
return false; \ }
} \
VMAddress ElfImageReader::GetProgramHeaderTableAddress() {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return ehdr_address_ +
(memory_.Is64Bit() ? header_64_.e_phoff : header_32_.e_phoff);
}
bool ElfImageReader::InitializeProgramHeaders(bool verbose) {
#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header) \
do { \
if (header.e_phentsize != sizeof(PhdrType)) { \
LOG_IF(ERROR, verbose) << "unexpected phdr size"; \
return false; \
} \
auto phdrs = new ProgramHeaderTableSpecific<PhdrType>(); \
program_headers_.reset(phdrs); \
if (!phdrs->Initialize(memory_, \
ehdr_address_ + header.e_phoff, \
header.e_phnum, \
verbose)) { \
return false; \
} \
} while (false); } while (false);
if (memory_.Is64Bit()) { if (memory_.Is64Bit()) {

View File

@ -118,7 +118,13 @@ class ElfImageReader {
//! \param[in] memory A memory reader for the remote process. //! \param[in] memory A memory reader for the remote process.
//! \param[in] address The address in the remote process' address space where //! \param[in] address The address in the remote process' address space where
//! the ELF image is loaded. //! the ELF image is loaded.
bool Initialize(const ProcessMemoryRange& memory, VMAddress address); //! \param[in] verbose `true` if this method should log error messages during
//! initialization. Setting this value to `false` will reduce the error
//! messages relating to verifying the ELF image, but may not suppress
//! logging entirely.
bool Initialize(const ProcessMemoryRange& memory,
VMAddress address,
bool verbose = true);
//! \brief Returns the base address of the image's memory range. //! \brief Returns the base address of the image's memory range.
//! //!
@ -172,6 +178,16 @@ class ElfImageReader {
//! \return `true` if the debug address was found. //! \return `true` if the debug address was found.
bool GetDebugAddress(VMAddress* debug); bool GetDebugAddress(VMAddress* debug);
//! \brief Determine the address of `PT_DYNAMIC` segment.
//!
//! \param[out] address The address of the array, valid if this method returns
//! `true`.
//! \return `true` on success. Otherwise `false` with a message logged.
bool GetDynamicArrayAddress(VMAddress* address);
//! \brief Return the address of the program header table.
VMAddress GetProgramHeaderTableAddress();
//! \brief Return a NoteReader for this image, which scans all PT_NOTE //! \brief Return a NoteReader for this image, which scans all PT_NOTE
//! segments in the image. //! segments in the image.
//! //!
@ -238,7 +254,7 @@ class ElfImageReader {
template <typename PhdrType> template <typename PhdrType>
class ProgramHeaderTableSpecific; class ProgramHeaderTableSpecific;
bool InitializeProgramHeaders(); bool InitializeProgramHeaders(bool verbose);
bool InitializeDynamicArray(); bool InitializeDynamicArray();
bool InitializeDynamicSymbolTable(); bool InitializeDynamicSymbolTable();
bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address); bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address);

View File

@ -103,10 +103,10 @@ void LocateExecutable(PtraceConnection* connection,
ASSERT_TRUE(memory_map.Initialize(connection)); ASSERT_TRUE(memory_map.Initialize(connection));
const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping); ASSERT_TRUE(phdr_mapping);
const MemoryMap::Mapping* exe_mapping = std::vector<const MemoryMap::Mapping*> possible_mappings =
memory_map.FindFileMmapStart(*phdr_mapping); memory_map.FindFilePossibleMmapStarts(*phdr_mapping);
ASSERT_TRUE(exe_mapping); ASSERT_EQ(possible_mappings.size(), 1u);
*elf_address = exe_mapping->range.Base(); *elf_address = possible_mappings[0]->range.Base();
} }
#endif // OS_FUCHSIA #endif // OS_FUCHSIA

View File

@ -74,9 +74,10 @@ void TestAgainstTarget(PtraceConnection* connection) {
const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs); const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping); ASSERT_TRUE(phdr_mapping);
const MemoryMap::Mapping* exe_mapping = std::vector<const MemoryMap::Mapping*> exe_mappings =
mappings.FindFileMmapStart(*phdr_mapping); mappings.FindFilePossibleMmapStarts(*phdr_mapping);
LinuxVMAddress elf_address = exe_mapping->range.Base(); ASSERT_EQ(exe_mappings.size(), 1u);
LinuxVMAddress elf_address = exe_mappings[0]->range.Base();
ProcessMemoryLinux memory; ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(connection->GetProcessID())); ASSERT_TRUE(memory.Initialize(connection->GetProcessID()));
@ -142,9 +143,24 @@ void TestAgainstTarget(PtraceConnection* connection) {
mappings.FindMapping(module.dynamic_array); mappings.FindMapping(module.dynamic_array);
ASSERT_TRUE(dyn_mapping); ASSERT_TRUE(dyn_mapping);
const MemoryMap::Mapping* module_mapping = std::vector<const MemoryMap::Mapping*> possible_mappings =
mappings.FindFileMmapStart(*dyn_mapping); mappings.FindFilePossibleMmapStarts(*dyn_mapping);
ASSERT_TRUE(module_mapping); ASSERT_GE(possible_mappings.size(), 1u);
std::unique_ptr<ElfImageReader> module_reader;
const MemoryMap::Mapping* module_mapping = nullptr;
for (const auto mapping : possible_mappings) {
auto parsed_module = std::make_unique<ElfImageReader>();
VMAddress dynamic_address;
if (parsed_module->Initialize(range, mapping->range.Base()) &&
parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
dynamic_address == module.dynamic_array) {
module_reader = std::move(parsed_module);
module_mapping = mapping;
break;
}
}
ASSERT_TRUE(module_reader.get());
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
EXPECT_FALSE(module.name.empty()); EXPECT_FALSE(module.name.empty());
@ -168,20 +184,17 @@ void TestAgainstTarget(PtraceConnection* connection) {
module.name); module.name);
#endif // OS_ANDROID #endif // OS_ANDROID
ElfImageReader module_reader;
ASSERT_TRUE(module_reader.Initialize(range, module_mapping->range.Base()));
// Android's loader stops setting its own load bias after Android 4.4.4 // Android's loader stops setting its own load bias after Android 4.4.4
// (API 20) until Android 6.0 (API 23). // (API 20) until Android 6.0 (API 23).
if (is_android_loader && android_runtime_api > 20 && if (is_android_loader && android_runtime_api > 20 &&
android_runtime_api < 23) { android_runtime_api < 23) {
EXPECT_EQ(module.load_bias, 0); EXPECT_EQ(module.load_bias, 0);
} else { } else {
EXPECT_EQ(module.load_bias, module_reader.GetLoadBias()); EXPECT_EQ(module.load_bias, module_reader->GetLoadBias());
} }
CheckedLinuxAddressRange module_range( CheckedLinuxAddressRange module_range(
connection->Is64Bit(), module_reader.Address(), module_reader.Size()); connection->Is64Bit(), module_reader->Address(), module_reader->Size());
EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array)); EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array));
} }
} }

View File

@ -347,20 +347,45 @@ void ProcessReaderLinux::InitializeModules() {
return; return;
} }
const MemoryMap::Mapping* exe_mapping;
if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) ||
!(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) {
return;
}
ProcessMemoryRange range; ProcessMemoryRange range;
if (!range.Initialize(Memory(), is_64_bit_)) { if (!range.Initialize(Memory(), is_64_bit_)) {
return; return;
} }
auto exe_reader = std::make_unique<ElfImageReader>(); // The strategy used for identifying loaded modules depends on ELF files
if (!exe_reader->Initialize(range, exe_mapping->range.Base())) { // conventionally loading their header and program headers into memory.
return; // Locating the correct module could fail if the headers aren't mapped, are
// mapped at an unexpected location, or if there are other mappings
// constructed to look like the ELF module being searched for.
const MemoryMap::Mapping* exe_mapping = nullptr;
std::unique_ptr<ElfImageReader> exe_reader;
{
const MemoryMap::Mapping* phdr_mapping = memory_map_.FindMapping(phdrs);
if (!phdr_mapping) {
return;
}
std::vector<const MemoryMap::Mapping*> possible_mappings =
memory_map_.FindFilePossibleMmapStarts(*phdr_mapping);
for (auto riter = possible_mappings.rbegin();
riter != possible_mappings.rend();
++riter) {
auto mapping = *riter;
auto parsed_exe = std::make_unique<ElfImageReader>();
if (parsed_exe->Initialize(
range,
mapping->range.Base(),
/* verbose= */ possible_mappings.size() == 1) &&
parsed_exe->GetProgramHeaderTableAddress() == phdrs) {
exe_mapping = mapping;
exe_reader = std::move(parsed_exe);
break;
}
}
if (!exe_mapping) {
LOG(ERROR) << "no exe mappings " << possible_mappings.size();
return;
}
} }
LinuxVMAddress debug_address; LinuxVMAddress debug_address;
@ -386,19 +411,42 @@ void ProcessReaderLinux::InitializeModules() {
aux.GetValue(AT_BASE, &loader_base); aux.GetValue(AT_BASE, &loader_base);
for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) { for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) {
const MemoryMap::Mapping* mapping; const MemoryMap::Mapping* module_mapping = nullptr;
if (!(mapping = memory_map_.FindMapping(entry.dynamic_array)) || std::unique_ptr<ElfImageReader> elf_reader;
!(mapping = memory_map_.FindFileMmapStart(*mapping))) { {
continue; const MemoryMap::Mapping* dyn_mapping =
} memory_map_.FindMapping(entry.dynamic_array);
if (!dyn_mapping) {
continue;
}
auto elf_reader = std::make_unique<ElfImageReader>(); std::vector<const MemoryMap::Mapping*> possible_mappings =
if (!elf_reader->Initialize(range, mapping->range.Base())) { memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);
continue; for (auto riter = possible_mappings.rbegin();
riter != possible_mappings.rend();
++riter) {
auto mapping = *riter;
auto parsed_module = std::make_unique<ElfImageReader>();
VMAddress dynamic_address;
if (parsed_module->Initialize(
range,
mapping->range.Base(),
/* verbose= */ possible_mappings.size() == 1) &&
parsed_module->GetDynamicArrayAddress(&dynamic_address) &&
dynamic_address == entry.dynamic_array) {
module_mapping = mapping;
elf_reader = std::move(parsed_module);
break;
}
}
if (!module_mapping) {
LOG(ERROR) << "no module mappings " << possible_mappings.size();
continue;
}
} }
Module module = {}; Module module = {};
module.name = !entry.name.empty() ? entry.name : mapping->name; module.name = !entry.name.empty() ? entry.name : module_mapping->name;
module.elf_reader = elf_reader.get(); module.elf_reader = elf_reader.get();
module.type = loader_base && elf_reader->Address() == loader_base module.type = loader_base && elf_reader->Address() == loader_base
? ModuleSnapshot::kModuleTypeDynamicLoader ? ModuleSnapshot::kModuleTypeDynamicLoader

View File

@ -14,6 +14,8 @@
#include "snapshot/linux/process_reader_linux.h" #include "snapshot/linux/process_reader_linux.h"
#include <dlfcn.h>
#include <elf.h>
#include <errno.h> #include <errno.h>
#include <link.h> #include <link.h>
#include <pthread.h> #include <pthread.h>
@ -38,8 +40,13 @@
#include "test/linux/fake_ptrace_connection.h" #include "test/linux/fake_ptrace_connection.h"
#include "test/linux/get_tls.h" #include "test/linux/get_tls.h"
#include "test/multiprocess.h" #include "test/multiprocess.h"
#include "test/scoped_module_handle.h"
#include "test/test_paths.h"
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/file/file_writer.h"
#include "util/file/filesystem.h"
#include "util/linux/direct_ptrace_connection.h" #include "util/linux/direct_ptrace_connection.h"
#include "util/misc/address_sanitizer.h"
#include "util/misc/from_pointer_cast.h" #include "util/misc/from_pointer_cast.h"
#include "util/synchronization/semaphore.h" #include "util/synchronization/semaphore.h"
@ -264,12 +271,17 @@ void ExpectThreads(const ThreadMap& thread_map,
iterator->second.tls); iterator->second.tls);
ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address)); ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address));
EXPECT_LE(thread.stack_region_address, iterator->second.stack_address);
ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address + ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address +
thread.stack_region_size - 1)); thread.stack_region_size - 1));
#if !defined(ADDRESS_SANITIZER)
// AddressSanitizer causes stack variables to be stored separately from the
// call stack.
EXPECT_LE(thread.stack_region_address, iterator->second.stack_address);
EXPECT_GE(thread.stack_region_address + thread.stack_region_size, EXPECT_GE(thread.stack_region_address + thread.stack_region_size,
iterator->second.stack_address); iterator->second.stack_address);
#endif // !defined(ADDRESS_SANITIZER)
if (iterator->second.max_stack_size) { if (iterator->second.max_stack_size) {
EXPECT_LT(thread.stack_region_size, iterator->second.max_stack_size); EXPECT_LT(thread.stack_region_size, iterator->second.max_stack_size);
} }
@ -501,7 +513,220 @@ void ExpectModulesFromSelf(
#endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 #endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21
} }
bool WriteTestModule(const base::FilePath& module_path) {
#if defined(ARCH_CPU_64_BITS)
using Ehdr = Elf64_Ehdr;
using Phdr = Elf64_Phdr;
using Shdr = Elf64_Shdr;
using Dyn = Elf64_Dyn;
using Sym = Elf64_Sym;
unsigned char elf_class = ELFCLASS64;
#else
using Ehdr = Elf32_Ehdr;
using Phdr = Elf32_Phdr;
using Shdr = Elf32_Shdr;
using Dyn = Elf32_Dyn;
using Sym = Elf32_Sym;
unsigned char elf_class = ELFCLASS32;
#endif
struct {
Ehdr ehdr;
struct {
Phdr load1;
Phdr load2;
Phdr dynamic;
} phdr_table;
struct {
Dyn hash;
Dyn strtab;
Dyn symtab;
Dyn strsz;
Dyn syment;
Dyn null;
} dynamic_array;
struct {
Elf32_Word nbucket;
Elf32_Word nchain;
Elf32_Word bucket;
Elf32_Word chain;
} hash_table;
struct {
} string_table;
struct {
Sym und_symbol;
} symbol_table;
struct {
Shdr null;
Shdr dynamic;
Shdr string_table;
} shdr_table;
} module = {};
module.ehdr.e_ident[EI_MAG0] = ELFMAG0;
module.ehdr.e_ident[EI_MAG1] = ELFMAG1;
module.ehdr.e_ident[EI_MAG2] = ELFMAG2;
module.ehdr.e_ident[EI_MAG3] = ELFMAG3;
module.ehdr.e_ident[EI_CLASS] = elf_class;
#if defined(ARCH_CPU_LITTLE_ENDIAN)
module.ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
#else
module.ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
#endif // ARCH_CPU_LITTLE_ENDIAN
module.ehdr.e_ident[EI_VERSION] = EV_CURRENT;
module.ehdr.e_type = ET_DYN;
#if defined(ARCH_CPU_X86)
module.ehdr.e_machine = EM_386;
#elif defined(ARCH_CPU_X86_64)
module.ehdr.e_machine = EM_X86_64;
#elif defined(ARCH_CPU_ARMEL)
module.ehdr.e_machine = EM_ARM;
#elif defined(ARCH_CPU_ARM64)
module.ehdr.e_machine = EM_AARCH64;
#elif defined(ARCH_CPU_MIPSEL) || defined(ARCH_CPU_MIPS64EL)
module.ehdr.e_machine = EM_MIPS;
#endif
module.ehdr.e_version = EV_CURRENT;
module.ehdr.e_ehsize = sizeof(module.ehdr);
module.ehdr.e_phoff = offsetof(decltype(module), phdr_table);
module.ehdr.e_phnum = sizeof(module.phdr_table) / sizeof(Phdr);
module.ehdr.e_phentsize = sizeof(Phdr);
module.ehdr.e_shoff = offsetof(decltype(module), shdr_table);
module.ehdr.e_shentsize = sizeof(Shdr);
module.ehdr.e_shnum = sizeof(module.shdr_table) / sizeof(Shdr);
module.ehdr.e_shstrndx = SHN_UNDEF;
constexpr size_t load2_vaddr = 0x200000;
module.phdr_table.load1.p_type = PT_LOAD;
module.phdr_table.load1.p_offset = 0;
module.phdr_table.load1.p_vaddr = 0;
module.phdr_table.load1.p_filesz = sizeof(module);
module.phdr_table.load1.p_memsz = sizeof(module);
module.phdr_table.load1.p_flags = PF_R;
module.phdr_table.load1.p_align = load2_vaddr;
module.phdr_table.load2.p_type = PT_LOAD;
module.phdr_table.load2.p_offset = 0;
module.phdr_table.load2.p_vaddr = load2_vaddr;
module.phdr_table.load2.p_filesz = sizeof(module);
module.phdr_table.load2.p_memsz = sizeof(module);
module.phdr_table.load2.p_flags = PF_R | PF_W;
module.phdr_table.load2.p_align = load2_vaddr;
module.phdr_table.dynamic.p_type = PT_DYNAMIC;
module.phdr_table.dynamic.p_offset =
offsetof(decltype(module), dynamic_array);
module.phdr_table.dynamic.p_vaddr =
load2_vaddr + module.phdr_table.dynamic.p_offset;
module.phdr_table.dynamic.p_filesz = sizeof(module.dynamic_array);
module.phdr_table.dynamic.p_memsz = sizeof(module.dynamic_array);
module.phdr_table.dynamic.p_flags = PF_R | PF_W;
module.phdr_table.dynamic.p_align = 8;
module.dynamic_array.hash.d_tag = DT_HASH;
module.dynamic_array.hash.d_un.d_ptr = offsetof(decltype(module), hash_table);
module.dynamic_array.strtab.d_tag = DT_STRTAB;
module.dynamic_array.strtab.d_un.d_ptr =
offsetof(decltype(module), string_table);
module.dynamic_array.symtab.d_tag = DT_SYMTAB;
module.dynamic_array.symtab.d_un.d_ptr =
offsetof(decltype(module), symbol_table);
module.dynamic_array.strsz.d_tag = DT_STRSZ;
module.dynamic_array.strsz.d_un.d_val = sizeof(module.string_table);
module.dynamic_array.syment.d_tag = DT_SYMENT;
module.dynamic_array.syment.d_un.d_val = sizeof(Sym);
module.dynamic_array.null.d_tag = DT_NULL;
module.hash_table.nbucket = 1;
module.hash_table.nchain = 1;
module.hash_table.bucket = 0;
module.hash_table.chain = 0;
module.shdr_table.null.sh_type = SHT_NULL;
module.shdr_table.dynamic.sh_name = 0;
module.shdr_table.dynamic.sh_type = SHT_DYNAMIC;
module.shdr_table.dynamic.sh_flags = SHF_WRITE | SHF_ALLOC;
module.shdr_table.dynamic.sh_addr = module.phdr_table.dynamic.p_vaddr;
module.shdr_table.dynamic.sh_offset = module.phdr_table.dynamic.p_offset;
module.shdr_table.dynamic.sh_size = module.phdr_table.dynamic.p_filesz;
module.shdr_table.dynamic.sh_link =
offsetof(decltype(module.shdr_table), string_table) / sizeof(Shdr);
module.shdr_table.string_table.sh_name = 0;
module.shdr_table.string_table.sh_type = SHT_STRTAB;
module.shdr_table.string_table.sh_offset =
offsetof(decltype(module), string_table);
FileWriter writer;
if (!writer.Open(module_path,
FileWriteMode::kCreateOrFail,
FilePermissions::kWorldReadable)) {
ADD_FAILURE();
return false;
}
if (!writer.Write(&module, sizeof(module))) {
ADD_FAILURE();
return false;
}
return true;
}
ScopedModuleHandle LoadTestModule(const std::string& module_name) {
base::FilePath module_path(
TestPaths::Executable().DirName().Append(module_name));
if (!WriteTestModule(module_path)) {
return ScopedModuleHandle(nullptr);
}
EXPECT_TRUE(IsRegularFile(module_path));
ScopedModuleHandle handle(
dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
EXPECT_TRUE(handle.valid())
<< "dlopen: " << module_path.value() << " " << dlerror();
EXPECT_TRUE(LoggingRemoveFile(module_path));
return handle;
}
void ExpectTestModule(ProcessReaderLinux* reader,
const std::string& module_name) {
for (const auto& module : reader->Modules()) {
if (module.name.find(module_name) != std::string::npos) {
ASSERT_TRUE(module.elf_reader);
VMAddress dynamic_addr;
ASSERT_TRUE(module.elf_reader->GetDynamicArrayAddress(&dynamic_addr));
auto dynamic_mapping = reader->GetMemoryMap()->FindMapping(dynamic_addr);
auto mappings =
reader->GetMemoryMap()->FindFilePossibleMmapStarts(*dynamic_mapping);
EXPECT_EQ(mappings.size(), 2u);
return;
}
}
ADD_FAILURE() << "Test module not found";
}
TEST(ProcessReaderLinux, SelfModules) { TEST(ProcessReaderLinux, SelfModules) {
const std::string module_name = "test_module.so";
ScopedModuleHandle empty_test_module(LoadTestModule(module_name));
ASSERT_TRUE(empty_test_module.valid());
FakePtraceConnection connection; FakePtraceConnection connection;
connection.Initialize(getpid()); connection.Initialize(getpid());
@ -509,15 +734,19 @@ TEST(ProcessReaderLinux, SelfModules) {
ASSERT_TRUE(process_reader.Initialize(&connection)); ASSERT_TRUE(process_reader.Initialize(&connection));
ExpectModulesFromSelf(process_reader.Modules()); ExpectModulesFromSelf(process_reader.Modules());
ExpectTestModule(&process_reader, module_name);
} }
class ChildModuleTest : public Multiprocess { class ChildModuleTest : public Multiprocess {
public: public:
ChildModuleTest() : Multiprocess() {} ChildModuleTest() : Multiprocess(), module_name_("test_module.so") {}
~ChildModuleTest() = default; ~ChildModuleTest() = default;
private: private:
void MultiprocessParent() override { void MultiprocessParent() override {
char c;
ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));
DirectPtraceConnection connection; DirectPtraceConnection connection;
ASSERT_TRUE(connection.Initialize(ChildPID())); ASSERT_TRUE(connection.Initialize(ChildPID()));
@ -525,9 +754,20 @@ class ChildModuleTest : public Multiprocess {
ASSERT_TRUE(process_reader.Initialize(&connection)); ASSERT_TRUE(process_reader.Initialize(&connection));
ExpectModulesFromSelf(process_reader.Modules()); ExpectModulesFromSelf(process_reader.Modules());
ExpectTestModule(&process_reader, module_name_);
} }
void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } void MultiprocessChild() override {
ScopedModuleHandle empty_test_module(LoadTestModule(module_name_));
ASSERT_TRUE(empty_test_module.valid());
char c;
ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, sizeof(c)));
CheckedReadFileAtEOF(ReadPipeHandle());
}
const std::string module_name_;
DISALLOW_COPY_AND_ASSIGN(ChildModuleTest); DISALLOW_COPY_AND_ASSIGN(ChildModuleTest);
}; };

View File

@ -31,6 +31,7 @@ ProcessSnapshotMinidump::ProcessSnapshotMinidump()
crashpad_info_(), crashpad_info_(),
annotations_simple_map_(), annotations_simple_map_(),
file_reader_(nullptr), file_reader_(nullptr),
process_id_(static_cast<pid_t>(-1)),
initialized_() { initialized_() {
} }
@ -83,19 +84,19 @@ bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) {
stream_map_[stream_type] = &directory.Location; stream_map_[stream_type] = &directory.Location;
} }
INITIALIZATION_STATE_SET_VALID(initialized_); if (!InitializeCrashpadInfo() ||
!InitializeMiscInfo() ||
if (!InitializeCrashpadInfo()) { !InitializeModules()) {
return false; return false;
} }
return InitializeModules(); INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
} }
pid_t ProcessSnapshotMinidump::ProcessID() const { pid_t ProcessSnapshotMinidump::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://crashpad.chromium.org/bug/10 return process_id_;
return 0;
} }
pid_t ProcessSnapshotMinidump::ParentProcessID() const { pid_t ProcessSnapshotMinidump::ParentProcessID() const {
@ -234,6 +235,45 @@ bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
&annotations_simple_map_); &annotations_simple_map_);
} }
bool ProcessSnapshotMinidump::InitializeMiscInfo() {
const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMiscInfo);
if (stream_it == stream_map_.end()) {
return true;
}
if (!file_reader_->SeekSet(stream_it->second->Rva)) {
return false;
}
const size_t size = stream_it->second->DataSize;
if (size != sizeof(MINIDUMP_MISC_INFO_5) &&
size != sizeof(MINIDUMP_MISC_INFO_4) &&
size != sizeof(MINIDUMP_MISC_INFO_3) &&
size != sizeof(MINIDUMP_MISC_INFO_2) &&
size != sizeof(MINIDUMP_MISC_INFO)) {
LOG(ERROR) << "misc_info size mismatch";
return false;
}
MINIDUMP_MISC_INFO_5 info;
if (!file_reader_->ReadExactly(&info, size)) {
return false;
}
switch (stream_it->second->DataSize) {
case sizeof(MINIDUMP_MISC_INFO_5):
case sizeof(MINIDUMP_MISC_INFO_4):
case sizeof(MINIDUMP_MISC_INFO_3):
case sizeof(MINIDUMP_MISC_INFO_2):
case sizeof(MINIDUMP_MISC_INFO):
// TODO(jperaza): Save the remaining misc info.
// https://crashpad.chromium.org/bug/10
process_id_ = info.ProcessId;
}
return true;
}
bool ProcessSnapshotMinidump::InitializeModules() { bool ProcessSnapshotMinidump::InitializeModules() {
const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList); const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList);
if (stream_it == stream_map_.end()) { if (stream_it == stream_map_.end()) {

View File

@ -92,6 +92,10 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot {
std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>* std::map<uint32_t, MINIDUMP_LOCATION_DESCRIPTOR>*
module_crashpad_info_links); module_crashpad_info_links);
// Initializes data carried in a MINIDUMP_MISC_INFO structure on behalf of
// Initialize().
bool InitializeMiscInfo();
MINIDUMP_HEADER header_; MINIDUMP_HEADER header_;
std::vector<MINIDUMP_DIRECTORY> stream_directory_; std::vector<MINIDUMP_DIRECTORY> stream_directory_;
std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_; std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
@ -100,6 +104,7 @@ class ProcessSnapshotMinidump final : public ProcessSnapshot {
MinidumpCrashpadInfo crashpad_info_; MinidumpCrashpadInfo crashpad_info_;
std::map<std::string, std::string> annotations_simple_map_; std::map<std::string, std::string> annotations_simple_map_;
FileReaderInterface* file_reader_; // weak FileReaderInterface* file_reader_; // weak
pid_t process_id_;
InitializationStateDcheck initialized_; InitializationStateDcheck initialized_;
DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMinidump); DISALLOW_COPY_AND_ASSIGN(ProcessSnapshotMinidump);

View File

@ -420,6 +420,38 @@ TEST(ProcessSnapshotMinidump, Modules) {
EXPECT_EQ(annotation_objects, annotations_4); EXPECT_EQ(annotation_objects, annotations_4);
} }
TEST(ProcessSnapshotMinidump, ProcessID) {
StringFile string_file;
MINIDUMP_HEADER header = {};
ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
static const pid_t kTestProcessId = 42;
MINIDUMP_MISC_INFO misc_info = {};
misc_info.SizeOfInfo = sizeof(misc_info);
misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID;
misc_info.ProcessId = kTestProcessId;
MINIDUMP_DIRECTORY misc_directory = {};
misc_directory.StreamType = kMinidumpStreamTypeMiscInfo;
misc_directory.Location.DataSize = sizeof(misc_info);
misc_directory.Location.Rva = static_cast<RVA>(string_file.SeekGet());
ASSERT_TRUE(string_file.Write(&misc_info, sizeof(misc_info)));
header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
header.Signature = MINIDUMP_SIGNATURE;
header.Version = MINIDUMP_VERSION;
header.NumberOfStreams = 1;
ASSERT_TRUE(string_file.SeekSet(0));
ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
ProcessSnapshotMinidump process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(&string_file));
EXPECT_EQ(process_snapshot.ProcessID(), kTestProcessId);
}
} // namespace } // namespace
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

View File

@ -19,6 +19,7 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "test/multiprocess_exec.h" #include "test/multiprocess_exec.h"
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/misc/address_sanitizer.h"
#include "util/numeric/safe_assignment.h" #include "util/numeric/safe_assignment.h"
#if defined(OS_LINUX) || defined(OS_ANDROID) #if defined(OS_LINUX) || defined(OS_ANDROID)
@ -162,6 +163,23 @@ class StackSanitizationChecker : public MemorySnapshot::Delegate {
// MemorySnapshot::Delegate // MemorySnapshot::Delegate
bool MemorySnapshotDelegateRead(void* data, size_t size) override { bool MemorySnapshotDelegateRead(void* data, size_t size) override {
#if defined(ADDRESS_SANITIZER) && (defined(OS_LINUX) || defined(OS_ANDROID))
// AddressSanitizer causes stack variables to be stored separately from the
// call stack.
auto addr_not_in_stack_range =
[](VMAddress addr, VMAddress stack_addr, VMSize stack_size) {
return addr < stack_addr || addr >= stack_addr + stack_size;
};
EXPECT_PRED3(addr_not_in_stack_range,
addrs_.code_pointer_address,
stack_->Address(),
size);
EXPECT_PRED3(addr_not_in_stack_range,
addrs_.string_address,
stack_->Address(),
size);
return true;
#else
size_t pointer_offset; size_t pointer_offset;
if (!AssignIfInRange(&pointer_offset, if (!AssignIfInRange(&pointer_offset,
addrs_.code_pointer_address - stack_->Address())) { addrs_.code_pointer_address - stack_->Address())) {
@ -192,6 +210,7 @@ class StackSanitizationChecker : public MemorySnapshot::Delegate {
EXPECT_STREQ(string, kSensitiveStackData); EXPECT_STREQ(string, kSensitiveStackData);
} }
return true; return true;
#endif // ADDRESS_SANITIZER && (OS_LINUX || OS_ANDROID)
} }
private: private:

View File

@ -36,6 +36,11 @@ void ScopedModuleHandle::Impl::Close(ModuleHandle handle) {
ScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {} ScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {}
ScopedModuleHandle::ScopedModuleHandle(ScopedModuleHandle&& other)
: handle_(other.handle_) {
other.handle_ = nullptr;
}
ScopedModuleHandle::~ScopedModuleHandle() { ScopedModuleHandle::~ScopedModuleHandle() {
if (valid()) { if (valid()) {
Impl::Close(handle_); Impl::Close(handle_);

View File

@ -57,6 +57,7 @@ class ScopedModuleHandle {
using ModuleHandle = Impl::ModuleHandle; using ModuleHandle = Impl::ModuleHandle;
explicit ScopedModuleHandle(ModuleHandle handle); explicit ScopedModuleHandle(ModuleHandle handle);
ScopedModuleHandle(ScopedModuleHandle&& handle);
~ScopedModuleHandle(); ~ScopedModuleHandle();
//! \return The module handle being managed. //! \return The module handle being managed.

View File

@ -296,40 +296,39 @@ const MemoryMap::Mapping* MemoryMap::FindMappingWithName(
return nullptr; return nullptr;
} }
const MemoryMap::Mapping* MemoryMap::FindFileMmapStart( std::vector<const MemoryMap::Mapping*> MemoryMap::FindFilePossibleMmapStarts(
const Mapping& mapping) const { const Mapping& mapping) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
size_t index = 0; std::vector<const Mapping*> possible_starts;
for (; index < mappings_.size(); ++index) {
if (mappings_[index].Equals(mapping)) {
break;
}
}
if (index >= mappings_.size()) {
LOG(ERROR) << "mapping not found";
return nullptr;
}
// If the mapping is anonymous, as is for the VDSO, there is no mapped file to // If the mapping is anonymous, as is for the VDSO, there is no mapped file to
// find the start of, so just return the input mapping. // find the start of, so just return the input mapping.
if (mapping.device == 0 && mapping.inode == 0) { if (mapping.device == 0 && mapping.inode == 0) {
return &mappings_[index]; for (const auto& candidate : mappings_) {
if (mapping.Equals(candidate)) {
possible_starts.push_back(&candidate);
return possible_starts;
}
}
LOG(ERROR) << "mapping not found";
return std::vector<const Mapping*>();
} }
do { for (const auto& candidate : mappings_) {
// There may by anonymous mappings or other files mapped into the holes, if (candidate.device == mapping.device &&
// so check that the mapping uses the same file as the input, but keep candidate.inode == mapping.inode &&
// searching if it doesn't. candidate.offset == 0) {
if (mappings_[index].device == mapping.device && possible_starts.push_back(&candidate);
mappings_[index].inode == mapping.inode &&
mappings_[index].offset == 0) {
return &mappings_[index];
} }
} while (index--); if (mapping.Equals(candidate)) {
return possible_starts;
}
}
LOG(ERROR) << "mapping not found"; LOG(ERROR) << "mapping not found";
return nullptr; return std::vector<const Mapping*>();
} }
} // namespace crashpad } // namespace crashpad

View File

@ -76,20 +76,27 @@ class MemoryMap {
//! it was obtained from. //! it was obtained from.
const Mapping* FindMappingWithName(const std::string& name) const; const Mapping* FindMappingWithName(const std::string& name) const;
//! \brief Find the first Mapping in a series of mappings for the same file. //! \brief Find Mappings that share a Mapping's file, mapped from offset 0.
//! //!
//! Executables and libaries are typically loaded into several mappings with //! Executables and libaries are typically loaded into several mappings with
//! varying permissions for different segments. This method searches for the //! varying permissions for different segments. Portions of an ELF file may
//! mapping with the highest address at or below \a mapping, which maps the //! be mapped multiple times as part of loading the file, for example, when
//! same file as \a mapping from file offset 0. //! initializing GNU_RELRO segments. This method searches for mappings at or
//! below \a mapping in memory that are mapped from the same file as \a
//! mapping from offset 0.
//! //!
//! If \a mapping is not found, `nullptr` is returned. If \a mapping is found //! This method is intended to help identify the possible base address for
//! but does not map a file, \a mapping is returned. //! loaded modules, but it is the caller's responsibility to determine which
//! returned mapping is correct.
//!
//! If \a mapping does not refer to a valid mapping, an empty vector will be
//! returned and a message will be logged. If \a mapping is found but does not
//! map a file, \a mapping is returned in \a possible_starts.
//! //!
//! \param[in] mapping A Mapping whose series to find the start of. //! \param[in] mapping A Mapping whose series to find the start of.
//! \return The first Mapping in the series or `nullptr` on failure with a //! \return a vector of the possible mapping starts.
//! message logged. std::vector<const Mapping*> FindFilePossibleMmapStarts(
const Mapping* FindFileMmapStart(const Mapping& mapping) const; const Mapping& mapping) const;
private: private:
std::vector<Mapping> mappings_; std::vector<Mapping> mappings_;

View File

@ -355,8 +355,8 @@ TEST(MemoryMap, MapRunningChild) {
// Expects first and third pages from mapping_start to refer to the same mapped // Expects first and third pages from mapping_start to refer to the same mapped
// file. The second page should not. // file. The second page should not.
void ExpectFindFileMmapStart(LinuxVMAddress mapping_start, void ExpectFindFilePossibleMmapStarts(LinuxVMAddress mapping_start,
LinuxVMSize page_size) { LinuxVMSize page_size) {
FakePtraceConnection connection; FakePtraceConnection connection;
ASSERT_TRUE(connection.Initialize(getpid())); ASSERT_TRUE(connection.Initialize(getpid()));
@ -373,17 +373,27 @@ void ExpectFindFileMmapStart(LinuxVMAddress mapping_start,
ASSERT_NE(mapping1, mapping2); ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3); ASSERT_NE(mapping2, mapping3);
EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1); std::vector<const MemoryMap::Mapping*> mappings;
EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping2);
EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1); mappings = map.FindFilePossibleMmapStarts(*mapping1);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
mappings = map.FindFilePossibleMmapStarts(*mapping2);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping2);
mappings = map.FindFilePossibleMmapStarts(*mapping3);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
} }
TEST(MemoryMap, FindFileMmapStart) { TEST(MemoryMap, FindFilePossibleMmapStarts) {
const size_t page_size = getpagesize(); const size_t page_size = getpagesize();
ScopedTempDir temp_dir; ScopedTempDir temp_dir;
base::FilePath path = base::FilePath path = temp_dir.path().Append(
temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile")); FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile"));
ScopedFileHandle handle; ScopedFileHandle handle;
size_t file_length = page_size * 3; size_t file_length = page_size * 3;
ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle)); ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle));
@ -418,9 +428,19 @@ TEST(MemoryMap, FindFileMmapStart) {
ASSERT_NE(mapping1, mapping2); ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3); ASSERT_NE(mapping2, mapping3);
EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1); std::vector<const MemoryMap::Mapping*> mappings;
EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping1);
EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1); mappings = map.FindFilePossibleMmapStarts(*mapping1);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
mappings = map.FindFilePossibleMmapStarts(*mapping2);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
mappings = map.FindFilePossibleMmapStarts(*mapping3);
ASSERT_EQ(mappings.size(), 1u);
EXPECT_EQ(mappings[0], mapping1);
#if defined(ARCH_CPU_64_BITS) #if defined(ARCH_CPU_64_BITS)
constexpr bool is_64_bit = true; constexpr bool is_64_bit = true;
@ -429,7 +449,7 @@ TEST(MemoryMap, FindFileMmapStart) {
#endif #endif
MemoryMap::Mapping bad_mapping; MemoryMap::Mapping bad_mapping;
bad_mapping.range.SetRange(is_64_bit, 0, 1); bad_mapping.range.SetRange(is_64_bit, 0, 1);
EXPECT_EQ(map.FindFileMmapStart(bad_mapping), nullptr); EXPECT_EQ(map.FindFilePossibleMmapStarts(bad_mapping).size(), 0u);
} }
// Make the second page an anonymous mapping // Make the second page an anonymous mapping
@ -449,12 +469,12 @@ TEST(MemoryMap, FindFileMmapStart) {
MAP_PRIVATE | MAP_FIXED, MAP_PRIVATE | MAP_FIXED,
handle.get(), handle.get(),
page_size * 2)); page_size * 2));
ExpectFindFileMmapStart(mapping_start, page_size); ExpectFindFilePossibleMmapStarts(mapping_start, page_size);
// Map the second page to another file. // Map the second page to another file.
ScopedFileHandle handle2; ScopedFileHandle handle2;
base::FilePath path2 = base::FilePath path2 = temp_dir.path().Append(
temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile2")); FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile2"));
ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2)); ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2));
page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size, page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size,
@ -463,7 +483,106 @@ TEST(MemoryMap, FindFileMmapStart) {
MAP_PRIVATE | MAP_FIXED, MAP_PRIVATE | MAP_FIXED,
handle2.get(), handle2.get(),
0); 0);
ExpectFindFileMmapStart(mapping_start, page_size); ExpectFindFilePossibleMmapStarts(mapping_start, page_size);
}
TEST(MemoryMap, FindFilePossibleMmapStarts_MultipleStarts) {
ScopedTempDir temp_dir;
base::FilePath path =
temp_dir.path().Append(FILE_PATH_LITERAL("MultipleStartsTestFile"));
const size_t page_size = getpagesize();
ScopedFileHandle handle;
ASSERT_NO_FATAL_FAILURE(InitializeFile(path, page_size * 2, &handle));
// Locate a sequence of pages to setup a test in.
char* seq_addr;
{
ScopedMmap whole_mapping;
ASSERT_TRUE(whole_mapping.ResetMmap(
nullptr, page_size * 8, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0));
seq_addr = whole_mapping.addr_as<char*>();
}
// Arrange file and anonymous mappings in the sequence.
ScopedMmap file_mapping0;
ASSERT_TRUE(file_mapping0.ResetMmap(seq_addr,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
page_size));
ScopedMmap file_mapping1;
ASSERT_TRUE(file_mapping1.ResetMmap(seq_addr + page_size,
page_size * 2,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
ScopedMmap file_mapping2;
ASSERT_TRUE(file_mapping2.ResetMmap(seq_addr + page_size * 3,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
// Skip a page
ScopedMmap file_mapping3;
ASSERT_TRUE(file_mapping3.ResetMmap(seq_addr + page_size * 5,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
ScopedMmap anon_mapping;
ASSERT_TRUE(anon_mapping.ResetMmap(seq_addr + page_size * 6,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_ANON | MAP_FIXED,
-1,
0));
ScopedMmap file_mapping4;
ASSERT_TRUE(file_mapping4.ResetMmap(seq_addr + page_size * 7,
page_size,
PROT_READ,
MAP_PRIVATE | MAP_FIXED,
handle.get(),
0));
FakePtraceConnection connection;
ASSERT_TRUE(connection.Initialize(getpid()));
MemoryMap map;
ASSERT_TRUE(map.Initialize(&connection));
auto mapping = map.FindMapping(file_mapping0.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
auto possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 0u);
mapping = map.FindMapping(file_mapping1.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 1u);
mapping = map.FindMapping(file_mapping2.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 2u);
mapping = map.FindMapping(file_mapping3.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 3u);
mapping = map.FindMapping(file_mapping4.addr_as<VMAddress>());
ASSERT_TRUE(mapping);
possible_starts = map.FindFilePossibleMmapStarts(*mapping);
EXPECT_EQ(possible_starts.size(), 4u);
} }
} // namespace } // namespace

View File

@ -101,7 +101,7 @@ constexpr const char* kFlavorNames[] = {
"PPC_THREAD_STATE64", "PPC_THREAD_STATE64",
"PPC_EXCEPTION_STATE64", "PPC_EXCEPTION_STATE64",
"THREAD_STATE_NONE", "THREAD_STATE_NONE",
#elif defined(__arm__) || defined(__arm64__) #elif defined(__arm__) || defined(__aarch64__)
// sed -Ene 's/^#define ((ARM|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p' // sed -Ene 's/^#define ((ARM|THREAD)_[[:graph:]]+)[[:space:]]+[[:digit:]]{1,2}.*$/ "\1",/p'
// usr/include/mach/arm/thread_status.h // usr/include/mach/arm/thread_status.h
// (iOS 7 SDK) // (iOS 7 SDK)
@ -153,7 +153,7 @@ std::string ThreadStateFlavorFullToShort(const base::StringPiece& flavor) {
static constexpr char kArchPrefix[] = "x86_"; static constexpr char kArchPrefix[] = "x86_";
#elif defined(__ppc__) || defined(__ppc64__) #elif defined(__ppc__) || defined(__ppc64__)
static constexpr char kArchPrefix[] = "PPC_"; static constexpr char kArchPrefix[] = "PPC_";
#elif defined(__arm__) || defined(__arm64__) #elif defined(__arm__) || defined(__aarch64__)
static constexpr char kArchPrefix[] = "ARM_" static constexpr char kArchPrefix[] = "ARM_"
#endif #endif
prefix_len = strlen(kArchPrefix); prefix_len = strlen(kArchPrefix);

View File

@ -801,7 +801,7 @@ constexpr struct {
{PPC_VECTOR_STATE, "PPC_VECTOR_STATE", "VECTOR"}, {PPC_VECTOR_STATE, "PPC_VECTOR_STATE", "VECTOR"},
{PPC_THREAD_STATE64, "PPC_THREAD_STATE64", "THREAD64"}, {PPC_THREAD_STATE64, "PPC_THREAD_STATE64", "THREAD64"},
{PPC_EXCEPTION_STATE64, "PPC_EXCEPTION_STATE64", "EXCEPTION64"}, {PPC_EXCEPTION_STATE64, "PPC_EXCEPTION_STATE64", "EXCEPTION64"},
#elif defined(__arm__) || defined(__arm64__) #elif defined(__arm__) || defined(__aarch64__)
{ARM_THREAD_STATE, "ARM_THREAD_STATE", "THREAD"}, {ARM_THREAD_STATE, "ARM_THREAD_STATE", "THREAD"},
{ARM_VFP_STATE, "ARM_VFP_STATE", "VFP"}, {ARM_VFP_STATE, "ARM_VFP_STATE", "VFP"},
{ARM_EXCEPTION_STATE, "ARM_EXCEPTION_STATE", "EXCEPTION"}, {ARM_EXCEPTION_STATE, "ARM_EXCEPTION_STATE", "EXCEPTION"},
@ -860,7 +860,7 @@ TEST(SymbolicConstantsMach, ThreadStateFlavorToString) {
flavor <= x86_AVX_STATE flavor <= x86_AVX_STATE
#elif defined(__ppc__) || defined(__ppc64__) #elif defined(__ppc__) || defined(__ppc64__)
flavor <= THREAD_STATE_NONE flavor <= THREAD_STATE_NONE
#elif defined(__arm__) || defined(__arm64__) #elif defined(__arm__) || defined(__aarch64__)
(flavor <= ARM_EXCEPTION_STATE64 || flavor == ARM_THREAD_STATE32 || (flavor <= ARM_EXCEPTION_STATE64 || flavor == ARM_THREAD_STATE32 ||
(flavor >= ARM_DEBUG_STATE32 && flavor <= ARM_NEON_STATE64)) (flavor >= ARM_DEBUG_STATE32 && flavor <= ARM_NEON_STATE64))
#endif #endif
@ -948,7 +948,7 @@ TEST(SymbolicConstantsMach, StringToThreadStateFlavor) {
"PPC_JUNK_STATE32", "PPC_JUNK_STATE32",
"x86_THREAD_STATE", "x86_THREAD_STATE",
"ARM_THREAD_STATE", "ARM_THREAD_STATE",
#elif defined(__arm__) || defined(__arm64__) #elif defined(__arm__) || defined(__aarch64__)
" ARM_THREAD_STATE64", " ARM_THREAD_STATE64",
"ARM_THREAD_STATE64 ", "ARM_THREAD_STATE64 ",
"ARM_THREAD_STATE642", "ARM_THREAD_STATE642",
@ -1013,7 +1013,7 @@ TEST(SymbolicConstantsMach, StringToThreadStateFlavor) {
NUL_TEST_DATA("PPC_THREAD_\0STATE64"), NUL_TEST_DATA("PPC_THREAD_\0STATE64"),
NUL_TEST_DATA("PPC_THREAD_STA\0TE64"), NUL_TEST_DATA("PPC_THREAD_STA\0TE64"),
NUL_TEST_DATA("PPC_THREAD_STATE\00064"), NUL_TEST_DATA("PPC_THREAD_STATE\00064"),
#elif defined(__arm__) || defined(__arm64__) #elif defined(__arm__) || defined(__aarch64__)
NUL_TEST_DATA("\0ARM_THREAD_STATE64"), NUL_TEST_DATA("\0ARM_THREAD_STATE64"),
NUL_TEST_DATA("ARM\0_THREAD_STATE64"), NUL_TEST_DATA("ARM\0_THREAD_STATE64"),
NUL_TEST_DATA("ARM_\0THREAD_STATE64"), NUL_TEST_DATA("ARM_\0THREAD_STATE64"),

View File

@ -60,12 +60,16 @@ void TestCaptureContext() {
kReferencePC); kReferencePC);
#endif // !defined(ADDRESS_SANITIZER) #endif // !defined(ADDRESS_SANITIZER)
// Declare sp and context_2 here because all local variables need to be const uintptr_t sp = StackPointerFromContext(context_1);
// declared before computing the stack pointer reference value, so that the
// reference value can be the lowest value possible. // Declare context_2 here because all local variables need to be declared
uintptr_t sp; // before computing the stack pointer reference value, so that the reference
// value can be the lowest value possible.
NativeCPUContext context_2; NativeCPUContext context_2;
// AddressSanitizer on Linux causes stack variables to be stored separately from
// the call stack.
#if !defined(ADDRESS_SANITIZER) || (!defined(OS_LINUX) && !defined(OS_ANDROID))
// The stack pointer reference value is the lowest address of a local variable // The stack pointer reference value is the lowest address of a local variable
// in this function. The captured program counter will be slightly less than // in this function. The captured program counter will be slightly less than
// or equal to the reference stack pointer. // or equal to the reference stack pointer.
@ -74,11 +78,11 @@ void TestCaptureContext() {
reinterpret_cast<uintptr_t>(&context_2)), reinterpret_cast<uintptr_t>(&context_2)),
std::min(reinterpret_cast<uintptr_t>(&pc), std::min(reinterpret_cast<uintptr_t>(&pc),
reinterpret_cast<uintptr_t>(&sp))); reinterpret_cast<uintptr_t>(&sp)));
sp = StackPointerFromContext(context_1);
EXPECT_PRED2([](uintptr_t actual, EXPECT_PRED2([](uintptr_t actual,
uintptr_t reference) { return reference - actual < 768u; }, uintptr_t reference) { return reference - actual < 768u; },
sp, sp,
kReferenceSP); kReferenceSP);
#endif // !ADDRESS_SANITIZER || (!OS_LINUX && !OS_ANDROID)
// Capture the context again, expecting that the stack pointer stays the same // Capture the context again, expecting that the stack pointer stays the same
// and the program counter increases. Strictly speaking, theres no guarantee // and the program counter increases. Strictly speaking, theres no guarantee

View File

@ -77,6 +77,10 @@ class Metrics {
//! server. //! server.
kUploadFailed = 4, kUploadFailed = 4,
//! \brief There was an error between accessing the report from the database
//! and uploading it to the crash server.
kPrepareForUploadFailed = 5,
//! \brief The number of values in this enumeration; not a valid value. //! \brief The number of values in this enumeration; not a valid value.
kMaxValue kMaxValue
}; };