blob: 26cbfa5a1e541af0d31f43ad450e797a5040a03e [file] [log] [blame]
// Copyright 2021 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 "src/devices/bin/driver_manager/debug_dump.h"
#include "src/devices/lib/bind/ffi_bindings.h"
namespace {
void DumpDevice(VmoWriter* vmo, const Device* dev, size_t indent) {
zx_koid_t pid = dev->host() ? dev->host()->koid() : 0;
if (pid == 0) {
vmo->Printf("%*s[%s]\n", (int)(indent * 3), "", dev->name().data());
} else {
vmo->Printf("%*s%c%s%c pid=%zu %s\n", (int)(indent * 3), "",
dev->flags & DEV_CTX_PROXY ? '<' : '[', dev->name().data(),
dev->flags & DEV_CTX_PROXY ? '>' : ']', pid, dev->libname().data());
}
if (dev->proxy()) {
indent++;
DumpDevice(vmo, dev->proxy().get(), indent);
}
if (dev->new_proxy()) {
indent++;
DumpDevice(vmo, dev->new_proxy().get(), indent);
}
for (const auto& child : dev->children()) {
DumpDevice(vmo, &child, indent + 1);
}
}
void DumpDriver(const Driver& drv, VmoWriter& writer) {
writer.Printf("Name : %s\n", drv.name.c_str());
writer.Printf("Driver : %s\n", !drv.libname.empty() ? drv.libname.c_str() : "(null)");
writer.Printf("Flags : %#08x\n", drv.flags);
writer.Printf("Bytecode Version : %u\n", drv.bytecode_version);
if (!drv.binding_size) {
return;
}
if (drv.bytecode_version == 1) {
auto* binding = std::get_if<std::unique_ptr<zx_bind_inst_t[]>>(&drv.binding);
if (!binding) {
return;
}
char line[256];
uint32_t count = drv.binding_size / static_cast<uint32_t>(sizeof(binding->get()[0]));
writer.Printf("Binding : %u instruction%s (%u bytes)\n", count, (count == 1) ? "" : "s",
drv.binding_size);
for (uint32_t i = 0; i < count; ++i) {
di_dump_bind_inst(&binding->get()[i], line, sizeof(line));
writer.Printf("[%u/%u]: %s\n", i + 1, count, line);
}
} else if (drv.bytecode_version == 2) {
auto* binding = std::get_if<std::unique_ptr<uint8_t[]>>(&drv.binding);
if (!binding) {
return;
}
writer.Printf("Bytecode (%u byte%s): ", drv.binding_size, (drv.binding_size == 1) ? "" : "s");
writer.Printf("%s", dump_bytecode(binding->get(), drv.binding_size));
writer.Printf("\n\n");
}
}
void DumpDeviceProps(VmoWriter* vmo, const Device* dev) {
if (dev->host()) {
vmo->Printf("Name [%s]%s%s%s\n", dev->name().data(), dev->libname().empty() ? "" : " Driver [",
dev->libname().empty() ? "" : dev->libname().data(),
dev->libname().empty() ? "" : "]");
vmo->Printf("Flags :%s%s%s%s%s%s%s\n", dev->flags & DEV_CTX_IMMORTAL ? " Immortal" : "",
dev->flags & DEV_CTX_MUST_ISOLATE ? " Isolate" : "",
dev->flags & DEV_CTX_MULTI_BIND ? " MultiBind" : "",
dev->flags & DEV_CTX_BOUND ? " Bound" : "",
dev->flags & DEV_CTX_BUS_DEVICE ? " Bus" : "",
(dev->state() == Device::State::kDead) ? " Dead" : "",
dev->flags & DEV_CTX_PROXY ? " Proxy" : "");
char a = (char)((dev->protocol_id() >> 24) & 0xFF);
char b = (char)((dev->protocol_id() >> 16) & 0xFF);
char c = (char)((dev->protocol_id() >> 8) & 0xFF);
char d = (char)(dev->protocol_id() & 0xFF);
vmo->Printf("ProtoId : '%c%c%c%c' %#08x(%u)\n", isprint(a) ? a : '.', isprint(b) ? b : '.',
isprint(c) ? c : '.', isprint(d) ? d : '.', dev->protocol_id(), dev->protocol_id());
const auto& props = dev->props();
vmo->Printf("%zu Propert%s\n", props.size(), props.size() == 1 ? "y" : "ies");
for (uint32_t i = 0; i < props.size(); ++i) {
const zx_device_prop_t* p = &props[i];
const char* param_name = di_bind_param_name(p->id);
if (param_name) {
vmo->Printf("[%2u/%2zu] : Value %#08x Id %s\n", i, props.size(), p->value, param_name);
} else {
vmo->Printf("[%2u/%2zu] : Value %#08x Id %#04hx\n", i, props.size(), p->value, p->id);
}
}
const auto& str_props = dev->str_props();
vmo->Printf("%zu String Propert%s\n", str_props.size(), str_props.size() == 1 ? "y" : "ies");
for (uint32_t i = 0; i < str_props.size(); ++i) {
const StrProperty* p = &str_props[i];
vmo->Printf("[%2u/%2zu] : %s=", i, str_props.size(), p->key.data());
std::visit(
[vmo](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, uint32_t>) {
vmo->Printf("%#08x\n", arg);
} else if constexpr (std::is_same_v<T, std::string>) {
vmo->Printf("\"%s\"\n", arg.data());
} else if constexpr (std::is_same_v<T, bool>) {
vmo->Printf("%s\n", arg ? "true" : "false");
} else {
vmo->Printf("(unknown value type!)\n");
}
},
p->value);
}
vmo->Printf("\n");
}
if (dev->proxy()) {
DumpDeviceProps(vmo, dev->proxy().get());
}
if (dev->new_proxy()) {
DumpDeviceProps(vmo, dev->new_proxy().get());
}
for (const auto& child : dev->children()) {
DumpDeviceProps(vmo, &child);
}
}
} // namespace
DebugDump::DebugDump(Coordinator* coordinator) : coordinator_(coordinator) {}
DebugDump::~DebugDump() {}
void DebugDump::DumpTree(DumpTreeRequestView request, DumpTreeCompleter::Sync& completer) {
VmoWriter writer{std::move(request->output)};
DumpState(&writer);
completer.Reply(writer.status(), writer.written(), writer.available());
}
void DebugDump::DumpDrivers(DumpDriversRequestView request, DumpDriversCompleter::Sync& completer) {
VmoWriter writer{std::move(request->output)};
for (const auto& drv : coordinator_->drivers()) {
DumpDriver(drv, writer);
}
auto drivers = coordinator_->driver_loader().GetAllDriverIndexDrivers();
for (const auto& drv : drivers) {
DumpDriver(*drv, writer);
}
completer.Reply(writer.status(), writer.written(), writer.available());
}
void DebugDump::DumpBindingProperties(DumpBindingPropertiesRequestView request,
DumpBindingPropertiesCompleter::Sync& completer) {
VmoWriter writer{std::move(request->output)};
DumpDeviceProps(&writer, coordinator_->root_device().get());
if (coordinator_->sys_device()) {
DumpDeviceProps(&writer, coordinator_->sys_device().get());
}
completer.Reply(writer.status(), writer.written(), writer.available());
}
void DebugDump::DumpState(VmoWriter* vmo) const {
DumpDevice(vmo, coordinator_->root_device().get(), 0);
if (coordinator_->sys_device()) {
DumpDevice(vmo, coordinator_->sys_device().get(), 1);
}
}