blob: 57f11d1bc5fafb068b8b29bed039816191ad17f5 [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_run.h"
#include <lib/syslog/cpp/macros.h>
#include "src/developer/debug/shared/zx_status.h"
#include "src/developer/debug/zxdb/client/remote_api.h"
#include "src/developer/debug/zxdb/client/session.h"
#include "src/developer/debug/zxdb/client/target.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 {
constexpr int kRunComponentSwitch = 1;
const char kRunShortHelp[] = "run / r: Run the program.";
const char kRunHelp[] =
R"(run [--component] [ <program name> <program args>* ]
Alias: "r"
Runs the program. With no arguments, "run" will run the binary stored in the
process context, if any. With an argument, the binary name will be set and
that binary will be run.
Why "run" is usually wrong
The following loader environments all have different capabilities (in order
from least capable to most capable):
The debugger's "run <file name>" command (base system process stuff).
The system console or "fx shell" (adds some libraries).
The base component environment via the shells run and the debuggers
"run -c <package url>" (adds component capabilities).
The test environment via "fx test".
The user environment when launched from a story (adds high-level
services like scenic).
This panoply of environments is why the debugger can't have a simple run
command that always works.
When the debugger launches a process or a component, that process or
component will have the same capabilities as the debug_agent running on the
system. Whether this is enough to run a specific process or component is
mostly accidental.
The only way to get the correct environment is to launch your process or
component in the way it expects and attach the debugger to it. Filters
allow you to attach to a new process as it's created to debug from the
beginning. A typical flow is:
# Register for the process name. Use the name that appears in "ps" for
# the process:
[zxdb] attach my_app_name
Waiting for process matching "my_app_name"
# Set a pending breakpoint to stop where you want:
[zxdb] break main
Breakpoint 1 (Software) on Global, Enabled, stop=All, @ main
Pending: No matches for location, it will be pending library loads.
# Launch your app like normal, the debugger should catch it:
Attached Process 1 [Running] koid=33213 debug_agent.cmx
🛑 on bp 1 main(…) main.cc:220
219 ...
220 int main(int argc, const char* argv[]) {
221 ...
Arguments
--component | -c
Run this program as a component. The program name should be a component
URL. In addition to the above-discussed limitations, the debugger must
currently be attached to the system root job.
Hints
By default "run" will run the active process context (create a new one with
"new" to run multiple programs at once). To run an explicit process context,
specify it explicitly: "process 2 run".
To see a list of available process contexts, type "process".
Examples
run
process 2 run
Runs a process that's already been configured with a binary name.
run /boot/bin/ps
run chrome --no-sandbox http://www.google.com/
Runs the given process.
)";
void LaunchComponent(const Command& cmd) {
debug_ipc::LaunchRequest request;
request.inferior_type = debug_ipc::InferiorType::kComponent;
request.argv = cmd.args();
auto launch_cb = [target = cmd.target()->GetWeakPtr()](const Err& err,
debug_ipc::LaunchReply reply) {
if (err.has_error()) {
Console::get()->Output(err);
return;
}
FX_DCHECK(reply.inferior_type == debug_ipc::InferiorType::kComponent)
<< "Expected Component, Got: " << debug_ipc::InferiorTypeToString(reply.inferior_type);
if (reply.status.has_error()) {
// TODO(donosoc): This should interpret the component termination reason values.
Console::get()->Output(Err("Could not start component %s: %s", reply.process_name.c_str(),
reply.status.message().c_str()));
return;
}
FX_DCHECK(target);
// We tell the session we will be expecting this component.
FX_DCHECK(reply.process_id == 0);
FX_DCHECK(reply.component_id != 0);
target->session()->ExpectComponent(reply.component_id);
};
cmd.target()->session()->remote_api()->Launch(std::move(request), std::move(launch_cb));
}
Err RunVerbRun(ConsoleContext* context, const Command& cmd, CommandCallback callback = nullptr) {
// Only a process can be run.
Err err = cmd.ValidateNouns({Noun::kProcess});
if (err.has_error())
return err;
// May need to create a new target.
auto err_or_target = GetRunnableTarget(context, cmd);
if (err_or_target.has_error())
return err_or_target.err();
Target* target = err_or_target.value();
// Output warning about this possibly not working.
OutputBuffer warning(Syntax::kWarning, GetExclamation());
warning.Append(
" Run won't work for many processes and components. "
"See \"help run\".\n");
Console::get()->Output(warning);
if (!cmd.HasSwitch(kRunComponentSwitch)) {
if (cmd.args().empty()) {
// Use the args already set on the target.
if (target->GetArgs().empty())
return Err("No program to run. Try \"run <program name>\".");
} else {
target->SetArgs(cmd.args());
}
target->Launch([callback = std::move(callback)](fxl::WeakPtr<Target> target, const Err& err,
uint64_t timestamp) mutable {
// The ConsoleContext displays messages for new processes, so don't display messages when
// successfully starting.
ProcessCommandCallback(target, false, err, std::move(callback));
});
} else {
LaunchComponent(cmd);
}
return Err();
}
} // namespace
VerbRecord GetRunVerbRecord() {
VerbRecord run(&RunVerbRun, {"run", "r"}, kRunShortHelp, kRunHelp, CommandGroup::kProcess);
run.switches.push_back(SwitchRecord(kRunComponentSwitch, false, "component", 'c'));
return run;
}
} // namespace zxdb