diff options
Diffstat (limited to 'debuggerd/handler/debuggerd_fallback.cpp')
-rw-r--r-- | debuggerd/handler/debuggerd_fallback.cpp | 100 |
1 files changed, 70 insertions, 30 deletions
diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp index c8b25ae2b..f61578068 100644 --- a/debuggerd/handler/debuggerd_fallback.cpp +++ b/debuggerd/handler/debuggerd_fallback.cpp @@ -1,17 +1,29 @@ /* - * Copyright 2017 The Android Open Source Project + * Copyright (C) 2017 The Android Open Source Project + * All rights reserved. * - * 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 + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. * - * 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. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. */ #include <dirent.h> @@ -73,13 +85,14 @@ static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { thread.registers.reset( unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext)); + // TODO: Create this once and store it in a global? + unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid()); // Do not use the thread cache here because it will call pthread_key_create // which doesn't work in linker code. See b/189803009. // Use a normal cached object because the process is stopped, and there // is no chance of data changing between reads. auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid()); - // TODO: Create this once and store it in a global? - unwindstack::UnwinderFromPid unwinder(kMaxFrames, getpid(), process_memory); + unwinder.SetProcessMemory(process_memory); dump_backtrace_thread(output_fd, &unwinder, thread); } __linker_disable_fallback_allocator(); @@ -97,6 +110,32 @@ static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t __linker_disable_fallback_allocator(); } +static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) { + pid_t current_tid = gettid(); + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid); + DIR* dir = opendir(buf); + + if (!dir) { + async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno)); + return; + } + + struct dirent* ent; + while ((ent = readdir(dir))) { + char* end; + long tid = strtol(ent->d_name, &end, 10); + if (end == ent->d_name || *end != '\0') { + continue; + } + + if (tid != current_tid) { + callback(tid, output_fd); + } + } + closedir(dir); +} + static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) { // Make sure the thread actually got the signal. struct pollfd pfd = { @@ -189,21 +228,21 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { } // Only allow one thread to perform a trace at a time. - static std::mutex trace_mutex; - if (!trace_mutex.try_lock()) { - async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed"); + static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER; + int ret = pthread_mutex_trylock(&trace_mutex); + if (ret != 0) { + async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s", + strerror(ret)); return; } - std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock); - // Fetch output fd from tombstoned. unique_fd tombstone_socket, output_fd; 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?"); - return; + goto exit; } dump_backtrace_header(output_fd.get()); @@ -212,15 +251,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { debuggerd_fallback_trace(output_fd.get(), ucontext); // Send a signal to all of our siblings, asking them to dump their stack. - pid_t current_tid = gettid(); - if (!iterate_tids(current_tid, [&output_fd](pid_t tid) { + iterate_siblings( + [](pid_t tid, int output_fd) { // Use a pipe, to be able to detect situations where the thread gracefully exits before // receiving our signal. unique_fd pipe_read, pipe_write; if (!Pipe(&pipe_read, &pipe_write)) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s", strerror(errno)); - return; + return false; } uint64_t expected = pack_thread_fd(-1, -1); @@ -230,7 +269,7 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "thread %d is already outputting to fd %d?", tid, fd); close(sent_fd); - return; + return false; } siginfo_t siginfo = {}; @@ -242,10 +281,10 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid, strerror(errno)); - return; + return false; } - bool success = forward_output(pipe_read.get(), output_fd.get(), tid); + bool success = forward_output(pipe_read.get(), output_fd, tid); if (!success) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "timeout expired while waiting for thread %d to dump", tid); @@ -261,14 +300,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { } } - return; - })) { - async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s", - current_tid, strerror(errno)); - } + return true; + }, + output_fd.get()); dump_backtrace_footer(output_fd.get()); tombstoned_notify_completion(tombstone_socket.get()); + +exit: + pthread_mutex_unlock(&trace_mutex); } static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) { |