mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
Collect abort messages on Android.
As of Android Q, the android_set_abort_message() function copies the abort message into a mapping with a specific name that starts with a magic number. This makes it possible for Crashpad to collect the abort message by looking for the mapping with this name in procmaps and checking for the magic number. The abort message is stored in a process annotation named "abort_message". Test: No regressions in build/run_tests.py on devices running P and Q Test: Patched into Chromium; manually verified that HWASAN crash report appears in minidump Bug: crashpad:287 Change-Id: I23c4d9e11015c84341de2d2e47e38a1eec508a36 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/1544875 Commit-Queue: Peter Collingbourne <pcc@chromium.org> Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
parent
7d5d5ff25f
commit
94dc7eb437
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "client/crashpad_client.h"
|
#include "client/crashpad_client.h"
|
||||||
|
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/syscall.h>
|
#include <sys/syscall.h>
|
||||||
@ -41,6 +42,15 @@
|
|||||||
#include "util/misc/from_pointer_cast.h"
|
#include "util/misc/from_pointer_cast.h"
|
||||||
#include "util/posix/signals.h"
|
#include "util/posix/signals.h"
|
||||||
|
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
#include <android/set_abort_message.h>
|
||||||
|
#include "dlfcn_internal.h"
|
||||||
|
|
||||||
|
// Normally this comes from set_abort_message.h, but only at API level 21.
|
||||||
|
extern "C" void android_set_abort_message(const char* msg)
|
||||||
|
__attribute__((weak));
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
namespace {
|
namespace {
|
||||||
@ -103,10 +113,26 @@ TEST(CrashpadClient, SimulateCrash) {
|
|||||||
constexpr char kTestAnnotationName[] = "name_of_annotation";
|
constexpr char kTestAnnotationName[] = "name_of_annotation";
|
||||||
constexpr char kTestAnnotationValue[] = "value_of_annotation";
|
constexpr char kTestAnnotationValue[] = "value_of_annotation";
|
||||||
|
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
constexpr char kTestAbortMessage[] = "test abort message";
|
||||||
|
#endif
|
||||||
|
|
||||||
void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
||||||
ProcessSnapshotMinidump minidump_snapshot;
|
ProcessSnapshotMinidump minidump_snapshot;
|
||||||
ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));
|
ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));
|
||||||
|
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
// This part of the test requires Q. The API level on Q devices will be 28
|
||||||
|
// until the API is finalized, so we can't check API level yet. For now, test
|
||||||
|
// for the presence of a libc symbol which was introduced in Q.
|
||||||
|
if (crashpad::internal::Dlsym(RTLD_DEFAULT, "android_fdsan_close_with_tag")) {
|
||||||
|
const auto& annotations = minidump_snapshot.AnnotationsSimpleMap();
|
||||||
|
auto abort_message = annotations.find("abort_message");
|
||||||
|
ASSERT_NE(annotations.end(), abort_message);
|
||||||
|
EXPECT_EQ(kTestAbortMessage, abort_message->second);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
for (const ModuleSnapshot* module : minidump_snapshot.Modules()) {
|
for (const ModuleSnapshot* module : minidump_snapshot.Modules()) {
|
||||||
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
|
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
|
||||||
if (static_cast<Annotation::Type>(annotation.type) !=
|
if (static_cast<Annotation::Type>(annotation.type) !=
|
||||||
@ -153,6 +179,12 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) {
|
|||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
if (android_set_abort_message) {
|
||||||
|
android_set_abort_message(kTestAbortMessage);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
|
|
||||||
NOTREACHED();
|
NOTREACHED();
|
||||||
|
@ -107,7 +107,9 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
process_snapshot.SetClientID(client_id);
|
process_snapshot.SetClientID(client_id);
|
||||||
process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
|
for (auto& p : *process_annotations_) {
|
||||||
|
process_snapshot.AddAnnotation(p.first, p.second);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
||||||
CrashReportDatabase::OperationStatus database_status =
|
CrashReportDatabase::OperationStatus database_status =
|
||||||
|
@ -280,6 +280,81 @@ const std::vector<ProcessReaderLinux::Module>& ProcessReaderLinux::Modules() {
|
|||||||
return modules_;
|
return modules_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProcessReaderLinux::InitializeAbortMessage() {
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
const MemoryMap::Mapping* mapping =
|
||||||
|
memory_map_.FindMappingWithName("[anon:abort message]");
|
||||||
|
if (!mapping) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_64_bit_) {
|
||||||
|
ReadAbortMessage<true>(mapping);
|
||||||
|
} else {
|
||||||
|
ReadAbortMessage<false>(mapping);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
|
||||||
|
// These structure definitions and the magic numbers below were copied from
|
||||||
|
// bionic/libc/bionic/android_set_abort_message.cpp
|
||||||
|
|
||||||
|
template <bool is64Bit>
|
||||||
|
struct abort_msg_t {
|
||||||
|
uint32_t size;
|
||||||
|
char msg[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct abort_msg_t<true> {
|
||||||
|
uint64_t size;
|
||||||
|
char msg[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
template <bool is64Bit>
|
||||||
|
struct magic_abort_msg_t {
|
||||||
|
uint64_t magic1;
|
||||||
|
uint64_t magic2;
|
||||||
|
abort_msg_t<is64Bit> msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <bool is64Bit>
|
||||||
|
void ProcessReaderLinux::ReadAbortMessage(const MemoryMap::Mapping* mapping) {
|
||||||
|
magic_abort_msg_t<is64Bit> header;
|
||||||
|
if (!Memory()->Read(
|
||||||
|
mapping->range.Base(), sizeof(magic_abort_msg_t<is64Bit>), &header)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = header.msg.size - sizeof(magic_abort_msg_t<is64Bit>) - 1;
|
||||||
|
if (header.magic1 != 0xb18e40886ac388f0ULL ||
|
||||||
|
header.magic2 != 0xc6dfba755a1de0b5ULL ||
|
||||||
|
mapping->range.Size() <
|
||||||
|
offsetof(magic_abort_msg_t<is64Bit>, msg.msg) + size) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
abort_message_.resize(size);
|
||||||
|
if (!Memory()->Read(
|
||||||
|
mapping->range.Base() + offsetof(magic_abort_msg_t<is64Bit>, msg.msg),
|
||||||
|
size,
|
||||||
|
&abort_message_[0])) {
|
||||||
|
abort_message_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // OS_ANDROID
|
||||||
|
|
||||||
|
const std::string& ProcessReaderLinux::AbortMessage() {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
if (abort_message_.empty()) {
|
||||||
|
InitializeAbortMessage();
|
||||||
|
}
|
||||||
|
return abort_message_;
|
||||||
|
}
|
||||||
|
|
||||||
void ProcessReaderLinux::InitializeThreads() {
|
void ProcessReaderLinux::InitializeThreads() {
|
||||||
DCHECK(threads_.empty());
|
DCHECK(threads_.empty());
|
||||||
initialized_threads_ = true;
|
initialized_threads_ = true;
|
||||||
|
@ -153,15 +153,23 @@ class ProcessReaderLinux {
|
|||||||
//! `0`) corresponds to the main executable.
|
//! `0`) corresponds to the main executable.
|
||||||
const std::vector<Module>& Modules();
|
const std::vector<Module>& Modules();
|
||||||
|
|
||||||
|
//! \return On Android, the abort message that was passed to
|
||||||
|
//! android_set_abort_message(). This is only available on Q or later.
|
||||||
|
const std::string& AbortMessage();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InitializeThreads();
|
void InitializeThreads();
|
||||||
void InitializeModules();
|
void InitializeModules();
|
||||||
|
void InitializeAbortMessage();
|
||||||
|
template <bool Is64Bit>
|
||||||
|
void ReadAbortMessage(const MemoryMap::Mapping* mapping);
|
||||||
|
|
||||||
PtraceConnection* connection_; // weak
|
PtraceConnection* connection_; // weak
|
||||||
ProcessInfo process_info_;
|
ProcessInfo process_info_;
|
||||||
MemoryMap memory_map_;
|
MemoryMap memory_map_;
|
||||||
std::vector<Thread> threads_;
|
std::vector<Thread> threads_;
|
||||||
std::vector<Module> modules_;
|
std::vector<Module> modules_;
|
||||||
|
std::string abort_message_;
|
||||||
std::vector<std::unique_ptr<ElfImageReader>> elf_readers_;
|
std::vector<std::unique_ptr<ElfImageReader>> elf_readers_;
|
||||||
bool is_64_bit_;
|
bool is_64_bit_;
|
||||||
bool initialized_threads_;
|
bool initialized_threads_;
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "test/errors.h"
|
#include "test/errors.h"
|
||||||
|
#include "test/gtest_disabled.h"
|
||||||
#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"
|
||||||
@ -53,6 +54,12 @@
|
|||||||
|
|
||||||
#if defined(OS_ANDROID)
|
#if defined(OS_ANDROID)
|
||||||
#include <android/api-level.h>
|
#include <android/api-level.h>
|
||||||
|
#include <android/set_abort_message.h>
|
||||||
|
#include "dlfcn_internal.h"
|
||||||
|
|
||||||
|
// Normally this comes from set_abort_message.h, but only at API level 21.
|
||||||
|
extern "C" void android_set_abort_message(const char* msg)
|
||||||
|
__attribute__((weak));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -86,6 +93,8 @@ TEST(ProcessReaderLinux, SelfBasic) {
|
|||||||
sizeof(kTestMemory),
|
sizeof(kTestMemory),
|
||||||
&buffer));
|
&buffer));
|
||||||
EXPECT_STREQ(kTestMemory, buffer);
|
EXPECT_STREQ(kTestMemory, buffer);
|
||||||
|
|
||||||
|
EXPECT_EQ("", process_reader.AbortMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char kTestMemory[] = "Read me from another process";
|
constexpr char kTestMemory[] = "Read me from another process";
|
||||||
@ -778,6 +787,30 @@ TEST(ProcessReaderLinux, ChildModules) {
|
|||||||
test.Run();
|
test.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
const char kTestAbortMessage[] = "test abort message";
|
||||||
|
|
||||||
|
TEST(ProcessReaderLinux, AbortMessage) {
|
||||||
|
// This test requires Q. The API level on Q devices will be 28 until the API
|
||||||
|
// is finalized, so we can't check API level yet. For now, test for the
|
||||||
|
// presence of a libc symbol which was introduced in Q.
|
||||||
|
if (!crashpad::internal::Dlsym(RTLD_DEFAULT,
|
||||||
|
"android_fdsan_close_with_tag")) {
|
||||||
|
DISABLED_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
android_set_abort_message(kTestAbortMessage);
|
||||||
|
|
||||||
|
FakePtraceConnection connection;
|
||||||
|
connection.Initialize(getpid());
|
||||||
|
|
||||||
|
ProcessReaderLinux process_reader;
|
||||||
|
ASSERT_TRUE(process_reader.Initialize(&connection));
|
||||||
|
|
||||||
|
EXPECT_EQ(kTestAbortMessage, process_reader.AbortMessage());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -43,6 +43,7 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) {
|
|||||||
|
|
||||||
InitializeThreads();
|
InitializeThreads();
|
||||||
InitializeModules();
|
InitializeModules();
|
||||||
|
InitializeAnnotations();
|
||||||
|
|
||||||
INITIALIZATION_STATE_SET_VALID(initialized_);
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
||||||
return true;
|
return true;
|
||||||
@ -265,4 +266,13 @@ void ProcessSnapshotLinux::InitializeModules() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProcessSnapshotLinux::InitializeAnnotations() {
|
||||||
|
#if defined(OS_ANDROID)
|
||||||
|
const std::string& abort_message = process_reader_.AbortMessage();
|
||||||
|
if (!abort_message.empty()) {
|
||||||
|
annotations_simple_map_["abort_message"] = abort_message;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -78,15 +78,16 @@ class ProcessSnapshotLinux final : public ProcessSnapshot {
|
|||||||
//! ClientID() will return an identifier consisting entirely of zeroes.
|
//! ClientID() will return an identifier consisting entirely of zeroes.
|
||||||
void SetClientID(const UUID& client_id) { client_id_ = client_id; }
|
void SetClientID(const UUID& client_id) { client_id_ = client_id; }
|
||||||
|
|
||||||
//! \brief Sets the value to be returned by AnnotationsSimpleMap().
|
//! \brief Add an annotation to be returned by AnnotationsSimpleMap().
|
||||||
//!
|
//!
|
||||||
//! All process annotations are under the control of the snapshot
|
//! Most process annotations are under the control of the snapshot
|
||||||
//! producer, which may call this method to establish these annotations.
|
//! producer, which may call this method to establish these annotations.
|
||||||
//! Contrast this with module annotations, which are under the control of the
|
//! On Android Q or later, the process snapshot may add an "abort_message"
|
||||||
//! process being snapshotted.
|
//! annotation, which will contain the abort message passed to the
|
||||||
void SetAnnotationsSimpleMap(
|
//! android_set_abort_message() function. Contrast this with module
|
||||||
const std::map<std::string, std::string>& annotations_simple_map) {
|
//! annotations, which are under the control of the process being snapshotted.
|
||||||
annotations_simple_map_ = annotations_simple_map;
|
void AddAnnotation(const std::string& key, const std::string& value) {
|
||||||
|
annotations_simple_map_[key] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! \brief Returns options from CrashpadInfo structures found in modules in
|
//! \brief Returns options from CrashpadInfo structures found in modules in
|
||||||
@ -120,6 +121,7 @@ class ProcessSnapshotLinux final : public ProcessSnapshot {
|
|||||||
private:
|
private:
|
||||||
void InitializeThreads();
|
void InitializeThreads();
|
||||||
void InitializeModules();
|
void InitializeModules();
|
||||||
|
void InitializeAnnotations();
|
||||||
|
|
||||||
std::map<std::string, std::string> annotations_simple_map_;
|
std::map<std::string, std::string> annotations_simple_map_;
|
||||||
timeval snapshot_time_;
|
timeval snapshot_time_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user