| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2019 The Android Open Source Project | 
 | 3 |  * All rights reserved. | 
 | 4 |  * | 
 | 5 |  * Redistribution and use in source and binary forms, with or without | 
 | 6 |  * modification, are permitted provided that the following conditions | 
 | 7 |  * are met: | 
 | 8 |  *  * Redistributions of source code must retain the above copyright | 
 | 9 |  *    notice, this list of conditions and the following disclaimer. | 
 | 10 |  *  * Redistributions in binary form must reproduce the above copyright | 
 | 11 |  *    notice, this list of conditions and the following disclaimer in | 
 | 12 |  *    the documentation and/or other materials provided with the | 
 | 13 |  *    distribution. | 
 | 14 |  * | 
 | 15 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 | 16 |  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 | 17 |  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 
 | 18 |  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 
 | 19 |  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 
 | 20 |  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 
 | 21 |  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS | 
 | 22 |  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | 
 | 23 |  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | 
 | 24 |  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | 
 | 25 |  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 
 | 26 |  * SUCH DAMAGE. | 
 | 27 |  */ | 
 | 28 |  | 
 | 29 | #include <inttypes.h> | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 30 | #include <stdint.h> | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 31 |  | 
 | 32 | #include <array> | 
 | 33 | #include <mutex> | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 34 | #include <string> | 
 | 35 | #include <string_view> | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 36 | #include <thread> | 
 | 37 | #include <utility> | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 38 | #include <vector> | 
 | 39 |  | 
| Josh Gao | 6f08866 | 2020-01-28 15:13:47 -0800 | [diff] [blame] | 40 | #include <android/fdsan.h> | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 41 | #include <android/set_abort_message.h> | 
| Josh Gao | 7596250 | 2020-01-28 13:24:33 -0800 | [diff] [blame] | 42 | #include <bionic/fdtrack.h> | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 43 |  | 
 | 44 | #include <android-base/no_destructor.h> | 
 | 45 | #include <android-base/thread_annotations.h> | 
 | 46 | #include <async_safe/log.h> | 
 | 47 | #include <bionic/reserved_signals.h> | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 48 | #include <unwindstack/Maps.h> | 
 | 49 | #include <unwindstack/Regs.h> | 
 | 50 | #include <unwindstack/RegsGetLocal.h> | 
 | 51 | #include <unwindstack/Unwinder.h> | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 52 |  | 
 | 53 | struct FdEntry { | 
 | 54 |   std::mutex mutex; | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 55 |   std::vector<unwindstack::FrameData> backtrace GUARDED_BY(mutex); | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 56 | }; | 
 | 57 |  | 
 | 58 | extern "C" void fdtrack_dump(); | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 59 | extern "C" void fdtrack_dump_fatal(); | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 60 |  | 
 | 61 | using fdtrack_callback_t = bool (*)(int fd, const char* const* function_names, | 
 | 62 |                                     const uint64_t* function_offsets, size_t count, void* arg); | 
 | 63 | extern "C" void fdtrack_iterate(fdtrack_callback_t callback, void* arg); | 
 | 64 |  | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 65 | static void fd_hook(android_fdtrack_event* event); | 
 | 66 |  | 
 | 67 | // Backtraces for the first 4k file descriptors ought to be enough to diagnose an fd leak. | 
 | 68 | static constexpr size_t kFdTableSize = 4096; | 
| Josh Gao | 55b91af | 2020-06-02 15:54:32 -0700 | [diff] [blame] | 69 |  | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 70 | // Only unwind up to 32 frames outside of libfdtrack.so. | 
 | 71 | static constexpr size_t kStackDepth = 32; | 
 | 72 |  | 
 | 73 | // Skip any initial frames from libfdtrack.so. | 
| Elliott Hughes | 263faa9 | 2022-12-12 21:40:31 +0000 | [diff] [blame] | 74 | // Also ignore frames from ART (http://b/236197847) because we'd rather spend | 
 | 75 | // our precious few frames on the actual Java calling code rather than the | 
 | 76 | // implementation of JNI! | 
 | 77 | static std::vector<std::string> kSkipFdtrackLib | 
 | 78 |     [[clang::no_destroy]] = {"libfdtrack.so", "libart.so"}; | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 79 |  | 
 | 80 | static bool installed = false; | 
