| // 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 shell‘s run and the debugger’s |
| "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 |