| // 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> |
| |
| 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 |
| |
| inspector_dso_print_list(output, dso.info); |
| fprintf(output, "arch: %s\n", arch); |
| inspector_print_backtrace(output, process->get(), entry.thread.get(), dso.info, pc, sp, fp, |
| true); |
| |
| 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 |