blob: 9351e11f5fa9f355b1115adc47fa21fa099abddd [file] [log] [blame] [edit]
// 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(), &regs)) != ZX_OK) {
fprintf(output, "ERROR: failed to read regs, code=%d", status);
continue;
};
inspector_print_general_regs(output, &regs, 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