text: // 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 *<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
  