diff --git a/client/crashpad_client_linux_test.cc b/client/crashpad_client_linux_test.cc index 1f80d25f..f61ea44f 100644 --- a/client/crashpad_client_linux_test.cc +++ b/client/crashpad_client_linux_test.cc @@ -14,6 +14,7 @@ #include "client/crashpad_client.h" +#include #include #include #include @@ -41,6 +42,15 @@ #include "util/misc/from_pointer_cast.h" #include "util/posix/signals.h" +#if defined(OS_ANDROID) +#include +#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 test { namespace { @@ -103,10 +113,26 @@ TEST(CrashpadClient, SimulateCrash) { constexpr char kTestAnnotationName[] = "name_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) { ProcessSnapshotMinidump minidump_snapshot; 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 AnnotationSnapshot& annotation : module->AnnotationObjects()) { if (static_cast(annotation.type) != @@ -153,6 +179,12 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerAtCrashChild) { return EXIT_FAILURE; } +#if defined(OS_ANDROID) + if (android_set_abort_message) { + android_set_abort_message(kTestAbortMessage); + } +#endif + __builtin_trap(); NOTREACHED(); diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index 71a7009b..910b2884 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -107,7 +107,9 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( } 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 new_report; CrashReportDatabase::OperationStatus database_status = diff --git a/snapshot/linux/process_reader_linux.cc b/snapshot/linux/process_reader_linux.cc index c1d6c40f..b96abfe7 100644 --- a/snapshot/linux/process_reader_linux.cc +++ b/snapshot/linux/process_reader_linux.cc @@ -280,6 +280,81 @@ const std::vector& ProcessReaderLinux::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(mapping); + } else { + ReadAbortMessage(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 +struct abort_msg_t { + uint32_t size; + char msg[0]; +}; + +template <> +struct abort_msg_t { + uint64_t size; + char msg[0]; +}; + +template +struct magic_abort_msg_t { + uint64_t magic1; + uint64_t magic2; + abort_msg_t msg; +}; + +template +void ProcessReaderLinux::ReadAbortMessage(const MemoryMap::Mapping* mapping) { + magic_abort_msg_t header; + if (!Memory()->Read( + mapping->range.Base(), sizeof(magic_abort_msg_t), &header)) { + return; + } + + size_t size = header.msg.size - sizeof(magic_abort_msg_t) - 1; + if (header.magic1 != 0xb18e40886ac388f0ULL || + header.magic2 != 0xc6dfba755a1de0b5ULL || + mapping->range.Size() < + offsetof(magic_abort_msg_t, msg.msg) + size) { + return; + } + + abort_message_.resize(size); + if (!Memory()->Read( + mapping->range.Base() + offsetof(magic_abort_msg_t, 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() { DCHECK(threads_.empty()); initialized_threads_ = true; diff --git a/snapshot/linux/process_reader_linux.h b/snapshot/linux/process_reader_linux.h index 1b30f53e..258e102a 100644 --- a/snapshot/linux/process_reader_linux.h +++ b/snapshot/linux/process_reader_linux.h @@ -153,15 +153,23 @@ class ProcessReaderLinux { //! `0`) corresponds to the main executable. const std::vector& 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: void InitializeThreads(); void InitializeModules(); + void InitializeAbortMessage(); + template + void ReadAbortMessage(const MemoryMap::Mapping* mapping); PtraceConnection* connection_; // weak ProcessInfo process_info_; MemoryMap memory_map_; std::vector threads_; std::vector modules_; + std::string abort_message_; std::vector> elf_readers_; bool is_64_bit_; bool initialized_threads_; diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc index e2e5566c..e25b8023 100644 --- a/snapshot/linux/process_reader_linux_test.cc +++ b/snapshot/linux/process_reader_linux_test.cc @@ -38,6 +38,7 @@ #include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" +#include "test/gtest_disabled.h" #include "test/linux/fake_ptrace_connection.h" #include "test/linux/get_tls.h" #include "test/multiprocess.h" @@ -53,6 +54,12 @@ #if defined(OS_ANDROID) #include +#include +#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 { @@ -86,6 +93,8 @@ TEST(ProcessReaderLinux, SelfBasic) { sizeof(kTestMemory), &buffer)); EXPECT_STREQ(kTestMemory, buffer); + + EXPECT_EQ("", process_reader.AbortMessage()); } constexpr char kTestMemory[] = "Read me from another process"; @@ -778,6 +787,30 @@ TEST(ProcessReaderLinux, ChildModules) { 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 test } // namespace crashpad diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index 041653a3..4141b2f4 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -43,6 +43,7 @@ bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { InitializeThreads(); InitializeModules(); + InitializeAnnotations(); INITIALIZATION_STATE_SET_VALID(initialized_); 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 diff --git a/snapshot/linux/process_snapshot_linux.h b/snapshot/linux/process_snapshot_linux.h index 236926d6..84bd226a 100644 --- a/snapshot/linux/process_snapshot_linux.h +++ b/snapshot/linux/process_snapshot_linux.h @@ -78,15 +78,16 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { //! ClientID() will return an identifier consisting entirely of zeroes. 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. - //! Contrast this with module annotations, which are under the control of the - //! process being snapshotted. - void SetAnnotationsSimpleMap( - const std::map& annotations_simple_map) { - annotations_simple_map_ = annotations_simple_map; + //! On Android Q or later, the process snapshot may add an "abort_message" + //! annotation, which will contain the abort message passed to the + //! android_set_abort_message() function. Contrast this with module + //! annotations, which are under the control of the process being snapshotted. + 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 @@ -120,6 +121,7 @@ class ProcessSnapshotLinux final : public ProcessSnapshot { private: void InitializeThreads(); void InitializeModules(); + void InitializeAnnotations(); std::map annotations_simple_map_; timeval snapshot_time_;