| Josh Gao | 38d00b8 | 2020-04-21 17:05:32 -0700 | [diff] [blame] | 81 | static std::array<FdEntry, kFdTableSize> stack_traces [[clang::no_destroy]]; | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 82 | static unwindstack::LocalUpdatableMaps& Maps() { | 
 | 83 |   static android::base::NoDestructor<unwindstack::LocalUpdatableMaps> maps; | 
 | 84 |   return *maps.get(); | 
 | 85 | } | 
 | 86 | static std::shared_ptr<unwindstack::Memory>& ProcessMemory() { | 
 | 87 |   static android::base::NoDestructor<std::shared_ptr<unwindstack::Memory>> process_memory; | 
 | 88 |   return *process_memory.get(); | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 89 | } | 
 | 90 |  | 
 | 91 | __attribute__((constructor)) static void ctor() { | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 92 |   for (auto& entry : stack_traces) { | 
 | 93 |     entry.backtrace.reserve(kStackDepth); | 
 | 94 |   } | 
 | 95 |  | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 96 |   struct sigaction sa = {}; | 
 | 97 |   sa.sa_sigaction = [](int, siginfo_t* siginfo, void*) { | 
 | 98 |     if (siginfo->si_code == SI_QUEUE && siginfo->si_int == 1) { | 
 | 99 |       fdtrack_dump_fatal(); | 
 | 100 |     } else { | 
 | 101 |       fdtrack_dump(); | 
 | 102 |     } | 
 | 103 |   }; | 
 | 104 |   sa.sa_flags = SA_SIGINFO | SA_ONSTACK; | 
 | 105 |   sigaction(BIONIC_SIGNAL_FDTRACK, &sa, nullptr); | 
 | 106 |  | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 107 |   if (Maps().Parse()) { | 
 | 108 |     ProcessMemory() = unwindstack::Memory::CreateProcessMemoryThreadCached(getpid()); | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 109 |     android_fdtrack_hook_t expected = nullptr; | 
 | 110 |     installed = android_fdtrack_compare_exchange_hook(&expected, &fd_hook); | 
 | 111 |   } | 
| Josh Gao | dcc97c0 | 2020-12-09 14:01:13 -0800 | [diff] [blame] | 112 |  | 
 | 113 |   android_fdtrack_set_globally_enabled(true); | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 114 | } | 
 | 115 |  | 
 | 116 | __attribute__((destructor)) static void dtor() { | 
 | 117 |   if (installed) { | 
 | 118 |     android_fdtrack_hook_t expected = &fd_hook; | 
 | 119 |     android_fdtrack_compare_exchange_hook(&expected, nullptr); | 
 | 120 |   } | 
 | 121 | } | 
 | 122 |  | 
 | 123 | FdEntry* GetFdEntry(int fd) { | 
 | 124 |   if (fd >= 0 && fd < static_cast<int>(kFdTableSize)) { | 
 | 125 |     return &stack_traces[fd]; | 
 | 126 |   } | 
 | 127 |   return nullptr; | 
 | 128 | } | 
 | 129 |  | 
 | 130 | static void fd_hook(android_fdtrack_event* event) { | 
 | 131 |   if (event->type == ANDROID_FDTRACK_EVENT_TYPE_CREATE) { | 
 | 132 |     if (FdEntry* entry = GetFdEntry(event->fd); entry) { | 
 | 133 |       std::lock_guard<std::mutex> lock(entry->mutex); | 
 | 134 |       entry->backtrace.clear(); | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 135 |  | 
 | 136 |       std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal()); | 
 | 137 |       unwindstack::RegsGetLocal(regs.get()); | 
 | 138 |       unwindstack::Unwinder unwinder(kStackDepth, &Maps(), regs.get(), ProcessMemory()); | 
 | 139 |       unwinder.Unwind(&kSkipFdtrackLib); | 
 | 140 |       entry->backtrace = unwinder.ConsumeFrames(); | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 141 |     } | 
 | 142 |   } else if (event->type == ANDROID_FDTRACK_EVENT_TYPE_CLOSE) { | 
 | 143 |     if (FdEntry* entry = GetFdEntry(event->fd); entry) { | 
 | 144 |       std::lock_guard<std::mutex> lock(entry->mutex); | 
 | 145 |       entry->backtrace.clear(); | 
 | 146 |     } | 
 | 147 |   } | 
 | 148 | } | 
 | 149 |  | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 150 | void fdtrack_iterate(fdtrack_callback_t callback, void* arg) { | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 151 |   bool prev = android_fdtrack_set_enabled(false); | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 152 |  | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 153 |   for (int fd = 0; fd < static_cast<int>(stack_traces.size()); ++fd) { | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 154 |     const char* function_names[kStackDepth]; | 
 | 155 |     uint64_t function_offsets[kStackDepth]; | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 156 |     FdEntry* entry = GetFdEntry(fd); | 
 | 157 |     if (!entry) { | 
 | 158 |       continue; | 
 | 159 |     } | 
 | 160 |  | 
| Josh Gao | 50955c4 | 2020-01-28 14:10:19 -0800 | [diff] [blame] | 161 |     if (!entry->mutex.try_lock()) { | 
 | 162 |       async_safe_format_log(ANDROID_LOG_WARN, "fdtrack", "fd %d locked, skipping", fd); | 
 | 163 |       continue; | 
 | 164 |     } | 
 | 165 |  | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 166 |     if (entry->backtrace.empty()) { | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 167 |       entry->mutex.unlock(); | 
 | 168 |       continue; | 
 | 169 |     } else if (entry->backtrace.size() < 2) { | 
 | 170 |       async_safe_format_log(ANDROID_LOG_WARN, "fdtrack", "fd %d missing frames: size = %zu", fd, | 
 | 171 |                             entry->backtrace.size()); | 
 | 172 |  | 
 | 173 |       entry->mutex.unlock(); | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 174 |       continue; | 
 | 175 |     } | 
 | 176 |  | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 177 |     for (size_t i = 0; i < entry->backtrace.size(); ++i) { | 
 | 178 |       function_names[i] = entry->backtrace[i].function_name.c_str(); | 
 | 179 |       function_offsets[i] = entry->backtrace[i].function_offset; | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 180 |     } | 
| Josh Gao | 50955c4 | 2020-01-28 14:10:19 -0800 | [diff] [blame] | 181 |  | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 182 |     bool should_continue = | 
 | 183 |         callback(fd, function_names, function_offsets, entry->backtrace.size(), arg); | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 184 |  | 
| Josh Gao | 50955c4 | 2020-01-28 14:10:19 -0800 | [diff] [blame] | 185 |     entry->mutex.unlock(); | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 186 |  | 
 | 187 |     if (!should_continue) { | 
 | 188 |       break; | 
 | 189 |     } | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 190 |   } | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 191 |  | 
| Josh Gao | 9727192 | 2019-11-06 13:15:00 -0800 | [diff] [blame] | 192 |   android_fdtrack_set_enabled(prev); | 
 | 193 | } | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 194 |  | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 195 | static size_t hash_stack(const char* const* function_names, const uint64_t* function_offsets, | 
 | 196 |                          size_t stack_depth) { | 
 | 197 |   size_t hash = 0; | 
 | 198 |   for (size_t i = 0; i < stack_depth; ++i) { | 
 | 199 |     // To future maintainers: if a libc++ update ever makes this invalid, replace this with +. | 
 | 200 |     hash = std::__hash_combine(hash, std::hash<std::string_view>()(function_names[i])); | 
 | 201 |     hash = std::__hash_combine(hash, std::hash<uint64_t>()(function_offsets[i])); | 
 | 202 |   } | 
 | 203 |   return hash; | 
 | 204 | } | 
 | 205 |  | 
 | 206 | static void fdtrack_dump_impl(bool fatal) { | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 207 |   if (!installed) { | 
 | 208 |     async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fdtrack not installed"); | 
 | 209 |   } else { | 
 | 210 |     async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fdtrack dumping..."); | 
 | 211 |   } | 
 | 212 |  | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 213 |   // If we're aborting, identify the most common stack in the hopes that it's the culprit, | 
 | 214 |   // and emit that in the abort message so crash reporting can separate different fd leaks out. | 
 | 215 |   // This is horrible and quadratic, but we need to avoid allocation since this can happen in | 
 | 216 |   // response to a signal generated asynchronously. We're only going to dump 1k fds by default, | 
 | 217 |   // and we're about to blow up the entire system, so this isn't too expensive. | 
 | 218 |   struct StackInfo { | 
 | 219 |     size_t hash = 0; | 
 | 220 |     size_t count = 0; | 
 | 221 |  | 
 | 222 |     size_t stack_depth = 0; | 
| Christopher Ferris | 459eecb | 2022-01-07 13:38:10 -0800 | [diff] [blame] | 223 |     const char* function_names[kStackDepth]; | 
 | 224 |     uint64_t function_offsets[kStackDepth]; | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 225 |   }; | 
 | 226 |   struct StackList { | 
 | 227 |     size_t count = 0; | 
 | 228 |     std::array<StackInfo, 128> data; | 
 | 229 |   }; | 
 | 230 |   static StackList stacks; | 
 | 231 |  | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 232 |   fdtrack_iterate( | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 233 |       [](int fd, const char* const* function_names, const uint64_t* function_offsets, | 
 | 234 |          size_t stack_depth, void* stacks_ptr) { | 
 | 235 |         auto stacks = static_cast<StackList*>(stacks_ptr); | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 236 |         uint64_t fdsan_owner = android_fdsan_get_owner_tag(fd); | 
 | 237 |         if (fdsan_owner != 0) { | 
| Yuxian Xu | 3a5ddd7 | 2020-04-09 10:35:37 +0800 | [diff] [blame] | 238 |           async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d: (owner = 0x%" PRIx64 ")", fd, | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 239 |                                 fdsan_owner); | 
 | 240 |         } else { | 
 | 241 |           async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "fd %d: (unowned)", fd); | 
 | 242 |         } | 
 | 243 |  | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 244 |         for (size_t i = 0; i < stack_depth; ++i) { | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 245 |           async_safe_format_log(ANDROID_LOG_INFO, "fdtrack", "  %zu: %s+%" PRIu64, i, | 
 | 246 |                                 function_names[i], function_offsets[i]); | 
 | 247 |         } | 
 | 248 |  | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 249 |         if (stacks) { | 
 | 250 |           size_t hash = hash_stack(function_names, function_offsets, stack_depth); | 
 | 251 |           bool found_stack = false; | 
 | 252 |           for (size_t i = 0; i < stacks->count; ++i) { | 
 | 253 |             if (stacks->data[i].hash == hash) { | 
 | 254 |               ++stacks->data[i].count; | 
 | 255 |               found_stack = true; | 
 | 256 |               break; | 
 | 257 |             } | 
 | 258 |           } | 
 | 259 |  | 
 | 260 |           if (!found_stack) { | 
 | 261 |             if (stacks->count < stacks->data.size()) { | 
 | 262 |               auto& stack = stacks->data[stacks->count++]; | 
 | 263 |               stack.hash = hash; | 
 | 264 |               stack.count = 1; | 
 | 265 |               stack.stack_depth = stack_depth; | 
 | 266 |               for (size_t i = 0; i < stack_depth; ++i) { | 
 | 267 |                 stack.function_names[i] = function_names[i]; | 
 | 268 |                 stack.function_offsets[i] = function_offsets[i]; | 
 | 269 |               } | 
 | 270 |             } | 
 | 271 |           } | 
 | 272 |         } | 
 | 273 |  | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 274 |         return true; | 
 | 275 |       }, | 
| Josh Gao | 1cb3681 | 2021-03-11 21:11:37 -0800 | [diff] [blame] | 276 |       fatal ? &stacks : nullptr); | 
 | 277 |  | 
 | 278 |   if (fatal) { | 
 | 279 |     // Find the most common stack. | 
 | 280 |     size_t max = 0; | 
 | 281 |     StackInfo* stack = nullptr; | 
 | 282 |     for (size_t i = 0; i < stacks.count; ++i) { | 
 | 283 |       if (stacks.data[i].count > max) { | 
 | 284 |         stack = &stacks.data[i]; | 
 | 285 |         max = stack->count; | 
 | 286 |       } | 
 | 287 |     } | 
 | 288 |  | 
 | 289 |     static char buf[1024]; | 
 | 290 |  | 
 | 291 |     if (!stack) { | 
 | 292 |       async_safe_format_buffer(buf, sizeof(buf), | 
 | 293 |                                "aborting due to fd leak: failed to find most common stack"); | 
 | 294 |     } else { | 
 | 295 |       char* p = buf; | 
 | 296 |       p += async_safe_format_buffer(buf, sizeof(buf), | 
 | 297 |                                     "aborting due to fd leak: most common stack =\n"); | 
 | 298 |  | 
 | 299 |       for (size_t i = 0; i < stack->stack_depth; ++i) { | 
 | 300 |         ssize_t bytes_left = buf + sizeof(buf) - p; | 
 | 301 |         if (bytes_left > 0) { | 
 | 302 |           p += async_safe_format_buffer(p, buf + sizeof(buf) - p, "  %zu: %s+%" PRIu64 "\n", i, | 
 | 303 |                                         stack->function_names[i], stack->function_offsets[i]); | 
 | 304 |         } | 
 | 305 |       } | 
 | 306 |     } | 
 | 307 |  | 
 | 308 |     android_set_abort_message(buf); | 
 | 309 |  | 
 | 310 |     // Abort on a different thread to avoid ART dumping runtime stacks. | 
 | 311 |     std::thread([]() { abort(); }).join(); | 
 | 312 |   } | 
 | 313 | } | 
 | 314 |  | 
 | 315 | void fdtrack_dump() { | 
 | 316 |   fdtrack_dump_impl(false); | 
 | 317 | } | 
 | 318 |  | 
 | 319 | void fdtrack_dump_fatal() { | 
 | 320 |   fdtrack_dump_impl(true); | 
| Josh Gao | ad8f02d | 2020-01-28 13:54:00 -0800 | [diff] [blame] | 321 | } |