| // 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); |
| } |
| } |