blob: 2bae0185f0a653d80d8c00838a86cfaf1342e068 [file] [log] [blame]
// Copyright 2020 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 "system_diagnostics_directory.h"
#include <lib/syslog/cpp/macros.h>
#include <algorithm>
#include <fs/pseudo_file.h>
#include "debug_info_retriever.h"
#include "lib/fit/promise.h"
#include "lib/inspect/cpp/inspector.h"
#include "lib/inspect/cpp/value_list.h"
#include "src/lib/fxl/strings/string_printf.h"
using fxl::StringPrintf;
namespace {
struct ThreadInfo {
zx_koid_t koid;
fbl::String name;
zx::thread thread;
int64_t runtime;
};
static constexpr size_t kMaxThreads = 2048;
const char* obj_type_get_name(zx_obj_type_t type) {
switch (type) {
case ZX_OBJ_TYPE_NONE:
return "none";
case ZX_OBJ_TYPE_PROCESS:
return "process";
case ZX_OBJ_TYPE_THREAD:
return "thread";
case ZX_OBJ_TYPE_VMO:
return "vmo";
case ZX_OBJ_TYPE_CHANNEL:
return "channel";
case ZX_OBJ_TYPE_EVENT:
return "event";
case ZX_OBJ_TYPE_PORT:
return "port";
case ZX_OBJ_TYPE_INTERRUPT:
return "interrupt";
case ZX_OBJ_TYPE_PCI_DEVICE:
return "pci_device";
case ZX_OBJ_TYPE_LOG:
return "log";
case ZX_OBJ_TYPE_SOCKET:
return "socket";
case ZX_OBJ_TYPE_RESOURCE:
return "resource";
case ZX_OBJ_TYPE_EVENTPAIR:
return "eventpair";
case ZX_OBJ_TYPE_JOB:
return "job";
case ZX_OBJ_TYPE_VMAR:
return "vmar";
case ZX_OBJ_TYPE_FIFO:
return "fifo";
case ZX_OBJ_TYPE_GUEST:
return "guest";
case ZX_OBJ_TYPE_VCPU:
return "vcpu";
case ZX_OBJ_TYPE_TIMER:
return "timer";
case ZX_OBJ_TYPE_IOMMU:
return "iommu";
case ZX_OBJ_TYPE_BTI:
return "bti";
case ZX_OBJ_TYPE_PROFILE:
return "profile";
default:
return "unknown";
}
}
zx_status_t GetProcessHandleStats(const zx::process* process,
zx_info_process_handle_stats_t* process_handle_stats) {
zx_status_t status = process->get_info(ZX_INFO_PROCESS_HANDLE_STATS, process_handle_stats,
sizeof(zx_info_process_handle_stats), nullptr, nullptr);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "zx_object_get_info failed, status: " << status;
return status;
}
return ZX_OK;
}
zx_status_t GetTaskStats(const zx::process* process, zx_info_task_stats_t* task_stats) {
zx_status_t status = process->get_info(ZX_INFO_TASK_STATS, task_stats,
sizeof(zx_info_task_stats_t), nullptr, nullptr);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "zx_object_get_info failed, status: " << status;
return status;
}
return ZX_OK;
}
zx_status_t GetThreadStats(zx_handle_t thread, zx_info_thread_stats_t* thread_stats) {
zx_status_t status = zx_object_get_info(thread, ZX_INFO_THREAD_STATS, thread_stats,
sizeof(zx_info_thread_stats_t), nullptr, nullptr);
if (status != ZX_OK) {
FX_LOGS(ERROR) << "zx_object_get_info failed, status: " << status << " thread: " << thread;
return status;
}
return ZX_OK;
}
void GetThreads(const zx::process* process, std::vector<ThreadInfo>* out) {
zx_koid_t thread_ids[kMaxThreads];
size_t num_ids;
if (process->get_info(ZX_INFO_PROCESS_THREADS, thread_ids, sizeof(zx_koid_t) * kMaxThreads,
&num_ids, nullptr) != ZX_OK) {
return;
}
for (size_t i = 0; i < num_ids; i++) {
zx::thread t;
char name[ZX_MAX_NAME_LEN];
zx_info_thread_stats_t stats = {};
if (process->get_child(thread_ids[i], ZX_RIGHT_SAME_RIGHTS, &t) != ZX_OK) {
return;
}
if (t.get_property(ZX_PROP_NAME, &name, ZX_MAX_NAME_LEN) != ZX_OK) {
return;
}
if (GetThreadStats(t.get(), &stats) != ZX_OK) {
return;
}
out->push_back(ThreadInfo{thread_ids[i], name, std::move(t), stats.total_runtime});
}
}
fit::promise<inspect::Inspector> PopulateThreadInspect(zx::process* process) {
// Make a 1mb buffer.
inspect::Inspector inspector(inspect::InspectSettings{.maximum_size = 1024 * 1024});
std::vector<ThreadInfo> threads;
threads.reserve(kMaxThreads);
GetThreads(process, &threads);
for (const auto& thread : threads) {
inspect::ValueList values;
auto koid_string = StringPrintf("%lu", thread.koid);
auto thread_obj = inspector.GetRoot().CreateChild(koid_string);
thread_obj.CreateString("name", thread.name.data(), &values);
thread_obj.CreateInt("total_runtime", thread.runtime, &values);
auto stack_obj = thread_obj.CreateChild("stack");
zx_koid_t koids[] = {thread.koid};
stack_obj.CreateString(
"dump",
StringPrintf("\n%s", component::DebugInfoRetriever::GetInfo(process, koids, 1).data()),
&values);
values.emplace(std::move(thread_obj));
values.emplace(std::move(stack_obj));
inspector.emplace(std::move(values));
}
return fit::make_ok_promise(std::move(inspector));
}
fit::promise<inspect::Inspector> PopulateMemoryInspect(zx::process* process) {
inspect::Inspector inspector;
zx_info_task_stats_t task_stats = {};
GetTaskStats(process, &task_stats);
inspector.GetRoot().CreateUint("mapped_bytes", task_stats.mem_mapped_bytes, &inspector);
inspector.GetRoot().CreateUint("private_bytes", task_stats.mem_private_bytes, &inspector);
inspector.GetRoot().CreateUint("shared_bytes", task_stats.mem_shared_bytes, &inspector);
inspector.GetRoot().CreateUint("scaled_shared_bytes", task_stats.mem_scaled_shared_bytes,
&inspector);
return fit::make_ok_promise(std::move(inspector));
}
} // namespace
namespace component {
SystemDiagnosticsDirectory::SystemDiagnosticsDirectory(zx::process process)
: process_(std::move(process)), inspector_() {
inspector_.GetRoot().CreateLazyNode(
"handle_count",
[this]() -> fit::promise<inspect::Inspector> {
inspect::Inspector inspector;
zx_info_process_handle_stats_t process_handle_stats;
if (GetProcessHandleStats(&process_, &process_handle_stats) != ZX_OK) {
return fit::make_result_promise<inspect::Inspector>(fit::error());
}
for (zx_obj_type_t obj_type = ZX_OBJ_TYPE_NONE; obj_type < ZX_OBJ_TYPE_UPPER_BOUND;
++obj_type) {
inspector.GetRoot().CreateUint(obj_type_get_name(obj_type),
process_handle_stats.handle_count[obj_type], &inspector);
}
return fit::make_ok_promise(std::move(inspector));
},
&inspector_);
inspector_.GetRoot().CreateLazyNode(
"threads", [this] { return PopulateThreadInspect(&process_); }, &inspector_);
inspector_.GetRoot().CreateLazyNode(
"memory", [this] { return PopulateMemoryInspect(&process_); }, &inspector_);
}
} // namespace component