blob: 2bc1275eed4bead80e3bce8dd25e0558064009b0 [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_attach.h"
#include "src/developer/debug/zxdb/client/filter.h"
#include "src/developer/debug/zxdb/client/job.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/console/command.h"
#include "src/developer/debug/zxdb/console/command_utils.h"
#include "src/developer/debug/zxdb/console/console.h"
#include "src/developer/debug/zxdb/console/output_buffer.h"
#include "src/developer/debug/zxdb/console/string_util.h"
#include "src/developer/debug/zxdb/console/verbs.h"
namespace zxdb {
namespace {
const char kAttachShortHelp[] = "attach: Attach to a running process/job.";
const char kAttachHelp[] =
R"(attach <what>
Attaches to a current or future process.
Atttaching to a specific process
To attach to a specific process, supply the process' koid (process ID).
For example:
attach 12345
Use the "ps" command to view the active processes, their names, and koids.
Attaching to processes by name
Non-numeric arguments will be interpreted as a filter. A filter is a substring
that matches any part of the process name. The filter "t" will match any
process with the letter "t" in its name. Filters are not regular expressions.
Filters are applied to processes launched in jobs the debugger is attached to,
both current processes and future ones.
More on jobs:
See the currently attached jobs with the "job" command.
Attach to a new job with the "attach-job" command.
More on filters:
See the current filters with the "filter" command.
Delete a filter with "filter [X] rm" where X is the filter index from the
"filter" list. If no filter index is provided, the current filter will be
deleted.
Change a filter's pattern with "filter [X] set pattern = <newvalue>".
Attach to all processes in a job with "job attach *". Note that * is a
special string for filters, regular expressions are not supported.
If a job prefix is specified, only processes launched in that job matching the
pattern will be attached to:
job attach foo // Uses the current job context.
job 2 attach foo // Specifies job context #2.
If you have a specific job koid (12345) and want to watch "foo" processes in
it, a faster way is:
attach-job 12345 foo
Examples
attach 2371
Attaches to the process with koid 2371.
process 4 attach 2371
Attaches process context 4 to the process with koid 2371.
attach foobar
Attaches to any process that spawns under any job the debugger is attached
to with "foobar" in the name.
job 3 attach foobar
Attaches to any process that spawns under job 3 with "foobar" in the
name.
)";
bool HasAttachedJob(const System* system) {
for (const Job* job : system->GetJobs()) {
if (job->state() == Job::State::kAttached)
return true;
}
return false;
}
Err RunVerbAttach(ConsoleContext* context, const Command& cmd, CommandCallback callback) {
// Only a process can be attached.
if (Err err = cmd.ValidateNouns({Noun::kProcess, Noun::kJob}); err.has_error())
return err;
uint64_t koid = 0;
if (ReadUint64Arg(cmd, 0, "process koid", &koid).ok()) {
// Check for duplicate koids before doing anything else to avoid creating a container target
// in this case. It's easy to hit enter twice which will cause a duplicate attach. The
// duplicate target is the only reason to check here, the attach will fail later if there's
// a duplicate (say, created in a race condition).
if (context->session()->system().ProcessFromKoid(koid))
return Err("Process " + std::to_string(koid) + " is already being debugged.");
// Attach to a process by KOID.
auto err_or_target = GetRunnableTarget(context, cmd);
if (err_or_target.has_error())
return err_or_target.err();
err_or_target.value()->Attach(
koid, [callback = std::move(callback)](fxl::WeakPtr<Target> target, const Err& err,
uint64_t timestamp) mutable {
// Don't display a message on success because the ConsoleContext will print the new
// process information when it's detected.
ProcessCommandCallback(target, false, err, std::move(callback));
});
return Err();
}
// Not a number, make a filter instead. This only supports only "job" nouns.
if (cmd.ValidateNouns({Noun::kJob}).has_error()) {
return Err(
"Attaching by process name (a non-numeric argument)\nonly supports the \"job\" noun.");
}
if (cmd.args().size() != 1)
return Err("Wrong number of arguments to attach.");
Job* job = cmd.HasNoun(Noun::kJob) && cmd.job() ? cmd.job() : nullptr;
std::string pattern = cmd.args()[0];
if (!job && pattern == Filter::kAllProcessesPattern) {
// Bad things happen if we try to attach to all processes in the system, try to make this
// more difficult by preventing attaching to * with no specific job.
return Err("Use a specific job (\"job 3 attach *\") when attaching to all processes.");
}
// Display a warning if there are no attached jobs. The debugger tries to attach to the root job
// by default but if this fails (say there is more than one debug agent), attach will surprisingly
// fail.
if (!HasAttachedJob(&context->session()->system())) {
OutputBuffer warning;
warning.Append(Syntax::kWarning, GetExclamation());
warning.Append(
" There are currently no attached jobs. This could be because you\n"
"haven't attached to any, or because auto-attaching to the default jobs\n"
"failed (this can happen if there are more than one debug agents running).\n"
"Since attaching by name only applies to attached jobs, nothing will happen\n"
"until you attach to a job (\"attach-job <job-koid>\").\n\n");
Console::get()->Output(warning);
}
if (pattern.size() > kZirconMaxNameLength) {
Console::get()->Output(OutputBuffer(
Syntax::kWarning,
"The filter is trimmed to " + std::to_string(kZirconMaxNameLength) +
" characters because it's the maximum length for a process name in Zircon."));
pattern.resize(kZirconMaxNameLength);
}
Filter* filter = context->session()->system().CreateNewFilter();
filter->SetJob(job);
filter->SetPattern(pattern);
context->SetActiveFilter(filter);
// This doesn't use the default filter formatting to try to make it friendlier for people
// that are less familiar with the debugger and might be unsure what's happening (this is normally
// one of the first things people do in the debugger. The filter number is usually not relevant
// anyway.
Console::get()->Output("Waiting for process matching \"" + pattern +
"\".\n"
"Type \"filter\" to see the current filters.");
if (callback) {
callback(Err());
}
return Err();
}
} // namespace
VerbRecord GetAttachVerbRecord() {
VerbRecord attach(&RunVerbAttach, {"attach"}, kAttachShortHelp, kAttachHelp,
CommandGroup::kProcess);
return attach;
}
} // namespace zxdb