blob: 9036e21f52f6a40307dfc57c465d841347c680a1 [file] [log] [blame]
// Copyright 2023 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_runtime/dispatcher.h"
namespace driver_runtime {
namespace {
const char* DispatcherStateToString(Dispatcher::DispatcherState state) {
switch (state) {
case Dispatcher::DispatcherState::kRunning:
return "running";
case Dispatcher::DispatcherState::kShuttingDown:
return "shutting down";
case Dispatcher::DispatcherState::kShutdown:
return "shutdown";
case Dispatcher::DispatcherState::kDestroyed:
return "destroyed";
}
return "unknown dispatcher state";
}
const char* BoolToString(bool b) { return b ? "true" : "false"; }
void OutputFormattedString(std::vector<std::string>* dump_out, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
int size = std::vsnprintf(nullptr, 0, fmt, args);
std::vector<char> buf(size + 1);
std::vsnprintf(buf.data(), buf.size(), fmt, args);
dump_out->push_back(&buf[0]);
va_end(args);
}
void AppendCallbackRequestAsTask(Dispatcher::DumpState* out_state,
CallbackRequest& callback_request) {
ZX_ASSERT(callback_request.request_type() == CallbackRequest::RequestType::kTask);
async_task_t* task = static_cast<async_task_t*>(callback_request.async_operation());
out_state->queued_tasks.push_back(Dispatcher::TaskDebugInfo{
.ptr = task,
.handler = task->handler,
.initiating_dispatcher = callback_request.initiating_dispatcher(),
.initiating_driver = callback_request.initiating_driver(),
});
}
} // namespace
void Dispatcher::DumpToString(std::vector<std::string>* dump_out) {
DumpState dump_state;
Dump(&dump_state);
FormatDump(&dump_state, dump_out);
}
void Dispatcher::Dump(DumpState* out_state) {
fbl::AutoLock lock(&callback_lock_);
out_state->running_dispatcher = driver_context::GetCurrentDispatcher();
out_state->running_driver = driver_context::GetCurrentDriver();
out_state->dispatcher_to_dump = this;
out_state->driver_owner =
out_state->dispatcher_to_dump ? out_state->dispatcher_to_dump->owner() : nullptr;
out_state->name = name_.ToString();
out_state->synchronized = !unsynchronized_;
out_state->allow_sync_calls = allow_sync_calls_;
out_state->state = state_;
out_state->queued_tasks.clear();
out_state->debug_stats = debug_stats_;
for (auto& callback_request : callback_queue_) {
if (callback_request.request_type() == CallbackRequest::RequestType::kTask) {
AppendCallbackRequestAsTask(out_state, callback_request);
}
}
for (auto& callback_request : shutdown_queue_) {
if (callback_request.request_type() == CallbackRequest::RequestType::kTask) {
AppendCallbackRequestAsTask(out_state, callback_request);
}
}
}
void Dispatcher::FormatDump(DumpState* state, std::vector<std::string>* dump_out) {
dump_out->clear();
OutputFormattedString(dump_out, "---- Runtime Dispatcher Dump Begin ----");
if (state->running_dispatcher) {
OutputFormattedString(
dump_out,
"The current thread is managed by the driver runtime, running dispatcher: %p (driver %p)",
state->running_dispatcher, state->running_driver);
} else {
OutputFormattedString(dump_out, "The current thread is not managed by the driver runtime.");
}
OutputFormattedString(dump_out, "Dispatcher to dump: %s (%p) (driver %p)", state->name.data(),
state->dispatcher_to_dump, state->driver_owner);
OutputFormattedString(dump_out, "Synchronized: %s", BoolToString(state->synchronized));
OutputFormattedString(dump_out, "Allow sync calls: %s", BoolToString(state->allow_sync_calls));
OutputFormattedString(dump_out, "State: %s", DispatcherStateToString(state->state));
OutputFormattedString(dump_out, "Processed %lu requests, %lu were inlined",
state->debug_stats.num_total_requests,
state->debug_stats.num_inlined_requests);
const auto& non_inlined_stats = state->debug_stats.non_inlined;
if (state->debug_stats.num_total_requests != state->debug_stats.num_inlined_requests) {
OutputFormattedString(dump_out, "Reasons why requests were not inlined:");
if (non_inlined_stats.allow_sync_calls > 0) {
OutputFormattedString(dump_out, "* The dispatcher has ALLOW_SYNC_CALL set: %lu times",
non_inlined_stats.allow_sync_calls);
}
if (non_inlined_stats.parallel_dispatch > 0) {
OutputFormattedString(dump_out,
"* Another thread was already dispatching a request: %lu times",
non_inlined_stats.parallel_dispatch);
}
if (non_inlined_stats.task > 0) {
OutputFormattedString(dump_out, "* The request was a task: %lu times",
non_inlined_stats.task);
}
if (non_inlined_stats.unknown_thread > 0) {
OutputFormattedString(dump_out, "* The request was queued from an unknown thread: %lu times",
non_inlined_stats.unknown_thread);
}
if (non_inlined_stats.reentrant > 0) {
OutputFormattedString(dump_out, "* The request would have been reentrant: %lu times",
non_inlined_stats.reentrant);
}
if (non_inlined_stats.channel_wait_not_yet_registered > 0) {
OutputFormattedString(
dump_out,
"* Channel wait was not yet registered when the message was received: %lu times",
non_inlined_stats.channel_wait_not_yet_registered);
}
}
if (state->queued_tasks.empty()) {
OutputFormattedString(dump_out, "No queued tasks");
} else {
OutputFormattedString(dump_out, "%lu Queued Tasks:", state->queued_tasks.size());
for (auto task : state->queued_tasks) {
OutputFormattedString(dump_out, "Task: %p Handler: %p", task.ptr, task.handler);
if (task.initiating_dispatcher) {
OutputFormattedString(dump_out, " - Task was queued by dispatcher %p (driver %p)",
task.initiating_dispatcher, task.initiating_driver);
} else {
OutputFormattedString(dump_out, " - Task was not queued from a runtime thread");
}
}
}
}
} // namespace driver_runtime