blob: 92062457c2f8b1a68dcc76fdeabd10a48a9a983d [file] [log] [blame]
// 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_step.h"
#include "src/developer/debug/zxdb/client/frame.h"
#include "src/developer/debug/zxdb/client/step_into_thread_controller.h"
#include "src/developer/debug/zxdb/client/step_over_thread_controller.h"
#include "src/developer/debug/zxdb/client/thread.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"
namespace zxdb {
namespace {
constexpr int kStepIntoUnsymbolized = 1;
const char kStepShortHelp[] = "step / s: Step one source line, going into subroutines.";
const char kStepHelp[] =
R"(step [ <function-fragment> ]
Alias: "s"
When a thread is stopped, "step" will execute one source line and stop the
thread again. This will follow execution into subroutines. If the thread is
running it will issue an error.
By default, "step" will single-step the current thread. If a thread context
is given, the specified thread will be stepped. You can't step a process.
Other threads in the process will be unchanged so will remain running or
stopped.
If the thread ends up in a new function, that function's prologue will be
automatically skipped before the operation complets. An option to control
whether this happens can be added in the future if desired (bug 45309).
See also "stepi".
Stepping into specific functions
If provided, the parameter will specify a specific function call to step
into.
The string will be matched against the symbol names of subroutines called
directly from the current line. Execution will stop if the function name
contains this fragment, and automatically complete that function call
otherwise.
Arguments
--unsymbolized | -u
Force stepping into functions with no symbols. Normally "step" will
skip over library calls or thunks with no symbols. This option allows
one to step into these unsymbolized calls.
Examples
s
step
Step the current thread.
t 2 s
thread 2 step
Steps thread 2 in the current process.
s Pri
Steps into a function with the substring "Pri" anywhere in its name. If
you have a complex line such as:
Print(GetFoo(), std::string("bar");
The "s Pri" command will step over the GetFoo() and std::string() calls,
and leave execution at the beginning of the "Print" subroutine.
)";
Err RunVerbStep(ConsoleContext* context, const Command& cmd) {
if (Err err = AssertStoppedThreadCommand(context, cmd, true, "step"); err.has_error())
return err;
// All controllers do this on completion.
auto completion = [](const Err& err) {
if (err.has_error())
Console::get()->Output(err);
};
if (cmd.args().empty()) {
// Step for a single line.
auto controller = std::make_unique<StepIntoThreadController>(StepMode::kSourceLine);
controller->set_stop_on_no_symbols(cmd.HasSwitch(kStepIntoUnsymbolized));
cmd.thread()->ContinueWith(std::move(controller), std::move(completion));
} else if (cmd.args().size() == 1) {
// Step into a specific named subroutine. This uses the "step over" controller with a special
// condition.
if (cmd.HasSwitch(kStepIntoUnsymbolized)) {
return Err(
"The --unsymbolized switch is not compatible with a named "
"subroutine to step\ninto.");
}
auto controller = std::make_unique<StepOverThreadController>(StepMode::kSourceLine);
controller->set_subframe_should_stop_callback(
[substr = cmd.args()[0]](const Frame* frame) -> bool {
const Symbol* symbol = frame->GetLocation().symbol().Get();
if (!symbol)
return false; // Unsymbolized location, continue.
return symbol->GetFullName().find(substr) != std::string::npos;
});
cmd.thread()->ContinueWith(std::move(controller), std::move(completion));
} else {
return Err("Too many arguments for 'step'.");
}
return Err();
}
} // namespace
VerbRecord GetStepVerbRecord() {
SwitchRecord step_force(kStepIntoUnsymbolized, false, "unsymbolized", 'u');
VerbRecord step(&RunVerbStep, {"step", "s"}, kStepShortHelp, kStepHelp, CommandGroup::kStep,
SourceAffinity::kSource);
step.switches.push_back(step_force);
return step;
}
} // namespace zxdb