| // Copyright 2018 The Fuchsia Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "debug_info_retriever.h" | 
 |  | 
 | #include <lib/syslog/cpp/macros.h> | 
 |  | 
 | #include <list> | 
 |  | 
 | #include "inspector/inspector.h" | 
 |  | 
 | namespace component { | 
 |  | 
 | namespace { | 
 | struct thread_entry { | 
 |   zx_koid_t id; | 
 |   zx::thread thread; | 
 |   zx::suspend_token suspend_token; | 
 |   fbl::String name; | 
 | }; | 
 | }  // namespace | 
 |  | 
 | fbl::String DebugInfoRetriever::GetInfo(const zx::process* process, zx_koid_t* thread_ids, | 
 |                                         size_t num) { | 
 |   assert(process); | 
 |   zx_koid_t thread_ids_storage[kMaxThreads]; | 
 |  | 
 |   if (thread_ids == nullptr || num == 0) { | 
 |     return "ERROR (fxbug.dev/4687): Full thread dump disabled"; | 
 |     if (process->get_info(ZX_INFO_PROCESS_THREADS, &thread_ids_storage, | 
 |                           kMaxThreads * sizeof(zx_koid_t), &num, nullptr) != ZX_OK) { | 
 |       return "ERROR: failed to get threads for process"; | 
 |     }; | 
 |     thread_ids = thread_ids_storage; | 
 |   } | 
 |  | 
 |   std::list<thread_entry> threads; | 
 |   zx_status_t status; | 
 |  | 
 |   for (size_t i = 0; i < num; i++) { | 
 |     zx::thread thread; | 
 |     if (process->get_child(thread_ids[i], ZX_RIGHT_SAME_RIGHTS, &thread) == ZX_OK) { | 
 |       threads.push_back({.id = thread_ids[i], .thread = std::move(thread)}); | 
 |     }; | 
 |   } | 
 |  | 
 |   for (auto it = threads.begin(); it != threads.end();) { | 
 |     zx_info_thread_t thread_info; | 
 |     it->thread.get_info(ZX_INFO_THREAD, &thread_info, sizeof(thread_info), nullptr, nullptr); | 
 |  | 
 |     auto prev = it++; | 
 |     // All threads will resume when their suspend token goes out of scope. | 
 |     if ((status = prev->thread.suspend(&prev->suspend_token)) != ZX_OK) { | 
 |       FX_LOGS(INFO) << "Failed to suspend thread: " << status; | 
 |       threads.erase(prev); | 
 |     } | 
 |   } | 
 |  | 
 |   for (auto it = threads.begin(); it != threads.end();) { | 
 |     // Wait for the thread to actually get suspended, but also react if the | 
 |     // thread was terminated in between these operations. | 
 |     zx_signals_t signals = 0; | 
 |  | 
 |     auto prev = it++; | 
 |     if ((status = prev->thread.wait_one(ZX_THREAD_SUSPENDED | ZX_THREAD_TERMINATED, | 
 |                                         zx::deadline_after(zx::msec(100)), &signals)) != ZX_OK || | 
 |         (signals & ZX_THREAD_TERMINATED)) { | 
 |       FX_LOGS(INFO) << "Thread failed to suspend in time. Status: " << status | 
 |                     << ", signals: " << signals; | 
 |       threads.erase(prev); | 
 |     } | 
 |   } | 
 |  | 
 |   for (auto& entry : threads) { | 
 |     char temp_name[ZX_MAX_NAME_LEN]; | 
 |     entry.thread.get_property(ZX_PROP_NAME, &temp_name, ZX_MAX_NAME_LEN); | 
 |     entry.name = temp_name; | 
 |   } | 
 |  | 
 |   DsoListWrapper dso(*process); | 
 |   FILE* output = tmpfile(); | 
 |   if (output == nullptr) { | 
 |     FX_LOGS(ERROR) << "Failed to open tmpfile for output."; | 
 |     return ""; | 
 |   } | 
 |  | 
 |   for (const auto& entry : threads) { | 
 |     fprintf(output, "%s (%lu):\n", entry.name.c_str(), entry.id); | 
 |  | 
 |     zx_thread_state_general_regs_t regs; | 
 |     if ((status = inspector_read_general_regs(entry.thread.get(), ®s)) != ZX_OK) { | 
 |       fprintf(output, "ERROR: failed to read regs, code=%d", status); | 
 |       continue; | 
 |     }; | 
 |     inspector_print_general_regs(output, ®s, nullptr); | 
 |  | 
 |     // Get the program counter, stack, and frame pointers. | 
 |     zx_vaddr_t pc, sp, fp; | 
 |     const char* arch = "unknown"; | 
 | #if defined(__x86_64__) | 
 |     arch = "x86_64"; | 
 |     pc = regs.rip; | 
 |     sp = regs.rsp; | 
 |     fp = regs.rbp; | 
 | #elif defined(__aarch64__) | 
 |     arch = "aarch64"; | 
 |     pc = regs.pc; | 
 |     sp = regs.sp; | 
 |     fp = regs.r[29]; | 
 | #else | 
 |     fprintf(output, "unsupported architecture\n"); | 
 |     continue; | 
 | #endif | 
 |  | 
 |     fprintf(output, "arch: %s\n", arch); | 
 |     inspector_print_backtrace_markup(output, process->get(), entry.thread.get(), dso.info, pc, sp, | 
 |                                      fp); | 
 |  | 
 |     fprintf(output, "\n"); | 
 |   } | 
 |  | 
 |   rewind(output); | 
 |  | 
 |   std::vector<uint8_t> data; | 
 |   if (!files::ReadFileDescriptorToVector(fileno(output), &data)) { | 
 |     fclose(output); | 
 |     return "ERROR: Failed to read file contents"; | 
 |   } | 
 |  | 
 |   fclose(output); | 
 |   return fbl::String((char*)data.data(), data.size()); | 
 | } | 
 |  | 
 | }  // namespace component |