diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index 3a80b50bb..5565cfdd7 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp @@ -11,6 +11,11 @@ cc_defaults { local_include_dirs: ["include"], } +cc_library_headers { + name: "libdebuggerd_common_headers", + export_include_dirs: ["common/include"] +} + cc_library_shared { name: "libtombstoned_client", defaults: ["debuggerd_defaults"], @@ -19,15 +24,18 @@ cc_library_shared { "util.cpp", ], + header_libs: ["libdebuggerd_common_headers"], + static_libs: [ - "libasync_safe" + "libasync_safe", ], shared_libs: [ - "libcutils", "libbase", + "libcutils", ], + export_header_lib_headers: ["libdebuggerd_common_headers"], export_include_dirs: ["tombstoned/include"] } @@ -40,12 +48,15 @@ cc_library_static { "util.cpp", ], + header_libs: ["libdebuggerd_common_headers"], + whole_static_libs: [ "libasync_safe", "libcutils", "libbase", ], + export_header_lib_headers: ["libdebuggerd_common_headers"], export_include_dirs: ["tombstoned/include"] } @@ -55,11 +66,14 @@ cc_library_static { defaults: ["debuggerd_defaults"], srcs: ["handler/debuggerd_handler.cpp"], + header_libs: ["libdebuggerd_common_headers"], + whole_static_libs: [ "libasync_safe", "libdebuggerd", ], + export_header_lib_headers: ["libdebuggerd_common_headers"], export_include_dirs: ["include"], } @@ -107,11 +121,14 @@ cc_library { "util.cpp", ], + header_libs: ["libdebuggerd_common_headers"], + shared_libs: [ "libbase", "libcutils", ], + export_header_lib_headers: ["libdebuggerd_common_headers"], export_include_dirs: ["include"], } @@ -191,7 +208,8 @@ cc_test { "libbase", "libcutils", "libdebuggerd_client", - "liblog" + "liblog", + "libnativehelper" ], static_libs: [ @@ -271,6 +289,8 @@ cc_binary { ], defaults: ["debuggerd_defaults"], + header_libs: ["libdebuggerd_common_headers"], + static_libs: [ "libbase", "libcutils", diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp index 4ce038ce4..c357fd6d6 100644 --- a/debuggerd/client/debuggerd_client.cpp +++ b/debuggerd/client/debuggerd_client.cpp @@ -40,10 +40,12 @@ using namespace std::chrono_literals; using android::base::unique_fd; -static bool send_signal(pid_t pid, bool backtrace) { +static bool send_signal(pid_t pid, const DebuggerdDumpType dump_type) { + const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : DEBUGGER_SIGNAL; sigval val; - val.sival_int = backtrace; - if (sigqueue(pid, DEBUGGER_SIGNAL, val) != 0) { + val.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0; + + if (sigqueue(pid, signal, val) != 0) { PLOG(ERROR) << "libdebuggerd_client: failed to send signal to pid " << pid; return false; } @@ -58,8 +60,8 @@ static void populate_timeval(struct timeval* tv, const Duration& duration) { tv->tv_usec = static_cast(microseconds.count()); } -bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type, - unsigned int timeout_ms) { +bool debuggerd_trigger_dump(pid_t pid, DebuggerdDumpType dump_type, unsigned int timeout_ms, + unique_fd output_fd) { LOG(INFO) << "libdebuggerd_client: started dumping process " << pid; unique_fd sockfd; const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms); @@ -102,7 +104,7 @@ bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType du return false; } - InterceptRequest req = {.pid = pid }; + InterceptRequest req = {.pid = pid, .dump_type = dump_type}; if (!set_timeout(sockfd)) { PLOG(ERROR) << "libdebugger_client: failed to set timeout"; return false; @@ -140,8 +142,7 @@ bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType du return false; } - bool backtrace = dump_type == kDebuggerdBacktrace; - if (!send_signal(pid, backtrace)) { + if (!send_signal(pid, dump_type)) { return false; } @@ -210,15 +211,16 @@ bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType du return true; } -int dump_backtrace_to_file(pid_t tid, int fd) { - return dump_backtrace_to_file_timeout(tid, fd, 0); +int dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) { + return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd); } -int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs) { +int dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs, + int fd) { android::base::unique_fd copy(dup(fd)); if (copy == -1) { return -1; } int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0; - return debuggerd_trigger_dump(tid, std::move(copy), kDebuggerdBacktrace, timeout_ms) ? 0 : -1; + return debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1; } diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp index 8f97db1fa..8420f038f 100644 --- a/debuggerd/client/debuggerd_client_test.cpp +++ b/debuggerd/client/debuggerd_client_test.cpp @@ -67,7 +67,8 @@ TEST(debuggerd_client, race) { // Wait for a bit to let the child spawn all of its threads. std::this_thread::sleep_for(250ms); - ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000)); + ASSERT_TRUE( + debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 10000, std::move(pipe_write))); // Immediately kill the forked child, to make sure that the dump didn't return early. ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno); @@ -107,5 +108,6 @@ TEST(debuggerd_client, no_timeout) { unique_fd output_read, output_write; ASSERT_TRUE(Pipe(&output_read, &output_write)); - ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0)); + ASSERT_TRUE( + debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 0, std::move(output_write))); } diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h new file mode 100644 index 000000000..203269e96 --- /dev/null +++ b/debuggerd/common/include/dump_type.h @@ -0,0 +1,49 @@ +#pragma once + +/* + * Copyright 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include + +enum DebuggerdDumpType : uint8_t { + kDebuggerdNativeBacktrace, + kDebuggerdTombstone, + kDebuggerdJavaBacktrace, + kDebuggerdAnyIntercept +}; + +inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) { + switch (rhs) { + case kDebuggerdNativeBacktrace: + stream << "kDebuggerdNativeBacktrace"; + break; + case kDebuggerdTombstone: + stream << "kDebuggerdTombstone"; + break; + case kDebuggerdJavaBacktrace: + stream << "kDebuggerdJavaBacktrace"; + break; + case kDebuggerdAnyIntercept: + stream << "kDebuggerdAnyIntercept"; + break; + default: + stream << "[unknown]"; + } + + return stream; +} diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index be2807914..cc7824219 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -150,13 +150,13 @@ static void signal_handler(int) { _exit(1); } -static void abort_handler(pid_t target, const bool& tombstoned_connected, +static void abort_handler(pid_t target, const bool tombstoned_connected, unique_fd& tombstoned_socket, unique_fd& output_fd, const char* abort_msg) { // If we abort before we get an output fd, contact tombstoned to let any // potential listeners know that we failed. if (!tombstoned_connected) { - if (!tombstoned_connect(target, &tombstoned_socket, &output_fd)) { + if (!tombstoned_connect(target, &tombstoned_socket, &output_fd, kDebuggerdAnyIntercept)) { // We failed to connect, not much we can do. LOG(ERROR) << "failed to connected to tombstoned to report failure"; _exit(1); @@ -207,12 +207,14 @@ int main(int argc, char** argv) { action.sa_handler = signal_handler; debuggerd_register_handlers(&action); - if (argc != 3) { + if (argc != 4) { + LOG(FATAL) << "Wrong number of args: " << argc << " (expected 4)"; return 1; } pid_t main_tid; pid_t pseudothread_tid; + int dump_type; if (!android::base::ParseInt(argv[1], &main_tid, 1, std::numeric_limits::max())) { LOG(FATAL) << "invalid main tid: " << argv[1]; @@ -222,6 +224,10 @@ int main(int argc, char** argv) { LOG(FATAL) << "invalid pseudothread tid: " << argv[2]; } + if (!android::base::ParseInt(argv[3], &dump_type, 0, 1)) { + LOG(FATAL) << "invalid requested dump type: " << argv[3]; + } + if (target == 1) { LOG(FATAL) << "target died before we could attach (received main tid = " << main_tid << ")"; } @@ -305,8 +311,9 @@ int main(int argc, char** argv) { // Drop our capabilities now that we've attached to the threads we care about. drop_capabilities(); - LOG(INFO) << "obtaining output fd from tombstoned"; - tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd); + const DebuggerdDumpType dump_type_enum = static_cast(dump_type); + LOG(INFO) << "obtaining output fd from tombstoned, type: " << dump_type_enum; + tombstoned_connected = tombstoned_connect(target, &tombstoned_socket, &output_fd, dump_type_enum); // Write a '\1' to stdout to tell the crashing process to resume. // It also restores the value of PR_SET_DUMPABLE at this point. diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index 4997dd64b..6298ace9d 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -72,8 +72,8 @@ int main(int argc, char* argv[]) { } std::thread redirect_thread = spawn_redirect_thread(std::move(piperead)); - if (!debuggerd_trigger_dump(pid, std::move(pipewrite), - backtrace_only ? kDebuggerdBacktrace : kDebuggerdTombstone, 0)) { + if (!debuggerd_trigger_dump(pid, backtrace_only ? kDebuggerdNativeBacktrace : kDebuggerdTombstone, + 0, std::move(pipewrite))) { redirect_thread.join(); errx(1, "failed to dump process %d", pid); } diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index f17724a40..8c00b76a0 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp @@ -88,14 +88,15 @@ constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb"; } \ } while (0) -static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) { +static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd, + DebuggerdDumpType intercept_type) { intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET)); if (intercept_fd->get() == -1) { FAIL() << "failed to contact tombstoned: " << strerror(errno); } - InterceptRequest req = {.pid = target_pid}; + InterceptRequest req = {.pid = target_pid, .dump_type = intercept_type}; unique_fd output_pipe_write; if (!Pipe(output_fd, &output_pipe_write)) { @@ -146,7 +147,7 @@ class CrasherTest : public ::testing::Test { CrasherTest(); ~CrasherTest(); - void StartIntercept(unique_fd* output_fd); + void StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type = kDebuggerdTombstone); // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code. void FinishIntercept(int* result); @@ -172,12 +173,12 @@ CrasherTest::~CrasherTest() { android::base::SetProperty(kWaitForGdbKey, previous_wait_for_gdb ? "1" : "0"); } -void CrasherTest::StartIntercept(unique_fd* output_fd) { +void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) { if (crasher_pid == -1) { FAIL() << "crasher hasn't been started"; } - tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd); + tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, intercept_type); } void CrasherTest::FinishIntercept(int* result) { @@ -428,7 +429,7 @@ TEST_F(CrasherTest, backtrace) { StartProcess([]() { abort(); }); - StartIntercept(&output_fd); + StartIntercept(&output_fd, kDebuggerdNativeBacktrace); std::this_thread::sleep_for(500ms); @@ -595,11 +596,11 @@ TEST(tombstoned, no_notify) { pid_t pid = 123'456'789 + i; unique_fd intercept_fd, output_fd; - tombstoned_intercept(pid, &intercept_fd, &output_fd); + tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone); { unique_fd tombstoned_socket, input_fd; - ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd)); + ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone)); ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid))); } @@ -627,7 +628,7 @@ TEST(tombstoned, stress) { pid_t pid = pid_base + dump; unique_fd intercept_fd, output_fd; - tombstoned_intercept(pid, &intercept_fd, &output_fd); + tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone); // Pretend to crash, and then immediately close the socket. unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName, @@ -658,11 +659,11 @@ TEST(tombstoned, stress) { pid_t pid = pid_base + dump; unique_fd intercept_fd, output_fd; - tombstoned_intercept(pid, &intercept_fd, &output_fd); + tombstoned_intercept(pid, &intercept_fd, &output_fd, kDebuggerdTombstone); { unique_fd tombstoned_socket, input_fd; - ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd)); + ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone)); ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid))); tombstoned_notify_completion(tombstoned_socket.get()); } diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp index a9c9862e4..43104ecbd 100644 --- a/debuggerd/handler/debuggerd_fallback.cpp +++ b/debuggerd/handler/debuggerd_fallback.cpp @@ -151,7 +151,7 @@ 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)) { + if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, kDebuggerdNativeBacktrace)) { goto exit; } @@ -215,7 +215,8 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes } unique_fd tombstone_socket, output_fd; - bool tombstoned_connected = tombstoned_connect(getpid(), &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); if (tombstoned_connected) { tombstoned_notify_completion(tombstone_socket.get()); diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp index 8fd6e1141..55cd03e38 100644 --- a/debuggerd/handler/debuggerd_handler.cpp +++ b/debuggerd/handler/debuggerd_handler.cpp @@ -50,6 +50,8 @@ #include +#include "dump_type.h" + // see man(2) prctl, specifically the section about PR_GET_NAME #define MAX_TASK_NAME_LEN (16) @@ -253,6 +255,14 @@ struct debugger_thread_info { // process. static void* pseudothread_stack; +static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) { + if (thread_info->signal_number == DEBUGGER_SIGNAL && thread_info->info->si_value.sival_int) { + return kDebuggerdNativeBacktrace; + } + + return kDebuggerdTombstone; +} + static int debuggerd_dispatch_pseudothread(void* arg) { debugger_thread_info* thread_info = static_cast(arg); @@ -285,11 +295,15 @@ static int debuggerd_dispatch_pseudothread(void* arg) { char main_tid[10]; char pseudothread_tid[10]; + char debuggerd_dump_type[10]; async_safe_format_buffer(main_tid, sizeof(main_tid), "%d", thread_info->crashing_tid); async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), "%d", thread_info->pseudothread_tid); + async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), "%d", + get_dump_type(thread_info)); - execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, nullptr); + execl(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type, + nullptr); fatal_errno("exec failed"); } else { diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h index 01de57b40..b7284b08e 100644 --- a/debuggerd/include/debuggerd/client.h +++ b/debuggerd/include/debuggerd/client.h @@ -22,15 +22,13 @@ #include -enum DebuggerdDumpType { - kDebuggerdBacktrace, - kDebuggerdTombstone, -}; +#include "dump_type.h" // Trigger a dump of specified process to output_fd. // output_fd is consumed, timeout of 0 will wait forever. -bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd, - enum DebuggerdDumpType dump_type, unsigned int timeout_ms); +bool debuggerd_trigger_dump(pid_t pid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms, + android::base::unique_fd output_fd); -int dump_backtrace_to_file(pid_t tid, int fd); -int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs); +int dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd); +int dump_backtrace_to_file_timeout(pid_t tid, enum DebuggerdDumpType dump_type, int timeout_secs, + int output_fd); diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h index 144efc8f7..7e1961e75 100644 --- a/debuggerd/protocol.h +++ b/debuggerd/protocol.h @@ -18,6 +18,8 @@ #include +#include "dump_type.h" + // Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace. // Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed. constexpr char kTombstonedCrashSocketName[] = "tombstoned_crash"; @@ -40,6 +42,7 @@ enum class CrashPacketType : uint8_t { }; struct DumpRequest { + DebuggerdDumpType dump_type; int32_t pid; }; @@ -54,10 +57,15 @@ struct TombstonedCrashPacket { // Comes with a file descriptor via SCM_RIGHTS. // This packet should be sent before an actual dump happens. struct InterceptRequest { + DebuggerdDumpType dump_type; int32_t pid; }; enum class InterceptStatus : uint8_t { + // Returned when an intercept of a different type has already been + // registered (and is active) for a given PID. + kFailedAlreadyRegistered, + // Returned in all other failure cases. kFailed, kStarted, kRegistered, diff --git a/debuggerd/tombstoned/include/tombstoned/tombstoned.h b/debuggerd/tombstoned/include/tombstoned/tombstoned.h index 908517d7f..6403dbec4 100644 --- a/debuggerd/tombstoned/include/tombstoned/tombstoned.h +++ b/debuggerd/tombstoned/include/tombstoned/tombstoned.h @@ -20,7 +20,9 @@ #include +#include "dump_type.h" + bool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket, - android::base::unique_fd* output_fd, bool is_native_crash = true); + android::base::unique_fd* output_fd, DebuggerdDumpType dump_type); bool tombstoned_notify_completion(int tombstoned_socket); diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp index 4d4eb9e19..24960bc98 100644 --- a/debuggerd/tombstoned/intercept_manager.cpp +++ b/debuggerd/tombstoned/intercept_manager.cpp @@ -61,11 +61,24 @@ static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) { reason = "due to input"; } - LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " terminated " << reason; + LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type " + << intercept->dump_type << " terminated: " << reason; intercept_manager->intercepts.erase(it); } } +static bool is_intercept_request_valid(const InterceptRequest& request) { + if (request.pid <= 0 || request.pid > std::numeric_limits::max()) { + return false; + } + + if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) { + return false; + } + + return true; +} + static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) { auto intercept = reinterpret_cast(arg); InterceptManager* intercept_manager = intercept->intercept_manager; @@ -103,23 +116,24 @@ static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) { rcv_fd.reset(moved_fd); // We trust the other side, so only do minimal validity checking. - if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits::max()) { + if (!is_intercept_request_valid(intercept_request)) { InterceptResponse response = {}; response.status = InterceptStatus::kFailed; - snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32, - intercept_request.pid); + snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request"); TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))); goto fail; } intercept->intercept_pid = intercept_request.pid; + intercept->dump_type = intercept_request.dump_type; // Check if it's already registered. if (intercept_manager->intercepts.count(intercept_request.pid) > 0) { InterceptResponse response = {}; - response.status = InterceptStatus::kFailed; + response.status = InterceptStatus::kFailedAlreadyRegistered; snprintf(response.error_message, sizeof(response.error_message), - "pid %" PRId32 " already intercepted", intercept_request.pid); + "pid %" PRId32 " already intercepted, type %d", intercept_request.pid, + intercept_request.dump_type); TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))); LOG(WARNING) << response.error_message; goto fail; @@ -138,7 +152,8 @@ static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) { intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr(intercept); intercept->registered = true; - LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid; + LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type " + << intercept_request.dump_type; // Register a different read event on the socket so that we can remove intercepts if the socket // closes (e.g. if a user CTRL-C's the process that requested the intercept). @@ -174,16 +189,27 @@ InterceptManager::InterceptManager(event_base* base, int intercept_socket) : bas intercept_socket); } -bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) { +bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type, + android::base::unique_fd* out_fd) { auto it = this->intercepts.find(pid); if (it == this->intercepts.end()) { return false; } + if (dump_type == kDebuggerdAnyIntercept) { + LOG(INFO) << "found registered intercept of type " << it->second->dump_type + << " for requested type kDebuggerdAnyIntercept"; + } else if (it->second->dump_type != dump_type) { + LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type + << " for requested type: " << dump_type; + return false; + } + auto intercept = std::move(it->second); this->intercepts.erase(it); - LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid; + LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid + << " and type " << intercept->dump_type; InterceptResponse response = {}; response.status = InterceptStatus::kStarted; TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response))); diff --git a/debuggerd/tombstoned/intercept_manager.h b/debuggerd/tombstoned/intercept_manager.h index cb5db6262..a11d565a3 100644 --- a/debuggerd/tombstoned/intercept_manager.h +++ b/debuggerd/tombstoned/intercept_manager.h @@ -25,6 +25,8 @@ #include +#include "dump_type.h" + struct InterceptManager; struct Intercept { @@ -39,6 +41,7 @@ struct Intercept { pid_t intercept_pid = -1; android::base::unique_fd output_fd; bool registered = false; + DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace; }; struct InterceptManager { @@ -50,5 +53,5 @@ struct InterceptManager { InterceptManager(InterceptManager& copy) = delete; InterceptManager(InterceptManager&& move) = delete; - bool GetIntercept(pid_t pid, android::base::unique_fd* out_fd); + bool GetIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd); }; diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp index 05df9f2b6..0a9f00008 100644 --- a/debuggerd/tombstoned/tombstoned.cpp +++ b/debuggerd/tombstoned/tombstoned.cpp @@ -35,6 +35,7 @@ #include #include "debuggerd/handler.h" +#include "dump_type.h" #include "protocol.h" #include "util.h" @@ -52,10 +53,10 @@ enum CrashStatus { struct Crash; -class CrashType { +class CrashQueue { public: - CrashType(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts, - size_t max_concurrent_dumps) + CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts, + size_t max_concurrent_dumps) : file_name_prefix_(file_name_prefix), dir_path_(dir_path), dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)), @@ -114,8 +115,8 @@ class CrashType { void on_crash_completed() { --num_concurrent_dumps_; } - static CrashType* const tombstone; - static CrashType* const java_trace; + static CrashQueue* const tombstone; + static CrashQueue* const java_trace; private: void find_oldest_artifact() { @@ -158,19 +159,19 @@ class CrashType { std::deque queued_requests_; - DISALLOW_COPY_AND_ASSIGN(CrashType); + DISALLOW_COPY_AND_ASSIGN(CrashQueue); }; // Whether java trace dumps are produced via tombstoned. static constexpr bool kJavaTraceDumpsEnabled = false; -/* static */ CrashType* const CrashType::tombstone = - new CrashType("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */, - 1 /* max_concurrent_dumps */); +/* static */ CrashQueue* const CrashQueue::tombstone = + new CrashQueue("/data/tombstones", "tombstone_" /* file_name_prefix */, 10 /* max_artifacts */, + 1 /* max_concurrent_dumps */); -/* static */ CrashType* const CrashType::java_trace = - (kJavaTraceDumpsEnabled ? new CrashType("/data/anr", "anr_" /* file_name_prefix */, - 64 /* max_artifacts */, 4 /* max_concurrent_dumps */) +/* static */ CrashQueue* const CrashQueue::java_trace = + (kJavaTraceDumpsEnabled ? new CrashQueue("/data/anr", "anr_" /* file_name_prefix */, + 64 /* max_artifacts */, 4 /* max_concurrent_dumps */) : nullptr); // Ownership of Crash is a bit messy. @@ -183,10 +184,17 @@ struct Crash { pid_t crash_pid; event* crash_event = nullptr; - // Not owned by |Crash|. - CrashType* crash_type = nullptr; + DebuggerdDumpType crash_type; }; +static CrashQueue* get_crash_queue(const Crash* crash) { + if (crash->crash_type == kDebuggerdJavaBacktrace) { + return CrashQueue::java_trace; + } + + return CrashQueue::tombstone; +} + // Forward declare the callbacks so they can be placed in a sensible order. static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, void*); static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg); @@ -194,10 +202,8 @@ static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg); static void perform_request(Crash* crash) { unique_fd output_fd; - // Note that java traces are not interceptible. - if ((crash->crash_type == CrashType::java_trace) || - !intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) { - output_fd = crash->crash_type->get_output_fd(); + if (!intercept_manager->GetIntercept(crash->crash_pid, crash->crash_type, &output_fd)) { + output_fd = get_crash_queue(crash)->get_output_fd(); } TombstonedCrashPacket response = { @@ -220,7 +226,7 @@ static void perform_request(Crash* crash) { event_add(crash->crash_event, &timeout); } - crash->crash_type->on_crash_started(); + get_crash_queue(crash)->on_crash_started(); return; fail: @@ -228,22 +234,22 @@ fail: } static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, - void* crash_type) { + void*) { event_base* base = evconnlistener_get_base(listener); Crash* crash = new Crash(); + // TODO: Make sure that only java crashes come in on the java socket + // and only native crashes on the native socket. struct timeval timeout = { 1, 0 }; event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash); crash->crash_fd.reset(sockfd); crash->crash_event = crash_event; - crash->crash_type = static_cast(crash_type); event_add(crash_event, &timeout); } static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) { ssize_t rc; Crash* crash = static_cast(arg); - CrashType* type = crash->crash_type; TombstonedCrashPacket request = {}; @@ -271,7 +277,13 @@ static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) { goto fail; } - if (type == CrashType::tombstone) { + crash->crash_type = request.packet.dump_request.dump_type; + if (crash->crash_type < 0 || crash->crash_type > kDebuggerdAnyIntercept) { + LOG(WARNING) << "unexpected crash dump type: " << crash->crash_type; + goto fail; + } + + if (crash->crash_type != kDebuggerdJavaBacktrace) { crash->crash_pid = request.packet.dump_request.pid; } else { // Requests for java traces are sent from untrusted processes, so we @@ -290,7 +302,7 @@ static void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) { LOG(INFO) << "received crash request for pid " << crash->crash_pid; - if (type->maybe_enqueue_crash(crash)) { + if (get_crash_queue(crash)->maybe_enqueue_crash(crash)) { LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid; } else { perform_request(crash); @@ -307,7 +319,7 @@ static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) { Crash* crash = static_cast(arg); TombstonedCrashPacket request = {}; - crash->crash_type->on_crash_completed(); + get_crash_queue(crash)->on_crash_completed(); if ((ev & EV_READ) == 0) { goto fail; @@ -330,11 +342,11 @@ static void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) { } fail: - CrashType* type = crash->crash_type; + CrashQueue* queue = get_crash_queue(crash); delete crash; // If there's something queued up, let them proceed. - type->maybe_dequeue_crashes(perform_request); + queue->maybe_dequeue_crashes(perform_request); } int main(int, char* []) { @@ -366,7 +378,7 @@ int main(int, char* []) { intercept_manager = new InterceptManager(base, intercept_socket); evconnlistener* tombstone_listener = evconnlistener_new( - base, crash_accept_cb, CrashType::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket); + base, crash_accept_cb, CrashQueue::tombstone, -1, LEV_OPT_CLOSE_ON_FREE, crash_socket); if (!tombstone_listener) { LOG(FATAL) << "failed to create evconnlistener for tombstones."; } @@ -379,7 +391,7 @@ int main(int, char* []) { evutil_make_socket_nonblocking(java_trace_socket); evconnlistener* java_trace_listener = evconnlistener_new( - base, crash_accept_cb, CrashType::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket); + base, crash_accept_cb, CrashQueue::java_trace, -1, LEV_OPT_CLOSE_ON_FREE, java_trace_socket); if (!java_trace_listener) { LOG(FATAL) << "failed to create evconnlistener for java traces."; } diff --git a/debuggerd/tombstoned/tombstoned_client.cpp b/debuggerd/tombstoned/tombstoned_client.cpp index 39dc6eb07..bdb4c1a6f 100644 --- a/debuggerd/tombstoned/tombstoned_client.cpp +++ b/debuggerd/tombstoned/tombstoned_client.cpp @@ -31,10 +31,11 @@ using android::base::unique_fd; bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* output_fd, - bool is_native_crash) { - unique_fd sockfd(socket_local_client( - (is_native_crash ? kTombstonedCrashSocketName : kTombstonedJavaTraceSocketName), - ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET)); + DebuggerdDumpType dump_type) { + unique_fd sockfd( + socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName + : kTombstonedJavaTraceSocketName), + ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET)); if (sockfd == -1) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to connect to tombstoned: %s", strerror(errno)); @@ -44,6 +45,7 @@ bool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* outp TombstonedCrashPacket packet = {}; packet.packet_type = CrashPacketType::kDumpRequest; packet.packet.dump_request.pid = pid; + packet.packet.dump_request.dump_type = dump_type; if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write DumpRequest packet: %s", strerror(errno));