mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-20 18:53:47 +00:00
Merge master 42b57efa554a into doc
This commit is contained in:
commit
07e174cd8d
205
DEPS
205
DEPS
@ -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?$',
|
||||||
|
@ -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_) {
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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());
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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",
|
||||||
]
|
]
|
||||||
|
@ -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();
|
||||||
|
|
||||||
|
@ -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" }
|
||||||
|
@ -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"
|
||||||
|
@ -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()) {
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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()) {
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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_);
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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_;
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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"),
|
||||||
|
@ -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, there’s no guarantee
|
// and the program counter increases. Strictly speaking, there’s no guarantee
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user