Merge master 42b57efa554a into doc

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

205
DEPS
View File

@ -37,6 +37,90 @@ deps = {
'crashpad/third_party/zlib/zlib':
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
'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 = [
@ -118,28 +202,6 @@ hooks = [
'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
# sysroot.
@ -150,105 +212,6 @@ hooks = [
'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',
'pattern': '\.gypi?$',

View File

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

View File

@ -178,6 +178,7 @@ class CrashReportDatabase {
CrashReportDatabase* database_;
std::vector<std::unique_ptr<FileReader>> attachment_readers_;
std::map<std::string, FileReader*> attachment_map_;
bool report_metrics_;
DISALLOW_COPY_AND_ASSIGN(UploadReport);
};
@ -326,11 +327,16 @@ class CrashReportDatabase {
//! \param[in] uuid The unique identifier for the crash report record.
//! \param[out] report A crash report record for the report to be uploaded.
//! 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.
virtual OperationStatus GetReportForUploading(
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
//! upload attempt time as returned by

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,7 +26,7 @@
static constexpr __ptrace_request PTRACE_GET_THREAD_AREA =
static_cast<__ptrace_request>(25);
#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_cast<__ptrace_request>(22);
#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
#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_cast<__ptrace_request>(27);
#define PTRACE_GETVFPREGS PTRACE_GETVFPREGS

View File

@ -20,7 +20,7 @@
#include <features.h>
// 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)
using user_regs_struct = user_pt_regs;
using user_fpsimd_struct = user_fpsimd_state;

View File

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

View File

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

View File

@ -33,10 +33,19 @@ verifiers {
try_job {
buckets {
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_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_rel" }
builders { name: "crashpad_try_fuchsia_arm64_dbg" }

View File

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

View File

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

View File

@ -118,7 +118,13 @@ class ElfImageReader {
//! \param[in] memory A memory reader for the remote process.
//! \param[in] address The address in the remote process' address space where
//! 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.
//!
@ -172,6 +178,16 @@ class ElfImageReader {
//! \return `true` if the debug address was found.
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
//! segments in the image.
//!
@ -238,7 +254,7 @@ class ElfImageReader {
template <typename PhdrType>
class ProgramHeaderTableSpecific;
bool InitializeProgramHeaders();
bool InitializeProgramHeaders(bool verbose);
bool InitializeDynamicArray();
bool InitializeDynamicSymbolTable();
bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address);

View File

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

View File

@ -74,9 +74,10 @@ void TestAgainstTarget(PtraceConnection* connection) {
const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs);
ASSERT_TRUE(phdr_mapping);
const MemoryMap::Mapping* exe_mapping =
mappings.FindFileMmapStart(*phdr_mapping);
LinuxVMAddress elf_address = exe_mapping->range.Base();
std::vector<const MemoryMap::Mapping*> exe_mappings =
mappings.FindFilePossibleMmapStarts(*phdr_mapping);
ASSERT_EQ(exe_mappings.size(), 1u);
LinuxVMAddress elf_address = exe_mappings[0]->range.Base();
ProcessMemoryLinux memory;
ASSERT_TRUE(memory.Initialize(connection->GetProcessID()));
@ -142,9 +143,24 @@ void TestAgainstTarget(PtraceConnection* connection) {
mappings.FindMapping(module.dynamic_array);
ASSERT_TRUE(dyn_mapping);
const MemoryMap::Mapping* module_mapping =
mappings.FindFileMmapStart(*dyn_mapping);
ASSERT_TRUE(module_mapping);
std::vector<const MemoryMap::Mapping*> possible_mappings =
mappings.FindFilePossibleMmapStarts(*dyn_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)
EXPECT_FALSE(module.name.empty());
@ -168,20 +184,17 @@ void TestAgainstTarget(PtraceConnection* connection) {
module.name);
#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
// (API 20) until Android 6.0 (API 23).
if (is_android_loader && android_runtime_api > 20 &&
android_runtime_api < 23) {
EXPECT_EQ(module.load_bias, 0);
} else {
EXPECT_EQ(module.load_bias, module_reader.GetLoadBias());
EXPECT_EQ(module.load_bias, module_reader->GetLoadBias());
}
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));
}
}

View File

@ -347,20 +347,45 @@ void ProcessReaderLinux::InitializeModules() {
return;
}
const MemoryMap::Mapping* exe_mapping;
if (!(exe_mapping = GetMemoryMap()->FindMapping(phdrs)) ||
!(exe_mapping = GetMemoryMap()->FindFileMmapStart(*exe_mapping))) {
return;
}
ProcessMemoryRange range;
if (!range.Initialize(Memory(), is_64_bit_)) {
return;
}
auto exe_reader = std::make_unique<ElfImageReader>();
if (!exe_reader->Initialize(range, exe_mapping->range.Base())) {
return;
// The strategy used for identifying loaded modules depends on ELF files
// conventionally loading their header and program headers into memory.
// 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;
@ -386,19 +411,42 @@ void ProcessReaderLinux::InitializeModules() {
aux.GetValue(AT_BASE, &loader_base);
for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) {
const MemoryMap::Mapping* mapping;
if (!(mapping = memory_map_.FindMapping(entry.dynamic_array)) ||
!(mapping = memory_map_.FindFileMmapStart(*mapping))) {
continue;
}
const MemoryMap::Mapping* module_mapping = nullptr;
std::unique_ptr<ElfImageReader> elf_reader;
{
const MemoryMap::Mapping* dyn_mapping =
memory_map_.FindMapping(entry.dynamic_array);
if (!dyn_mapping) {
continue;
}
auto elf_reader = std::make_unique<ElfImageReader>();
if (!elf_reader->Initialize(range, mapping->range.Base())) {
continue;
std::vector<const MemoryMap::Mapping*> possible_mappings =
memory_map_.FindFilePossibleMmapStarts(*dyn_mapping);
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.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.type = loader_base && elf_reader->Address() == loader_base
? ModuleSnapshot::kModuleTypeDynamicLoader

View File

@ -14,6 +14,8 @@
#include "snapshot/linux/process_reader_linux.h"
#include <dlfcn.h>
#include <elf.h>
#include <errno.h>
#include <link.h>
#include <pthread.h>
@ -38,8 +40,13 @@
#include "test/linux/fake_ptrace_connection.h"
#include "test/linux/get_tls.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_writer.h"
#include "util/file/filesystem.h"
#include "util/linux/direct_ptrace_connection.h"
#include "util/misc/address_sanitizer.h"
#include "util/misc/from_pointer_cast.h"
#include "util/synchronization/semaphore.h"
@ -264,12 +271,17 @@ void ExpectThreads(const ThreadMap& thread_map,
iterator->second.tls);
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 +
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,
iterator->second.stack_address);
#endif // !defined(ADDRESS_SANITIZER)
if (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
}
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) {
const std::string module_name = "test_module.so";
ScopedModuleHandle empty_test_module(LoadTestModule(module_name));
ASSERT_TRUE(empty_test_module.valid());
FakePtraceConnection connection;
connection.Initialize(getpid());
@ -509,15 +734,19 @@ TEST(ProcessReaderLinux, SelfModules) {
ASSERT_TRUE(process_reader.Initialize(&connection));
ExpectModulesFromSelf(process_reader.Modules());
ExpectTestModule(&process_reader, module_name);
}
class ChildModuleTest : public Multiprocess {
public:
ChildModuleTest() : Multiprocess() {}
ChildModuleTest() : Multiprocess(), module_name_("test_module.so") {}
~ChildModuleTest() = default;
private:
void MultiprocessParent() override {
char c;
ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));
DirectPtraceConnection connection;
ASSERT_TRUE(connection.Initialize(ChildPID()));
@ -525,9 +754,20 @@ class ChildModuleTest : public Multiprocess {
ASSERT_TRUE(process_reader.Initialize(&connection));
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);
};

View File

@ -31,6 +31,7 @@ ProcessSnapshotMinidump::ProcessSnapshotMinidump()
crashpad_info_(),
annotations_simple_map_(),
file_reader_(nullptr),
process_id_(static_cast<pid_t>(-1)),
initialized_() {
}
@ -83,19 +84,19 @@ bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) {
stream_map_[stream_type] = &directory.Location;
}
INITIALIZATION_STATE_SET_VALID(initialized_);
if (!InitializeCrashpadInfo()) {
if (!InitializeCrashpadInfo() ||
!InitializeMiscInfo() ||
!InitializeModules()) {
return false;
}
return InitializeModules();
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;
}
pid_t ProcessSnapshotMinidump::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
NOTREACHED(); // https://crashpad.chromium.org/bug/10
return 0;
return process_id_;
}
pid_t ProcessSnapshotMinidump::ParentProcessID() const {
@ -234,6 +235,45 @@ bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
&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() {
const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList);
if (stream_it == stream_map_.end()) {

View File

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

View File

@ -420,6 +420,38 @@ TEST(ProcessSnapshotMinidump, Modules) {
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 test
} // namespace crashpad

View File

@ -19,6 +19,7 @@
#include "gtest/gtest.h"
#include "test/multiprocess_exec.h"
#include "util/file/file_io.h"
#include "util/misc/address_sanitizer.h"
#include "util/numeric/safe_assignment.h"
#if defined(OS_LINUX) || defined(OS_ANDROID)
@ -162,6 +163,23 @@ class StackSanitizationChecker : public MemorySnapshot::Delegate {
// MemorySnapshot::Delegate
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;
if (!AssignIfInRange(&pointer_offset,
addrs_.code_pointer_address - stack_->Address())) {
@ -192,6 +210,7 @@ class StackSanitizationChecker : public MemorySnapshot::Delegate {
EXPECT_STREQ(string, kSensitiveStackData);
}
return true;
#endif // ADDRESS_SANITIZER && (OS_LINUX || OS_ANDROID)
}
private:

View File

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

View File

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

View File

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

View File

@ -76,20 +76,27 @@ class MemoryMap {
//! it was obtained from.
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
//! varying permissions for different segments. This method searches for the
//! mapping with the highest address at or below \a mapping, which maps the
//! same file as \a mapping from file offset 0.
//! varying permissions for different segments. Portions of an ELF file may
//! be mapped multiple times as part of loading the file, for example, when
//! 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
//! but does not map a file, \a mapping is returned.
//! This method is intended to help identify the possible base address for
//! 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.
//! \return The first Mapping in the series or `nullptr` on failure with a
//! message logged.
const Mapping* FindFileMmapStart(const Mapping& mapping) const;
//! \return a vector of the possible mapping starts.
std::vector<const Mapping*> FindFilePossibleMmapStarts(
const Mapping& mapping) const;
private:
std::vector<Mapping> mappings_;

View File

@ -355,8 +355,8 @@ TEST(MemoryMap, MapRunningChild) {
// Expects first and third pages from mapping_start to refer to the same mapped
// file. The second page should not.
void ExpectFindFileMmapStart(LinuxVMAddress mapping_start,
LinuxVMSize page_size) {
void ExpectFindFilePossibleMmapStarts(LinuxVMAddress mapping_start,
LinuxVMSize page_size) {
FakePtraceConnection connection;
ASSERT_TRUE(connection.Initialize(getpid()));
@ -373,17 +373,27 @@ void ExpectFindFileMmapStart(LinuxVMAddress mapping_start,
ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3);
EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1);
EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping2);
EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1);
std::vector<const MemoryMap::Mapping*> mappings;
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();
ScopedTempDir temp_dir;
base::FilePath path =
temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile"));
base::FilePath path = temp_dir.path().Append(
FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile"));
ScopedFileHandle handle;
size_t file_length = page_size * 3;
ASSERT_NO_FATAL_FAILURE(InitializeFile(path, file_length, &handle));
@ -418,9 +428,19 @@ TEST(MemoryMap, FindFileMmapStart) {
ASSERT_NE(mapping1, mapping2);
ASSERT_NE(mapping2, mapping3);
EXPECT_EQ(map.FindFileMmapStart(*mapping1), mapping1);
EXPECT_EQ(map.FindFileMmapStart(*mapping2), mapping1);
EXPECT_EQ(map.FindFileMmapStart(*mapping3), mapping1);
std::vector<const MemoryMap::Mapping*> mappings;
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)
constexpr bool is_64_bit = true;
@ -429,7 +449,7 @@ TEST(MemoryMap, FindFileMmapStart) {
#endif
MemoryMap::Mapping bad_mapping;
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
@ -449,12 +469,12 @@ TEST(MemoryMap, FindFileMmapStart) {
MAP_PRIVATE | MAP_FIXED,
handle.get(),
page_size * 2));
ExpectFindFileMmapStart(mapping_start, page_size);
ExpectFindFilePossibleMmapStarts(mapping_start, page_size);
// Map the second page to another file.
ScopedFileHandle handle2;
base::FilePath path2 =
temp_dir.path().Append(FILE_PATH_LITERAL("FindFileMmapStartTestFile2"));
base::FilePath path2 = temp_dir.path().Append(
FILE_PATH_LITERAL("FindFilePossibleMmapStartsTestFile2"));
ASSERT_NO_FATAL_FAILURE(InitializeFile(path2, page_size, &handle2));
page2_mapping.ResetMmap(file_mapping.addr_as<char*>() + page_size,
@ -463,7 +483,106 @@ TEST(MemoryMap, FindFileMmapStart) {
MAP_PRIVATE | MAP_FIXED,
handle2.get(),
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

View File

@ -101,7 +101,7 @@ constexpr const char* kFlavorNames[] = {
"PPC_THREAD_STATE64",
"PPC_EXCEPTION_STATE64",
"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'
// usr/include/mach/arm/thread_status.h
// (iOS 7 SDK)
@ -153,7 +153,7 @@ std::string ThreadStateFlavorFullToShort(const base::StringPiece& flavor) {
static constexpr char kArchPrefix[] = "x86_";
#elif defined(__ppc__) || defined(__ppc64__)
static constexpr char kArchPrefix[] = "PPC_";
#elif defined(__arm__) || defined(__arm64__)
#elif defined(__arm__) || defined(__aarch64__)
static constexpr char kArchPrefix[] = "ARM_"
#endif
prefix_len = strlen(kArchPrefix);

View File

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

View File

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

View File

@ -77,6 +77,10 @@ class Metrics {
//! server.
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.
kMaxValue
};