blob: 330b22f71fc1cad914b4cbec1a11044a3e8a53bf [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_detach.h"
#include "src/developer/debug/shared/zx_status.h"
#include "src/developer/debug/zxdb/client/job.h"
#include "src/developer/debug/zxdb/client/process.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/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/verbs.h"
#include "src/lib/fxl/strings/string_number_conversions.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
const char kDetachShortHelp[] = "detach: Detach from a process/job.";
const char kDetachHelp[] =
R"(detach [pid]
Detaches the debugger from a running process/job.
The process will continue running.
Arguments
pid
Detach from a process from pid or tell the agent to release an
uncoordinated process.
Normally the client and the agent running on Fuchsia are coordinated.
But there are some cases where the agent will be attached to some
processes that the client is not aware of. This can happen either when:
- You are reconnecting to a pre-running agent that was already attached.
- There are processes waiting on an exception (Just In Time Debugging).
In both cases, the client is unaware of these processes. Normally upon
connection zxdb will inform you of these processes and you can query
those with the "status" command.
The user can connect to those processes by issuing an attach command or
it can tell the agent to release them by issuing a detach command. The
client will first look for any attached processes it is aware of and if
not it will notify the agent to detach from this "unknown" processes.
Hints
By default the current process/job is detached.
To detach a different process/job prefix with "process N" or "job N"
Examples
detach
Detaches from the current process.
detach 1546
Send a "detach from process 1546" message to the agent. It is not necessary for the client to
be attached to this process.
job detach
Detaches from the current job.
process 4 detach
Detaches from process context 4.
job 3 detach
Detaches from job context 3.
)";
// Returns nullptr if there is no target attached to |process_koid|.
Target* SearchForAttachedTarget(ConsoleContext* context, uint64_t process_koid) {
if (process_koid == 0)
return nullptr;
Target* target = nullptr;
auto targets = context->session()->system().GetTargets();
for (auto* target_ptr : targets) {
auto* process = target_ptr->GetProcess();
if (!process || process->GetKoid() != process_koid)
continue;
// We found a target that matches, mark that one as the one that has to detach.
target = target_ptr;
break;
}
return target;
}
void SendExplicitDetachMessage(ConsoleContext* context, uint64_t process_koid) {
debug_ipc::DetachRequest request = {};
request.koid = process_koid;
request.type = debug_ipc::TaskType::kProcess;
context->session()->remote_api()->Detach(
request, [process_koid](const Err& err, debug_ipc::DetachReply reply) {
Console* console = Console::get();
if (err.has_error()) {
console->Output(err);
return;
}
if (reply.status.has_error()) {
console->Output(Err("Could not detach from process " + std::to_string(process_koid) +
": " + reply.status.message()));
return;
}
console->Output(fxl::StringPrintf("Successfully detached from %" PRIu64 ".", process_koid));
});
}
Err RunVerbDetach(ConsoleContext* context, const Command& cmd, CommandCallback callback) {
// Only a process can be detached.
if (Err err = cmd.ValidateNouns({Noun::kProcess, Noun::kJob}); err.has_error())
return err;
uint64_t process_koid = 0;
if (cmd.args().size() == 1) {
if (cmd.HasNoun(Noun::kProcess) || cmd.HasNoun(Noun::kJob))
return Err(ErrType::kInput, "You can only specify PIDs without context.");
process_koid = fxl::StringToNumber<uint64_t>(cmd.args()[0]);
} else if (cmd.args().size() > 1) {
return Err(ErrType::kInput, "\"detach\" takes at most 1 argument.");
}
if (cmd.HasNoun(Noun::kJob)) {
cmd.job()->Detach(
[callback = std::move(callback)](fxl::WeakPtr<Job> job, const Err& err) mutable {
JobCommandCallback("detach", job, true, err, std::move(callback));
});
return Err();
}
Target* target = SearchForAttachedTarget(context, process_koid);
// If there is no suitable target and the user specified a pid to detach to, it means we need to
// send an explicit detach message.
if (!target && process_koid != 0) {
SendExplicitDetachMessage(context, process_koid);
return Err();
}
// Here we either found an attached target or we use the context one (because the user did not
// specify a process koid to detach from).
if (!target)
target = cmd.target();
// Only print something when there was an error detaching. The console context will watch for
// Process destruction and print messages for each one in the success case.
target->Detach(
[callback = std::move(callback)](fxl::WeakPtr<Target> target, const Err& err) mutable {
// The ConsoleContext displays messages for stopped processes, so don't display messages
// when successfully detaching.
ProcessCommandCallback(target, false, err, std::move(callback));
});
return Err();
}
} // namespace
VerbRecord GetDetachVerbRecord() {
return VerbRecord(&RunVerbDetach, {"detach"}, kDetachShortHelp, kDetachHelp,
CommandGroup::kProcess);
}
} // namespace zxdb