Merge changes from topic "berberis-crash-reporting-guest_regs" into main
* changes:
[Berberis][CrashReporting] Dump guest thread info to tombstone file
[Berberis][CrashReporting] Extend ThreadInfo to have guest registers
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 3a882ea..0c5543e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -452,6 +452,7 @@
header_libs: [
"bionic_libc_platform_headers",
+ "libnative_bridge_support_accessor_headers",
],
static_libs: [
@@ -461,6 +462,8 @@
"libtombstone_proto",
"libprotobuf-cpp-lite",
+
+ "libnative_bridge_guest_state_accessor",
],
shared_libs: [
@@ -476,6 +479,15 @@
// Required for tests.
required: ["crash_dump.policy"],
+
+ target: {
+ android: {
+ header_libs: [
+ "libnative_bridge_support_accessor_headers", // For dlext_namespaces.h
+ ],
+ shared_libs: ["libdl_android"], // For android_get_exported_namespace implementation
+ },
+ },
}
cc_binary {
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 203b485..4640aeb 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -25,10 +25,12 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <cstdint>
#include <limits>
#include <map>
#include <memory>
#include <set>
+#include <string>
#include <vector>
#include <android-base/errno_restorer.h>
@@ -42,6 +44,7 @@
#include <android-base/unique_fd.h>
#include <bionic/macros.h>
#include <bionic/reserved_signals.h>
+#include <bionic/tls_defines.h>
#include <cutils/sockets.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -52,7 +55,18 @@
#include <unwindstack/AndroidUnwinder.h>
#include <unwindstack/Error.h>
+#include <unwindstack/MachineArm.h>
+#include <unwindstack/MachineArm64.h>
+#include <unwindstack/MachineRiscv64.h>
#include <unwindstack/Regs.h>
+#include <unwindstack/RegsArm.h>
+#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsRiscv64.h>
+#include <unwindstack/UserArm.h>
+#include <unwindstack/UserArm64.h>
+#include <unwindstack/UserRiscv64.h>
+
+#include <native_bridge_support/guest_state_accessor/accessor.h>
#include "libdebuggerd/backtrace.h"
#include "libdebuggerd/tombstone.h"
@@ -68,6 +82,10 @@
using android::base::StringPrintf;
using android::base::unique_fd;
+// This stores guest architecture. When the architecture is supported, tombstone file will output
+// guest state information.
+static Architecture g_guest_arch;
+
static bool pid_contains_tid(int pid_proc_fd, pid_t tid) {
struct stat st;
std::string task_path = StringPrintf("task/%d", tid);
@@ -403,6 +421,113 @@
sigaction(SIGPIPE, &action, nullptr);
}
+static bool PtracePeek(int request, pid_t tid, uintptr_t addr, void* data, std::string err_msg,
+ uintptr_t* result) {
+ errno = 0;
+ *result = ptrace(request, tid, addr, data);
+ if (errno != 0) {
+ PLOG(ERROR) << err_msg;
+ return false;
+ }
+ return true;
+}
+
+static bool GetGuestRegistersFromCrashedProcess([[maybe_unused]] pid_t tid,
+ NativeBridgeGuestRegs* guest_regs) {
+ auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(tid);
+
+ uintptr_t header_ptr = 0;
+ uintptr_t base = 0;
+#if defined(__x86_64__)
+ if (!PtracePeek(PTRACE_PEEKUSER, tid, offsetof(user_regs_struct, fs_base), nullptr,
+ "failed to read thread register for thread " + std::to_string(tid), &base)) {
+ return false;
+ }
+#elif defined(__aarch64__)
+ // base is implicitly casted to uint64_t.
+ struct iovec pt_iov {
+ .iov_base = &base, .iov_len = sizeof(base),
+ };
+
+ if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) != 0) {
+ PLOG(ERROR) << "failed to read thread register for thread " << tid;
+ }
+#else
+ // TODO(b/339287219): Add case for Riscv host.
+ return false;
+#endif
+ auto ptr_to_guest_slot = base + TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE * sizeof(uintptr_t);
+ if (!process_memory->ReadFully(ptr_to_guest_slot, &header_ptr, sizeof(uintptr_t))) {
+ PLOG(ERROR) << "failed to get guest state TLS slot content for thread " << tid;
+ return false;
+ }
+
+ NativeBridgeGuestStateHeader header;
+ if (!process_memory->ReadFully(header_ptr, &header, sizeof(NativeBridgeGuestStateHeader))) {
+ PLOG(ERROR) << "failed to get the guest state header for thread " << tid;
+ return false;
+ }
+ if (header.signature != NATIVE_BRIDGE_GUEST_STATE_SIGNATURE) {
+ // Return when ptr points to unmapped memory or no valid guest state.
+ return false;
+ }
+ auto guest_state_data_copy = std::make_unique<unsigned char[]>(header.guest_state_data_size);
+ if (!process_memory->ReadFully(reinterpret_cast<uintptr_t>(header.guest_state_data),
+ guest_state_data_copy.get(), header.guest_state_data_size)) {
+ PLOG(ERROR) << "failed to read the guest state data for thread " << tid;
+ return false;
+ }
+
+ LoadGuestStateRegisters(guest_state_data_copy.get(), header.guest_state_data_size, guest_regs);
+ return true;
+}
+
+static void ReadGuestRegisters(std::unique_ptr<unwindstack::Regs>* regs, pid_t tid) {
+ NativeBridgeGuestRegs guest_regs;
+ if (!GetGuestRegistersFromCrashedProcess(tid, &guest_regs)) {
+ return;
+ }
+
+ switch (guest_regs.guest_arch) {
+ case NATIVE_BRIDGE_ARCH_ARM: {
+ unwindstack::arm_user_regs arm_user_regs = {};
+ for (size_t i = 0; i < unwindstack::ARM_REG_LAST; i++) {
+ arm_user_regs.regs[i] = guest_regs.regs_arm.r[i];
+ }
+ regs->reset(unwindstack::RegsArm::Read(&arm_user_regs));
+
+ g_guest_arch = Architecture::ARM32;
+ break;
+ }
+#if defined(__LP64__)
+ case NATIVE_BRIDGE_ARCH_ARM64: {
+ unwindstack::arm64_user_regs arm64_user_regs = {};
+ for (size_t i = 0; i < unwindstack::ARM64_REG_R31; i++) {
+ arm64_user_regs.regs[i] = guest_regs.regs_arm64.x[i];
+ }
+ arm64_user_regs.sp = guest_regs.regs_arm64.sp;
+ arm64_user_regs.pc = guest_regs.regs_arm64.ip;
+ regs->reset(unwindstack::RegsArm64::Read(&arm64_user_regs));
+
+ g_guest_arch = Architecture::ARM64;
+ break;
+ }
+ case NATIVE_BRIDGE_ARCH_RISCV64: {
+ unwindstack::riscv64_user_regs riscv64_user_regs = {};
+ // RISCV64_REG_PC is at the first position.
+ riscv64_user_regs.regs[0] = guest_regs.regs_riscv64.ip;
+ for (size_t i = 1; i < unwindstack::RISCV64_REG_REAL_COUNT; i++) {
+ riscv64_user_regs.regs[i] = guest_regs.regs_riscv64.x[i];
+ }
+ regs->reset(unwindstack::RegsRiscv64::Read(&riscv64_user_regs, tid));
+
+ g_guest_arch = Architecture::RISCV64;
+ break;
+ }
+#endif
+ }
+}
+
int main(int argc, char** argv) {
DefuseSignalHandlers();
InstallSigPipeHandler();
@@ -537,6 +662,7 @@
}
if (thread == g_target_thread) {
+ ReadGuestRegisters(&info.guest_registers, thread);
// Read the thread's registers along with the rest of the crash info out of the pipe.
ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, &recoverable_crash);
info.siginfo = &siginfo;
@@ -660,8 +786,32 @@
{
ATRACE_NAME("engrave_tombstone");
- engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,
- g_target_thread, process_info, &open_files, &amfd_data);
+ unwindstack::ArchEnum regs_arch = unwindstack::ARCH_UNKNOWN;
+ switch (g_guest_arch) {
+ case Architecture::ARM32: {
+ regs_arch = unwindstack::ARCH_ARM;
+ break;
+ }
+ case Architecture::ARM64: {
+ regs_arch = unwindstack::ARCH_ARM64;
+ break;
+ }
+ case Architecture::RISCV64: {
+ regs_arch = unwindstack::ARCH_RISCV64;
+ break;
+ }
+ default: {
+ }
+ }
+ if (regs_arch == unwindstack::ARCH_UNKNOWN) {
+ engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,
+ g_target_thread, process_info, &open_files, &amfd_data);
+ } else {
+ unwindstack::AndroidRemoteUnwinder guest_unwinder(vm_pid, regs_arch);
+ engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,
+ g_target_thread, process_info, &open_files, &amfd_data, &g_guest_arch,
+ &guest_unwinder);
+ }
}
}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
index be999e0..6371dbe 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h
@@ -28,6 +28,7 @@
#include <android-base/unique_fd.h>
#include "open_files_list.h"
+#include "tombstone.pb.h"
#include "types.h"
// Forward declarations
@@ -54,14 +55,17 @@
unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
- std::string* amfd_data);
+ std::string* amfd_data, Architecture* guest_arch = nullptr,
+ unwindstack::AndroidUnwinder* guest_unwinder = nullptr);
void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,
siginfo_t* siginfo, ucontext_t* ucontext);
void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
- const ProcessInfo& process_info, const OpenFilesList* open_files);
+ const ProcessInfo& process_info, const OpenFilesList* open_files,
+ Architecture* guest_arch,
+ unwindstack::AndroidUnwinder* guest_unwinder);
bool tombstone_proto_to_text(
const Tombstone& tombstone,
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
index 4d69658..c799f24 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h
@@ -39,6 +39,8 @@
int signo = 0;
siginfo_t* siginfo = nullptr;
+
+ std::unique_ptr<unwindstack::Regs> guest_registers;
};
// This struct is written into a pipe from inside the crashing process.
diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp
index 5a416d6..0da2ca7 100644
--- a/debuggerd/libdebuggerd/tombstone.cpp
+++ b/debuggerd/libdebuggerd/tombstone.cpp
@@ -125,10 +125,12 @@
unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,
const ProcessInfo& process_info, OpenFilesList* open_files,
- std::string* amfd_data) {
+ std::string* amfd_data, Architecture* guest_arch,
+ unwindstack::AndroidUnwinder* guest_unwinder) {
// Don't copy log messages to tombstone unless this is a development device.
Tombstone tombstone;
- engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files);
+ engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files,
+ guest_arch, guest_unwinder);
if (proto_fd != -1) {
if (!tombstone.SerializeToFileDescriptor(proto_fd.get())) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp
index 5546b7b..5e170df 100644
--- a/debuggerd/libdebuggerd/tombstone_proto.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto.cpp
@@ -482,7 +482,8 @@
}
static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
- const ThreadInfo& thread_info, bool memory_dump = false) {
+ const ThreadInfo& thread_info, bool memory_dump = false,
+ unwindstack::AndroidUnwinder* guest_unwinder = nullptr) {
Thread thread;
thread.set_id(thread_info.tid);
@@ -509,6 +510,26 @@
auto& threads = *tombstone->mutable_threads();
threads[thread_info.tid] = thread;
+
+ if (guest_unwinder) {
+ if (!thread_info.guest_registers) {
+ async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+ "No guest state registers information for tid %d", thread_info.tid);
+ return;
+ }
+ Thread guest_thread;
+ unwindstack::AndroidUnwinderData guest_data;
+ guest_data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();
+ if (guest_unwinder->Unwind(thread_info.guest_registers.get(), guest_data)) {
+ dump_thread_backtrace(guest_data.frames, guest_thread);
+ } else {
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Unwind guest state registers failed for tid %d: Error %s",
+ thread_info.tid, guest_data.GetErrorString().c_str());
+ }
+ dump_registers(guest_unwinder, *guest_data.saved_initial_regs, guest_thread, memory_dump);
+ *tombstone->mutable_guest_thread() = guest_thread;
+ }
}
static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps,
@@ -686,10 +707,17 @@
void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,
const std::map<pid_t, ThreadInfo>& threads, pid_t target_tid,
- const ProcessInfo& process_info, const OpenFilesList* open_files) {
+ const ProcessInfo& process_info, const OpenFilesList* open_files,
+ Architecture* guest_arch,
+ unwindstack::AndroidUnwinder* guest_unwinder) {
Tombstone result;
result.set_arch(get_arch());
+ if (guest_arch != nullptr) {
+ result.set_guest_arch(*guest_arch);
+ } else {
+ result.set_guest_arch(Architecture::NONE);
+ }
result.set_build_fingerprint(android::base::GetProperty("ro.build.fingerprint", "unknown"));
result.set_revision(android::base::GetProperty("ro.revision", "unknown"));
result.set_timestamp(get_timestamp());
@@ -750,7 +778,7 @@
dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);
dump_crash_details(&result, unwinder->GetProcessMemory(), process_info);
// Dump the target thread, but save the memory around the registers.
- dump_thread(&result, unwinder, target_thread, /* memory_dump */ true);
+ dump_thread(&result, unwinder, target_thread, /* memory_dump */ true, guest_unwinder);
for (const auto& [tid, thread_info] : threads) {
if (tid != target_tid) {
diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
index 08c1cc0..7b29692 100644
--- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
+++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp
@@ -79,8 +79,8 @@
return describe_end(value, desc);
}
-static const char* abi_string(const Tombstone& tombstone) {
- switch (tombstone.arch()) {
+static const char* abi_string(const Architecture& arch) {
+ switch (arch) {
case Architecture::ARM32:
return "arm";
case Architecture::ARM64:
@@ -578,11 +578,37 @@
}
}
+static void print_guest_thread(CallbackType callback, const Tombstone& tombstone,
+ const Thread& guest_thread) {
+ CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---");
+ CBS("Guest thread information");
+ print_thread_registers(callback, tombstone, guest_thread, true);
+
+ CBS("");
+ CB(true, "%d total frames", guest_thread.current_backtrace().size());
+ CB(true, "backtrace:");
+ print_backtrace(callback, tombstone, guest_thread.current_backtrace(), true);
+
+ print_thread_memory_dump(callback, tombstone, guest_thread);
+
+ CBS("");
+
+ // No memory maps to print.
+ if (!tombstone.memory_mappings().empty()) {
+ print_memory_maps(callback, tombstone);
+ } else {
+ CBS("No memory maps found");
+ }
+}
+
bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) {
CBL("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***");
CBL("Build fingerprint: '%s'", tombstone.build_fingerprint().c_str());
CBL("Revision: '%s'", tombstone.revision().c_str());
- CBL("ABI: '%s'", abi_string(tombstone));
+ CBL("ABI: '%s'", abi_string(tombstone.arch()));
+ if (tombstone.guest_arch() != Architecture::NONE) {
+ CBL("Guest architecture: '%s'", abi_string(tombstone.guest_arch()));
+ }
CBL("Timestamp: %s", tombstone.timestamp().c_str());
CBL("Process uptime: %ds", tombstone.process_uptime());
@@ -635,5 +661,10 @@
print_logs(callback, tombstone, 0);
+ // Process guest thread
+ if (tombstone.has_guest_thread()) {
+ print_guest_thread(callback, tombstone, tombstone.guest_thread());
+ }
+
return true;
}
diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto
index e70d525..4ddc54f 100644
--- a/debuggerd/proto/tombstone.proto
+++ b/debuggerd/proto/tombstone.proto
@@ -22,6 +22,7 @@
message Tombstone {
Architecture arch = 1;
+ Architecture guest_arch = 24;
string build_fingerprint = 2;
string revision = 3;
string timestamp = 4;
@@ -42,6 +43,7 @@
repeated Cause causes = 15;
map<uint32, Thread> threads = 16;
+ Thread guest_thread = 25;
repeated MemoryMapping memory_mappings = 17;
repeated LogBuffer log_buffers = 18;
repeated FD open_fds = 19;
@@ -49,7 +51,7 @@
uint32 page_size = 22;
bool has_been_16kb_mode = 23;
- reserved 24 to 999;
+ reserved 26 to 999;
}
enum Architecture {
@@ -58,8 +60,9 @@
X86 = 2;
X86_64 = 3;
RISCV64 = 4;
+ NONE = 5;
- reserved 5 to 999;
+ reserved 6 to 999;
}
message Signal {