Loading debuggerd/Android.bp +21 −0 Original line number Diff line number Diff line Loading @@ -176,6 +176,8 @@ cc_library_static { "libdebuggerd/open_files_list.cpp", "libdebuggerd/scudo.cpp", "libdebuggerd/tombstone.cpp", "libdebuggerd/tombstone_proto.cpp", "libdebuggerd/tombstone_proto_to_text.cpp", "libdebuggerd/utility.cpp", ], Loading Loading @@ -206,6 +208,8 @@ cc_library_static { whole_static_libs: [ "gwp_asan_crash_handler", "libscudo", "libtombstone_proto", "libprotobuf-cpp-lite", ], target: { Loading @@ -228,6 +232,20 @@ cc_library_static { }, } cc_binary { name: "pbtombstone", defaults: ["debuggerd_defaults"], srcs: ["pbtombstone.cpp"], static_libs: [ "libbase", "libdebuggerd", "liblog", "libprotobuf-cpp-lite", "libtombstone_proto", "libunwindstack", ], } cc_test { name: "debuggerd_test", defaults: ["debuggerd_defaults"], Loading Loading @@ -332,6 +350,9 @@ cc_binary { "libtombstoned_client_static", "libdebuggerd", "libcutils", "libtombstone_proto", "libprotobuf-cpp-lite", ], shared_libs: [ Loading debuggerd/common/include/dump_type.h +5 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,8 @@ enum DebuggerdDumpType : uint8_t { kDebuggerdNativeBacktrace, kDebuggerdTombstone, kDebuggerdJavaBacktrace, kDebuggerdAnyIntercept kDebuggerdAnyIntercept, kDebuggerdTombstoneProto, }; inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) { Loading @@ -41,6 +42,9 @@ inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& r case kDebuggerdAnyIntercept: stream << "kDebuggerdAnyIntercept"; break; case kDebuggerdTombstoneProto: stream << "kDebuggerdTombstoneProto"; break; default: stream << "[unknown]"; } Loading debuggerd/crash_dump.cpp +22 −6 Original line number Diff line number Diff line Loading @@ -195,6 +195,7 @@ static pid_t g_target_thread = -1; static bool g_tombstoned_connected = false; static unique_fd g_tombstoned_socket; static unique_fd g_output_fd; static unique_fd g_proto_fd; static void DefuseSignalHandlers() { // Don't try to dump ourselves. Loading @@ -215,7 +216,7 @@ static void Initialize(char** argv) { // If we abort before we get an output fd, contact tombstoned to let any // potential listeners know that we failed. if (!g_tombstoned_connected) { if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, &g_proto_fd, kDebuggerdAnyIntercept)) { // We failed to connect, not much we can do. LOG(ERROR) << "failed to connected to tombstoned to report failure"; Loading Loading @@ -248,10 +249,20 @@ static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdD } int dump_type_int; if (!android::base::ParseInt(argv[3], &dump_type_int, 0, 1)) { if (!android::base::ParseInt(argv[3], &dump_type_int, 0)) { LOG(FATAL) << "invalid requested dump type: " << argv[3]; } *dump_type = static_cast<DebuggerdDumpType>(dump_type_int); switch (*dump_type) { case kDebuggerdNativeBacktrace: case kDebuggerdTombstone: case kDebuggerdTombstoneProto: break; default: LOG(FATAL) << "invalid requested dump type: " << dump_type_int; } } static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, Loading Loading @@ -480,6 +491,11 @@ int main(int argc, char** argv) { info.process_name = process_name; info.thread_name = get_thread_name(thread); unique_fd attr_fd(openat(target_proc_fd, "attr/current", O_RDONLY | O_CLOEXEC)); if (!android::base::ReadFdToString(attr_fd, &info.selinux_label)) { PLOG(WARNING) << "failed to read selinux label"; } if (!ptrace_interrupt(thread, &info.signo)) { PLOG(WARNING) << "failed to ptrace interrupt thread " << thread; ptrace(PTRACE_DETACH, thread, 0, 0); Loading Loading @@ -558,8 +574,8 @@ int main(int argc, char** argv) { { ATRACE_NAME("tombstoned_connect"); LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type; g_tombstoned_connected = tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, dump_type); g_tombstoned_connected = tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, &g_proto_fd, dump_type); } if (g_tombstoned_connected) { Loading Loading @@ -612,8 +628,8 @@ int main(int argc, char** argv) { { ATRACE_NAME("engrave_tombstone"); engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread, process_info, &open_files, &amfd_data); engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info, g_target_thread, process_info, &open_files, &amfd_data); } } Loading debuggerd/debuggerd_test.cpp +69 −2 Original line number Diff line number Diff line Loading @@ -1309,11 +1309,11 @@ TEST(tombstoned, java_trace_intercept_smoke) { tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace); ASSERT_EQ(InterceptStatus::kRegistered, status); // First connect to tombstoned requesting a native backtrace. This // First connect to tombstoned requesting a native tombstone. This // should result in a "regular" FD and not the installed intercept. const char native[] = "native"; unique_fd tombstoned_socket, input_fd; ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace)); ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdTombstone)); ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native))); tombstoned_notify_completion(tombstoned_socket.get()); Loading Loading @@ -1425,3 +1425,70 @@ TEST_F(CrasherTest, stack_overflow) { ConsumeFd(std::move(output_fd), &result); ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)"); } TEST(tombstoned, proto) { const pid_t self = getpid(); unique_fd tombstoned_socket, text_fd, proto_fd; ASSERT_TRUE( tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto)); tombstoned_notify_completion(tombstoned_socket.get()); ASSERT_NE(-1, text_fd.get()); ASSERT_NE(-1, proto_fd.get()); struct stat text_st; ASSERT_EQ(0, fstat(text_fd.get(), &text_st)); // Give tombstoned some time to link the files into place. std::this_thread::sleep_for(100ms); // Find the tombstone. std::optional<int> tombstone_index; for (int i = 0; i < 50; ++i) { std::string path = android::base::StringPrintf("/data/tombstones/tombstone_%02d", i); struct stat st; if (TEMP_FAILURE_RETRY(stat(path.c_str(), &st)) != 0) { continue; } if (st.st_dev == text_st.st_dev && st.st_ino == text_st.st_ino) { tombstone_index = i; break; } } ASSERT_TRUE(tombstone_index); std::string proto_path = android::base::StringPrintf("/data/tombstones/tombstone_%02d.pb", *tombstone_index); struct stat proto_fd_st; struct stat proto_file_st; ASSERT_EQ(0, fstat(proto_fd.get(), &proto_fd_st)); ASSERT_EQ(0, stat(proto_path.c_str(), &proto_file_st)); ASSERT_EQ(proto_fd_st.st_dev, proto_file_st.st_dev); ASSERT_EQ(proto_fd_st.st_ino, proto_file_st.st_ino); } TEST(tombstoned, proto_intercept) { const pid_t self = getpid(); unique_fd intercept_fd, output_fd; InterceptStatus status; tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); ASSERT_EQ(InterceptStatus::kRegistered, status); unique_fd tombstoned_socket, text_fd, proto_fd; ASSERT_TRUE( tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto)); ASSERT_TRUE(android::base::WriteStringToFd("foo", text_fd.get())); tombstoned_notify_completion(tombstoned_socket.get()); text_fd.reset(); std::string output; ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output)); ASSERT_EQ("foo", output); } debuggerd/handler/debuggerd_fallback.cpp +10 −9 Original line number Diff line number Diff line Loading @@ -92,15 +92,15 @@ static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { __linker_disable_fallback_allocator(); } static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) { static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) { if (!__linker_enable_fallback_allocator()) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use"); return; } engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo, ucontext); engrave_tombstone_ucontext(output_fd, proto_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo, ucontext); __linker_disable_fallback_allocator(); } Loading Loading @@ -232,7 +232,8 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { // Fetch output fd from tombstoned. unique_fd tombstone_socket, output_fd; if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) { if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr, kDebuggerdNativeBacktrace)) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "missing crash_dump_fallback() in selinux policy?"); goto exit; Loading Loading @@ -325,10 +326,10 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes _exit(1); } unique_fd tombstone_socket, output_fd; bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone); debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message); unique_fd tombstone_socket, output_fd, proto_fd; bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd, kDebuggerdTombstoneProto); debuggerd_fallback_tombstone(output_fd.get(), proto_fd.get(), ucontext, info, abort_message); if (tombstoned_connected) { tombstoned_notify_completion(tombstone_socket.get()); } Loading Loading
debuggerd/Android.bp +21 −0 Original line number Diff line number Diff line Loading @@ -176,6 +176,8 @@ cc_library_static { "libdebuggerd/open_files_list.cpp", "libdebuggerd/scudo.cpp", "libdebuggerd/tombstone.cpp", "libdebuggerd/tombstone_proto.cpp", "libdebuggerd/tombstone_proto_to_text.cpp", "libdebuggerd/utility.cpp", ], Loading Loading @@ -206,6 +208,8 @@ cc_library_static { whole_static_libs: [ "gwp_asan_crash_handler", "libscudo", "libtombstone_proto", "libprotobuf-cpp-lite", ], target: { Loading @@ -228,6 +232,20 @@ cc_library_static { }, } cc_binary { name: "pbtombstone", defaults: ["debuggerd_defaults"], srcs: ["pbtombstone.cpp"], static_libs: [ "libbase", "libdebuggerd", "liblog", "libprotobuf-cpp-lite", "libtombstone_proto", "libunwindstack", ], } cc_test { name: "debuggerd_test", defaults: ["debuggerd_defaults"], Loading Loading @@ -332,6 +350,9 @@ cc_binary { "libtombstoned_client_static", "libdebuggerd", "libcutils", "libtombstone_proto", "libprotobuf-cpp-lite", ], shared_libs: [ Loading
debuggerd/common/include/dump_type.h +5 −1 Original line number Diff line number Diff line Loading @@ -24,7 +24,8 @@ enum DebuggerdDumpType : uint8_t { kDebuggerdNativeBacktrace, kDebuggerdTombstone, kDebuggerdJavaBacktrace, kDebuggerdAnyIntercept kDebuggerdAnyIntercept, kDebuggerdTombstoneProto, }; inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) { Loading @@ -41,6 +42,9 @@ inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& r case kDebuggerdAnyIntercept: stream << "kDebuggerdAnyIntercept"; break; case kDebuggerdTombstoneProto: stream << "kDebuggerdTombstoneProto"; break; default: stream << "[unknown]"; } Loading
debuggerd/crash_dump.cpp +22 −6 Original line number Diff line number Diff line Loading @@ -195,6 +195,7 @@ static pid_t g_target_thread = -1; static bool g_tombstoned_connected = false; static unique_fd g_tombstoned_socket; static unique_fd g_output_fd; static unique_fd g_proto_fd; static void DefuseSignalHandlers() { // Don't try to dump ourselves. Loading @@ -215,7 +216,7 @@ static void Initialize(char** argv) { // If we abort before we get an output fd, contact tombstoned to let any // potential listeners know that we failed. if (!g_tombstoned_connected) { if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, if (!tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, &g_proto_fd, kDebuggerdAnyIntercept)) { // We failed to connect, not much we can do. LOG(ERROR) << "failed to connected to tombstoned to report failure"; Loading Loading @@ -248,10 +249,20 @@ static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdD } int dump_type_int; if (!android::base::ParseInt(argv[3], &dump_type_int, 0, 1)) { if (!android::base::ParseInt(argv[3], &dump_type_int, 0)) { LOG(FATAL) << "invalid requested dump type: " << argv[3]; } *dump_type = static_cast<DebuggerdDumpType>(dump_type_int); switch (*dump_type) { case kDebuggerdNativeBacktrace: case kDebuggerdTombstone: case kDebuggerdTombstoneProto: break; default: LOG(FATAL) << "invalid requested dump type: " << dump_type_int; } } static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, Loading Loading @@ -480,6 +491,11 @@ int main(int argc, char** argv) { info.process_name = process_name; info.thread_name = get_thread_name(thread); unique_fd attr_fd(openat(target_proc_fd, "attr/current", O_RDONLY | O_CLOEXEC)); if (!android::base::ReadFdToString(attr_fd, &info.selinux_label)) { PLOG(WARNING) << "failed to read selinux label"; } if (!ptrace_interrupt(thread, &info.signo)) { PLOG(WARNING) << "failed to ptrace interrupt thread " << thread; ptrace(PTRACE_DETACH, thread, 0, 0); Loading Loading @@ -558,8 +574,8 @@ int main(int argc, char** argv) { { ATRACE_NAME("tombstoned_connect"); LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type; g_tombstoned_connected = tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, dump_type); g_tombstoned_connected = tombstoned_connect(g_target_thread, &g_tombstoned_socket, &g_output_fd, &g_proto_fd, dump_type); } if (g_tombstoned_connected) { Loading Loading @@ -612,8 +628,8 @@ int main(int argc, char** argv) { { ATRACE_NAME("engrave_tombstone"); engrave_tombstone(std::move(g_output_fd), &unwinder, thread_info, g_target_thread, process_info, &open_files, &amfd_data); engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info, g_target_thread, process_info, &open_files, &amfd_data); } } Loading
debuggerd/debuggerd_test.cpp +69 −2 Original line number Diff line number Diff line Loading @@ -1309,11 +1309,11 @@ TEST(tombstoned, java_trace_intercept_smoke) { tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace); ASSERT_EQ(InterceptStatus::kRegistered, status); // First connect to tombstoned requesting a native backtrace. This // First connect to tombstoned requesting a native tombstone. This // should result in a "regular" FD and not the installed intercept. const char native[] = "native"; unique_fd tombstoned_socket, input_fd; ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdNativeBacktrace)); ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdTombstone)); ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native))); tombstoned_notify_completion(tombstoned_socket.get()); Loading Loading @@ -1425,3 +1425,70 @@ TEST_F(CrasherTest, stack_overflow) { ConsumeFd(std::move(output_fd), &result); ASSERT_MATCH(result, R"(Cause: stack pointer[^\n]*stack overflow.\n)"); } TEST(tombstoned, proto) { const pid_t self = getpid(); unique_fd tombstoned_socket, text_fd, proto_fd; ASSERT_TRUE( tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto)); tombstoned_notify_completion(tombstoned_socket.get()); ASSERT_NE(-1, text_fd.get()); ASSERT_NE(-1, proto_fd.get()); struct stat text_st; ASSERT_EQ(0, fstat(text_fd.get(), &text_st)); // Give tombstoned some time to link the files into place. std::this_thread::sleep_for(100ms); // Find the tombstone. std::optional<int> tombstone_index; for (int i = 0; i < 50; ++i) { std::string path = android::base::StringPrintf("/data/tombstones/tombstone_%02d", i); struct stat st; if (TEMP_FAILURE_RETRY(stat(path.c_str(), &st)) != 0) { continue; } if (st.st_dev == text_st.st_dev && st.st_ino == text_st.st_ino) { tombstone_index = i; break; } } ASSERT_TRUE(tombstone_index); std::string proto_path = android::base::StringPrintf("/data/tombstones/tombstone_%02d.pb", *tombstone_index); struct stat proto_fd_st; struct stat proto_file_st; ASSERT_EQ(0, fstat(proto_fd.get(), &proto_fd_st)); ASSERT_EQ(0, stat(proto_path.c_str(), &proto_file_st)); ASSERT_EQ(proto_fd_st.st_dev, proto_file_st.st_dev); ASSERT_EQ(proto_fd_st.st_ino, proto_file_st.st_ino); } TEST(tombstoned, proto_intercept) { const pid_t self = getpid(); unique_fd intercept_fd, output_fd; InterceptStatus status; tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); ASSERT_EQ(InterceptStatus::kRegistered, status); unique_fd tombstoned_socket, text_fd, proto_fd; ASSERT_TRUE( tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto)); ASSERT_TRUE(android::base::WriteStringToFd("foo", text_fd.get())); tombstoned_notify_completion(tombstoned_socket.get()); text_fd.reset(); std::string output; ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output)); ASSERT_EQ("foo", output); }
debuggerd/handler/debuggerd_fallback.cpp +10 −9 Original line number Diff line number Diff line Loading @@ -92,15 +92,15 @@ static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { __linker_disable_fallback_allocator(); } static void debuggerd_fallback_tombstone(int output_fd, ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) { static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t* ucontext, siginfo_t* siginfo, void* abort_message) { if (!__linker_enable_fallback_allocator()) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use"); return; } engrave_tombstone_ucontext(output_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo, ucontext); engrave_tombstone_ucontext(output_fd, proto_fd, reinterpret_cast<uintptr_t>(abort_message), siginfo, ucontext); __linker_disable_fallback_allocator(); } Loading Loading @@ -232,7 +232,8 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { // Fetch output fd from tombstoned. unique_fd tombstone_socket, output_fd; if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) { if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr, kDebuggerdNativeBacktrace)) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "missing crash_dump_fallback() in selinux policy?"); goto exit; Loading Loading @@ -325,10 +326,10 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes _exit(1); } unique_fd tombstone_socket, output_fd; bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdTombstone); debuggerd_fallback_tombstone(output_fd.get(), ucontext, info, abort_message); unique_fd tombstone_socket, output_fd, proto_fd; bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd, kDebuggerdTombstoneProto); debuggerd_fallback_tombstone(output_fd.get(), proto_fd.get(), ucontext, info, abort_message); if (tombstoned_connected) { tombstoned_notify_completion(tombstone_socket.get()); } Loading