| // Copyright 2020 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_until.h" |
| |
| #include "src/developer/debug/zxdb/client/frame.h" |
| #include "src/developer/debug/zxdb/client/process.h" |
| #include "src/developer/debug/zxdb/client/thread.h" |
| #include "src/developer/debug/zxdb/client/until_thread_controller.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/input_location_parser.h" |
| #include "src/developer/debug/zxdb/console/output_buffer.h" |
| #include "src/developer/debug/zxdb/console/verbs.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| const char kUntilShortHelp[] = "until / u: Runs a thread until a location is reached."; |
| const char kUntilHelp[] = |
| R"(until <location> |
| |
| Alias: "u" |
| |
| Continues execution of a thread or a process until a given location is |
| reached. You could think of this command as setting an implicit one-shot |
| breakpoint at the given location and continuing execution. |
| |
| Normally this operation will apply only to the current thread. To apply to |
| all threads in a process, use "process until" (see the examples below). |
| |
| See also "finish". |
| |
| Location arguments |
| |
| Current frame's address (no input) |
| until |
| |
| )" LOCATION_ARG_HELP("until") |
| R"( |
| Examples |
| |
| u |
| until |
| Runs until the current frame's location is hit again. This can be useful |
| if the current code is called in a loop to advance to the next iteration |
| of the current code. |
| |
| f 1 u |
| frame 1 until |
| Runs until the given frame's location is hit. Since frame 1 is |
| always the current function's calling frame, this command will normally |
| stop when the current function returns. The exception is if the code |
| in the calling function is called recursively from the current location, |
| in which case the next invocation will stop ("until" does not match |
| stack frames on break). See "finish" for a stack-aware version. |
| |
| u 24 |
| until 24 |
| Runs the current thread until line 24 of the current frame's file. |
| |
| until foo.cc:24 |
| Runs the current thread until the given file/line is reached. |
| |
| thread 2 until 24 |
| process 1 thread 2 until 24 |
| Runs the specified thread until line 24 is reached. When no filename is |
| given, the specified thread's currently selected frame will be used. |
| |
| u MyClass::MyFunc |
| until MyClass::MyFunc |
| Runs the current thread until the given function is called. |
| |
| pr u MyClass::MyFunc |
| process until MyClass::MyFunc |
| Continues all threads of the current process, stopping the next time any |
| of them call the function. |
| )"; |
| |
| Err RunVerbUntil(ConsoleContext* context, const Command& cmd) { |
| // Decode the location. |
| // |
| // The validation on this is a bit tricky. Most uses apply to the current thread and take some |
| // implicit information from the current frame (which requires the thread be stopped). But when |
| // doing a process-wide one, don't require a currently stopped thread unless it's required to |
| // compute the location. |
| std::vector<InputLocation> locations; |
| if (cmd.args().empty()) { |
| // No args means use the current location. |
| if (!cmd.frame()) { |
| return Err(ErrType::kInput, "There isn't a current frame to take the location from."); |
| } |
| locations.emplace_back(cmd.frame()->GetAddress()); |
| } else if (cmd.args().size() == 1) { |
| // One arg = normal location (this function can handle null frames). |
| if (Err err = ParseLocalInputLocation(cmd.frame(), cmd.args()[0], &locations); err.has_error()) |
| return err; |
| } else { |
| return Err(ErrType::kInput, |
| "Expecting zero or one arg for the location.\n" |
| "Formats: <function>, <file>:<line#>, <line#>, or 0x<address>"); |
| } |
| |
| auto callback = [](const Err& err) { |
| if (err.has_error()) |
| Console::get()->Output(err); |
| }; |
| |
| // Dispatch the request. |
| if (cmd.HasNoun(Noun::kProcess) && !cmd.HasNoun(Noun::kThread) && !cmd.HasNoun(Noun::kFrame)) { |
| // Process-wide ("process until ..."). |
| if (Err err = AssertRunningTarget(context, "until", cmd.target()); err.has_error()) |
| return err; |
| cmd.target()->GetProcess()->ContinueUntil(locations, callback); |
| } else { |
| // Thread-specific. |
| if (Err err = AssertStoppedThreadWithFrameCommand(context, cmd, "until"); err.has_error()) |
| return err; |
| |
| auto controller = std::make_unique<UntilThreadController>(std::move(locations)); |
| cmd.thread()->ContinueWith(std::move(controller), [](const Err& err) { |
| if (err.has_error()) |
| Console::get()->Output(err); |
| }); |
| } |
| return Err(); |
| } |
| |
| } // namespace |
| |
| VerbRecord GetUntilVerbRecord() { |
| return VerbRecord(&RunVerbUntil, &CompleteInputLocation, {"until", "u"}, kUntilShortHelp, |
| kUntilHelp, CommandGroup::kStep); |
| } |
| |
| } // namespace zxdb |