From 53fb32f05cc17848f239dec6b8c821b7c5124ef4 Mon Sep 17 00:00:00 2001 From: Richard Uhler Date: Fri, 28 Oct 2016 16:37:33 +0100 Subject: [PATCH] debuggerd: Dump list of open files on process crash. Test: Open a bunch of files, wait for the process to crash, verify dubuggerd includes the list of open files the tombstone it generates. Test: Added OpenFilesListTest to debuggerd_test. Bug: 32013594 Change-Id: I6f939ae1d04dc58dc99abff0ed930da9e0ef0d1c --- debuggerd/Android.mk | 3 ++ debuggerd/debuggerd.cpp | 18 +++++-- debuggerd/open_files_list.cpp | 69 +++++++++++++++++++++++++ debuggerd/open_files_list.h | 36 +++++++++++++ debuggerd/test/open_files_list_test.cpp | 49 ++++++++++++++++++ debuggerd/tombstone.cpp | 12 +++-- debuggerd/tombstone.h | 3 +- debuggerd/utility.h | 3 +- 8 files changed, 183 insertions(+), 10 deletions(-) create mode 100644 debuggerd/open_files_list.cpp create mode 100644 debuggerd/open_files_list.h create mode 100644 debuggerd/test/open_files_list_test.cpp diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk index 155b30936..607745dd3 100644 --- a/debuggerd/Android.mk +++ b/debuggerd/Android.mk @@ -18,6 +18,7 @@ LOCAL_SRC_FILES:= \ debuggerd.cpp \ elf_utils.cpp \ getevent.cpp \ + open_files_list.cpp \ signal_sender.cpp \ tombstone.cpp \ utility.cpp \ @@ -108,9 +109,11 @@ include $(BUILD_EXECUTABLE) debuggerd_test_src_files := \ utility.cpp \ + open_files_list.cpp \ test/dump_memory_test.cpp \ test/elf_fake.cpp \ test/log_fake.cpp \ + test/open_files_list_test.cpp \ test/property_fake.cpp \ test/ptrace_fake.cpp \ test/tombstone_test.cpp \ diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index 5ae66db5c..272fbf689 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -55,6 +55,7 @@ #include "backtrace.h" #include "getevent.h" +#include "open_files_list.h" #include "signal_sender.h" #include "tombstone.h" #include "utility.h" @@ -452,7 +453,8 @@ static void ptrace_siblings(pid_t pid, pid_t main_tid, pid_t ignore_tid, std::se } static bool perform_dump(const debugger_request_t& request, int fd, int tombstone_fd, - BacktraceMap* backtrace_map, const std::set& siblings, + BacktraceMap* backtrace_map, const OpenFilesList& open_files, + const std::set& siblings, int* crash_signal, std::string* amfd_data) { if (TEMP_FAILURE_RETRY(write(fd, "\0", 1)) != 1) { ALOGE("debuggerd: failed to respond to client: %s\n", strerror(errno)); @@ -471,7 +473,8 @@ static bool perform_dump(const debugger_request_t& request, int fd, int tombston case SIGSTOP: if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { ALOGV("debuggerd: stopped -- dumping to tombstone"); - engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, + engrave_tombstone(tombstone_fd, backtrace_map, open_files, + request.pid, request.tid, siblings, request.abort_msg_address, amfd_data); } else if (request.action == DEBUGGER_ACTION_DUMP_BACKTRACE) { ALOGV("debuggerd: stopped -- dumping to fd"); @@ -498,7 +501,8 @@ static bool perform_dump(const debugger_request_t& request, int fd, int tombston case SIGTRAP: ALOGV("stopped -- fatal signal\n"); *crash_signal = signal; - engrave_tombstone(tombstone_fd, backtrace_map, request.pid, request.tid, siblings, + engrave_tombstone(tombstone_fd, backtrace_map, open_files, + request.pid, request.tid, siblings, request.abort_msg_address, amfd_data); break; @@ -593,6 +597,10 @@ static void worker_process(int fd, debugger_request_t& request) { // Generate the backtrace map before dropping privileges. std::unique_ptr backtrace_map(BacktraceMap::Create(request.pid)); + // Collect the list of open files before dropping privileges. + OpenFilesList open_files; + populate_open_files_list(request.pid, &open_files); + int amfd = -1; std::unique_ptr amfd_data; if (request.action == DEBUGGER_ACTION_CRASH) { @@ -610,8 +618,8 @@ static void worker_process(int fd, debugger_request_t& request) { } int crash_signal = SIGKILL; - succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), siblings, - &crash_signal, amfd_data.get()); + succeeded = perform_dump(request, fd, tombstone_fd, backtrace_map.get(), open_files, + siblings, &crash_signal, amfd_data.get()); if (succeeded) { if (request.action == DEBUGGER_ACTION_DUMP_TOMBSTONE) { if (!tombstone_path.empty()) { diff --git a/debuggerd/open_files_list.cpp b/debuggerd/open_files_list.cpp new file mode 100644 index 000000000..5ef2abc2f --- /dev/null +++ b/debuggerd/open_files_list.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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. + */ + +#define LOG_TAG "DEBUG" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "open_files_list.h" + +#include "utility.h" + +void populate_open_files_list(pid_t pid, OpenFilesList* list) { + std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd"; + std::unique_ptr dir(opendir(fd_dir_name.c_str()), closedir); + if (dir == nullptr) { + ALOGE("failed to open directory %s: %s", fd_dir_name.c_str(), strerror(errno)); + return; + } + + struct dirent* de; + while ((de = readdir(dir.get())) != nullptr) { + if (*de->d_name == '.') { + continue; + } + + int fd = atoi(de->d_name); + std::string path = fd_dir_name + "/" + std::string(de->d_name); + std::string target; + if (android::base::Readlink(path, &target)) { + list->emplace_back(fd, target); + } else { + ALOGE("failed to readlink %s: %s", path.c_str(), strerror(errno)); + list->emplace_back(fd, "???"); + } + } +} + +void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix) { + for (auto& file : files) { + _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str()); + } +} + diff --git a/debuggerd/open_files_list.h b/debuggerd/open_files_list.h new file mode 100644 index 000000000..b37228d03 --- /dev/null +++ b/debuggerd/open_files_list.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 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. + */ + +#ifndef _DEBUGGERD_OPEN_FILES_LIST_H +#define _DEBUGGERD_OPEN_FILES_LIST_H + +#include + +#include +#include +#include + +#include "utility.h" + +typedef std::vector> OpenFilesList; + +/* Populates the given list with open files for the given process. */ +void populate_open_files_list(pid_t pid, OpenFilesList* list); + +/* Dumps the open files list to the log. */ +void dump_open_files_list_to_log(const OpenFilesList& files, log_t* log, const char* prefix); + +#endif // _DEBUGGERD_OPEN_FILES_LIST_H diff --git a/debuggerd/test/open_files_list_test.cpp b/debuggerd/test/open_files_list_test.cpp new file mode 100644 index 000000000..85e069510 --- /dev/null +++ b/debuggerd/test/open_files_list_test.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2016 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 +#include + +#include + +#include + +#include "android-base/test_utils.h" + +#include "open_files_list.h" + +// Check that we can produce a list of open files for the current process, and +// that it includes a known open file. +TEST(OpenFilesListTest, BasicTest) { + // Open a temporary file that we can check for in the list of open files. + TemporaryFile tf; + + // Get the list of open files for this process. + OpenFilesList list; + populate_open_files_list(getpid(), &list); + + // Verify our open file is in the list. + bool found = false; + for (auto& file : list) { + if (file.first == tf.fd) { + EXPECT_EQ(file.second, std::string(tf.path)); + found = true; + break; + } + } + EXPECT_TRUE(found); +} diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp index 1e474834e..03b3a17dd 100644 --- a/debuggerd/tombstone.cpp +++ b/debuggerd/tombstone.cpp @@ -46,6 +46,7 @@ #include "backtrace.h" #include "elf_utils.h" #include "machine.h" +#include "open_files_list.h" #include "tombstone.h" #define STACK_WORDS 16 @@ -620,7 +621,8 @@ static void dump_logs(log_t* log, pid_t pid, unsigned int tail) { } // Dumps all information about the specified pid to the tombstone. -static void dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid, +static void dump_crash(log_t* log, BacktraceMap* map, + const OpenFilesList& open_files, pid_t pid, pid_t tid, const std::set& siblings, uintptr_t abort_msg_address) { // don't copy log messages to tombstone unless this is a dev device bool want_logs = __android_log_is_debuggable(); @@ -639,6 +641,9 @@ static void dump_crash(log_t* log, BacktraceMap* map, pid_t pid, pid_t tid, } } + _LOG(log, logtype::OPEN_FILES, "\nopen files:\n"); + dump_open_files_list_to_log(open_files, log, " "); + if (want_logs) { dump_logs(log, pid, 0); } @@ -697,7 +702,8 @@ int open_tombstone(std::string* out_path) { return fd; } -void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid, +void engrave_tombstone(int tombstone_fd, BacktraceMap* map, + const OpenFilesList& open_files, pid_t pid, pid_t tid, const std::set& siblings, uintptr_t abort_msg_address, std::string* amfd_data) { log_t log; @@ -711,5 +717,5 @@ void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid log.tfd = tombstone_fd; log.amfd_data = amfd_data; - dump_crash(&log, map, pid, tid, siblings, abort_msg_address); + dump_crash(&log, map, open_files, pid, tid, siblings, abort_msg_address); } diff --git a/debuggerd/tombstone.h b/debuggerd/tombstone.h index e1c39c54e..126f80401 100644 --- a/debuggerd/tombstone.h +++ b/debuggerd/tombstone.h @@ -32,7 +32,8 @@ class BacktraceMap; int open_tombstone(std::string* path); /* Creates a tombstone file and writes the crash dump to it. */ -void engrave_tombstone(int tombstone_fd, BacktraceMap* map, pid_t pid, pid_t tid, +void engrave_tombstone(int tombstone_fd, BacktraceMap* map, + const OpenFilesList& open_files, pid_t pid, pid_t tid, const std::set& siblings, uintptr_t abort_msg_address, std::string* amfd_data); diff --git a/debuggerd/utility.h b/debuggerd/utility.h index d820f0f91..f7a3f731b 100644 --- a/debuggerd/utility.h +++ b/debuggerd/utility.h @@ -70,7 +70,8 @@ enum logtype { MAPS, MEMORY, STACK, - LOGS + LOGS, + OPEN_FILES }; // Log information onto the tombstone.