blob: c494a608e24070c1223e1c442c3e2a8eb2bba743 [file] [log] [blame]
// Copyright 2019 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/developer/debug/zxdb/console/commands/verb_status.h"
#include "lib/fit/defer.h"
#include "src/developer/debug/zxdb/client/remote_api.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/console/command.h"
#include "src/developer/debug/zxdb/console/console.h"
#include "src/developer/debug/zxdb/console/console_context.h"
#include "src/developer/debug/zxdb/console/format_filter.h"
#include "src/developer/debug/zxdb/console/format_job.h"
#include "src/developer/debug/zxdb/console/format_table.h"
#include "src/developer/debug/zxdb/console/format_target.h"
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
const char kStatusShortHelp[] = "status: Show debugger status.";
const char kStatusHelp[] = R"(status: Show debugger status.
Shows information on the current connection, process, thread, etc. along
with suggestions on what to do.
)";
Err RunVerbStatus(ConsoleContext* context, const Command& cmd, CommandCallback cb = nullptr) {
OutputBuffer out;
out.Append(GetConnectionStatus(context->session()));
out.Append("\n");
if (!context->session()->IsConnected()) {
Console::get()->Output(std::move(out));
return Err();
}
out.Append(GetJobStatus(context));
out.Append("\n");
out.Append(GetFilterStatus(context));
out.Append("\n");
out.Append(GetProcessStatus(context));
out.Append("\n");
// Attempt to get the agent's state.
context->session()->remote_api()->Status(
{}, [out = std::move(out), session = context->session()->GetWeakPtr(), cb = std::move(cb)](
const Err& err, debug_ipc::StatusReply reply) mutable {
// Call the callback if applicable.
Err return_err = err;
auto defer = fit::defer([&return_err, cb = std::move(cb)]() mutable {
if (cb)
cb(return_err);
});
if (!session) {
return_err = Err("No session found.");
return;
}
if (err.has_error()) {
return_err = err;
return;
}
// Append the Limbo state.
out.Append(GetLimboStatus(reply.limbo));
Console::get()->Output(std::move(out));
});
return Err();
}
OutputBuffer FormatProcessRecords(std::vector<debug_ipc::ProcessRecord> records, int indent) {
// Sort by name.
std::sort(records.begin(), records.end(),
[](const debug_ipc::ProcessRecord& lhs, const debug_ipc::ProcessRecord& rhs) {
return lhs.process_name < rhs.process_name;
});
std::vector<std::vector<std::string>> rows;
for (const debug_ipc::ProcessRecord& record : records) {
auto& row = rows.emplace_back();
row.reserve(4);
row.push_back(std::to_string(record.process_koid));
row.push_back(record.process_name);
}
OutputBuffer out;
FormatTable({ColSpec(Align::kRight, 0, "Koid", indent), ColSpec(Align::kLeft, 0, "Name")}, rows,
&out);
return out;
}
} // namespace
VerbRecord GetStatusVerbRecord() {
return VerbRecord(&RunVerbStatus, {"status", "stat", "wtf"}, kStatusShortHelp, kStatusHelp,
CommandGroup::kGeneral);
}
OutputBuffer GetConnectionStatus(const Session* session) {
OutputBuffer result;
result.Append(Syntax::kHeading, "Connection\n");
if (session->is_minidump()) {
result.Append(Syntax::kHeading, " Opened minidump: ");
result.Append(session->minidump_path() + "\n");
} else if (session->IsConnected()) {
result.Append(fxl::StringPrintf(" Connected to '%s' on port %d.\n",
session->connected_host().c_str(), session->connected_port()));
} else {
result.Append(
" Not connected. You can type these commands (see also \"help "
"<command>\").\n\n");
result.Append(Syntax::kHeading, " connect");
result.Append(R"( <host+port>
Connects to a debug agent running on a remote system on the given port.
However, most users will use a debug command from their environment to
automatically run the debug_agent and connect the zxdb frontend to it
(e.g. "ffx debug connect"). See your environment's documentation.
)");
result.Append(Syntax::kHeading, " opendump");
result.Append(R"( <local filename>
Opens a local file containing a crash dump for analysis.
)");
result.Append(Syntax::kHeading, " quit");
result.Append(R"(
Have a nice day.
)");
}
return result;
}
OutputBuffer GetFilterStatus(ConsoleContext* context) {
OutputBuffer result;
result.Append(Syntax::kHeading, "Process name filters\n");
result.Append(
" Filters match the names of processes launched in attached jobs and\n"
" automatically attaches to them.\n");
if (context->session()->system().GetFilters().empty()) {
result.Append("\n There are no filters. Use \"attach <process-name>\" to create one.\n");
} else {
result.Append(FormatFilterList(context, nullptr, 2));
}
return result;
}
OutputBuffer GetJobStatus(ConsoleContext* context) {
OutputBuffer result;
result.Append(Syntax::kHeading, "Jobs\n");
auto jobs = context->session()->system().GetJobs();
int attached_count = 0;
for (const auto& j : jobs) {
if (j->state() == Job::State::kAttached)
attached_count++;
}
result.Append(
fxl::StringPrintf(" Attached to %d job(s) (jobs are nodes in the Zircon process tree). "
"Processes\n launched in attached jobs can be caught and debugged via "
"\"attach\" filters.\n See \"help job\" and \"help attach\". The "
"debugger has these:\n",
attached_count));
result.Append(FormatJobList(context, 2));
return result;
}
OutputBuffer GetProcessStatus(ConsoleContext* context) {
OutputBuffer result;
result.Append(Syntax::kHeading, "Processes\n");
auto targets = context->session()->system().GetTargets();
int attached_count = 0;
for (const auto& t : targets) {
if (t->GetState() == Target::State::kRunning)
attached_count++;
}
result.Append(
fxl::StringPrintf(" Attached to %d process(es). The debugger has these:\n", attached_count));
result.Append(FormatTargetList(context, 2));
return result;
}
OutputBuffer GetLimboStatus(const std::vector<debug_ipc::ProcessRecord>& limbo) {
OutputBuffer result;
result.Append(Syntax::kHeading, "Processes waiting on exception\n");
if (limbo.empty()) {
result.Append(" No processes waiting on exception.");
} else {
result.Append(fxl::StringPrintf(" %zu process(es) waiting on exception. ", limbo.size()));
result.Append(
"Run \"attach <KOID>\" to load one into\n"
" zxdb or \"detach <KOID>\" to terminate them. See \"help jitd\" for more\n"
" information on Just-In-Time Debugging.\n");
result.Append(FormatProcessRecords(limbo, 4));
}
return result;
}
} // namespace zxdb