blob: 161872397ab08573b47563cf596b5fa96ffb0a7c [file] [log] [blame]
// Copyright 2018 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 <inttypes.h>
#include "garnet/bin/zxdb/client/finish_thread_controller.h"
#include "garnet/bin/zxdb/client/frame.h"
#include "garnet/bin/zxdb/client/process.h"
#include "garnet/bin/zxdb/client/register.h"
#include "garnet/bin/zxdb/client/session.h"
#include "garnet/bin/zxdb/client/step_over_thread_controller.h"
#include "garnet/bin/zxdb/client/step_thread_controller.h"
#include "garnet/bin/zxdb/client/thread.h"
#include "garnet/bin/zxdb/client/until_thread_controller.h"
#include "garnet/bin/zxdb/common/err.h"
#include "garnet/bin/zxdb/console/command.h"
#include "garnet/bin/zxdb/console/command_utils.h"
#include "garnet/bin/zxdb/console/console.h"
#include "garnet/bin/zxdb/console/format_frame.h"
#include "garnet/bin/zxdb/console/format_register.h"
#include "garnet/bin/zxdb/console/format_table.h"
#include "garnet/bin/zxdb/console/format_value.h"
#include "garnet/bin/zxdb/console/format_value_process_context_impl.h"
#include "garnet/bin/zxdb/console/input_location_parser.h"
#include "garnet/bin/zxdb/console/output_buffer.h"
#include "garnet/bin/zxdb/console/verbs.h"
#include "garnet/bin/zxdb/expr/expr.h"
#include "garnet/bin/zxdb/expr/symbol_eval_context.h"
#include "garnet/bin/zxdb/symbols/code_block.h"
#include "garnet/bin/zxdb/symbols/function.h"
#include "garnet/bin/zxdb/symbols/location.h"
#include "garnet/bin/zxdb/symbols/variable.h"
#include "garnet/lib/debug_ipc/helper/message_loop.h"
#include "lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
constexpr int kStepIntoUnsymbolized = 1;
constexpr int kVerboseFormat = 2;
constexpr int kForceAllTypes = 3;
constexpr int kForceNumberChar = 4;
constexpr int kForceNumberSigned = 5;
constexpr int kForceNumberUnsigned = 6;
constexpr int kForceNumberHex = 7;
constexpr int kMaxArraySize = 8;
// If the system has at least one running process, returns true. If not,
// returns false and sets the err.
//
// When doing global things like System::Continue(), it will succeed if there
// are no running programs (it will successfully continue all 0 processes).
// This is confusing to the user so this function is used to check first.
bool VerifySystemHasRunningProcess(System* system, Err* err) {
for (const Target* target : system->GetTargets()) {
if (target->GetProcess())
return true;
}
*err = Err("No processes are running.");
return false;
}
// Populates the formatting options with the given command's switches.
Err GetFormatExprValueOptions(const Command& cmd,
FormatExprValueOptions* options) {
// Verbosity.
if (cmd.HasSwitch(kForceAllTypes))
options->verbosity = FormatExprValueOptions::Verbosity::kAllTypes;
else if (cmd.HasSwitch(kVerboseFormat))
options->verbosity = FormatExprValueOptions::Verbosity::kMedium;
else
options->verbosity = FormatExprValueOptions::Verbosity::kMinimal;
// Array size.
if (cmd.HasSwitch(kMaxArraySize)) {
int size = 0;
Err err = StringToInt(cmd.GetSwitchValue(kMaxArraySize), &size);
if (err.has_error())
return err;
options->max_array_size = static_cast<uint32_t>(size);
}
// Mapping from command-line parameter to format enum.
constexpr size_t kFormatCount = 4;
static constexpr std::pair<int, FormatExprValueOptions::NumFormat>
kFormats[kFormatCount] = {
{kForceNumberChar, FormatExprValueOptions::NumFormat::kChar},
{kForceNumberUnsigned, FormatExprValueOptions::NumFormat::kUnsigned},
{kForceNumberSigned, FormatExprValueOptions::NumFormat::kSigned},
{kForceNumberHex, FormatExprValueOptions::NumFormat::kHex}};
int num_type_overrides = 0;
for (const auto& cur : kFormats) {
if (cmd.HasSwitch(cur.first)) {
num_type_overrides++;
options->num_format = cur.second;
}
}
if (num_type_overrides > 1)
return Err("More than one type override (-c, -d, -u, -x) specified.");
return Err();
}
#define FORMAT_VALUE_SWITCHES \
" --max-array=<number>\n" \
" Specifies the maximum array size to print. By default this is\n" \
" 256. Specifying large values will slow things down and make the\n" \
" output harder to read, but the default is sometimes insufficient.\n" \
" This also applies to strings.\n" \
"\n" \
" -t\n" \
" --types\n" \
" Force type printing on. The type of every value printed will be\n" \
" explicitly shown. Implies -v.\n" \
"\n" \
" -v\n" \
" --verbose\n" \
" Don't elide type names. Show reference addresses and pointer\n" \
" types.\n" \
"\n" \
"Number formatting options\n" \
"\n" \
" Force numeric values to be of specific types with these options:\n" \
"\n" \
" -c Character\n" \
" -d Signed decimal\n" \
" -u Unsigned decimal\n" \
" -x Unsigned hexadecimal\n"
// backtrace -------------------------------------------------------------------
const char kBacktraceShortHelp[] = "backtrace / bt: Print a backtrace.";
const char kBacktraceHelp[] =
R"(backtrace / bt
Prints a backtrace of the selected thread. This is an alias for "frame -v".
To see less information, use "frame" or just "f".
Arguments
-t
--types
Include all type information for function parameters.
Examples
t 2 bt
thread 2 backtrace
)";
Err DoBacktrace(ConsoleContext* context, const Command& cmd) {
Err err = cmd.ValidateNouns({Noun::kProcess, Noun::kThread});
if (err.has_error())
return err;
if (!cmd.thread())
return Err("There is no thread to have frames.");
// TODO(brettw) this should share formatting options and parsing with the
// printing commands.
bool show_params = cmd.HasSwitch(kForceAllTypes);
OutputFrameList(cmd.thread(), show_params, true);
return Err();
}
// continue --------------------------------------------------------------------
const char kContinueShortHelp[] =
"continue / c: Continue a suspended thread or process.";
const char kContinueHelp[] =
R"(continue / c
When a thread is stopped at an exception or a breakpoint, "continue" will
continue execution.
See "pause" to stop a running thread or process.
The behavior will depend upon the context specified.
- By itself, "continue" will continue all threads of all processes that are
currently stopped.
- When a process is specified ("process 2 continue" for an explicit process
or "process continue" for the current process), only the threads in that
process will be continued. Other debugged processes currently stopped will
remain so.
- When a thread is specified ("thread 1 continue" for an explicit thread
or "thread continue" for the current thread), only that thread will be
continued. Other threads in that process and other processes currently
stopped will remain so.
TODO(brettw) it might be nice to have a --other flag that would continue
all threads other than the specified one (which the user might want to step
while everything else is going).
Examples
c
continue
Continue all processes and threads.
pr c
process continue
process 4 continue
Continue all threads of a process (the current process is implicit if
no process index is specified).
t c
thread continue
pr 2 t 4 c
process 2 thread 4 continue
Continue only one thread (the current process and thread are implicit
if no index is specified).
)";
Err DoContinue(ConsoleContext* context, const Command& cmd) {
Err err = cmd.ValidateNouns({Noun::kProcess, Noun::kThread});
if (err.has_error())
return err;
if (cmd.HasNoun(Noun::kThread)) {
cmd.thread()->Continue();
} else if (cmd.HasNoun(Noun::kProcess)) {
Process* process = cmd.target()->GetProcess();
if (!process)
return Err("Process not running, can't continue.");
process->Continue();
} else {
if (!VerifySystemHasRunningProcess(&context->session()->system(), &err))
return err;
context->session()->system().Continue();
}
return Err();
}
// finish ----------------------------------------------------------------------
const char kFinishShortHelp[] =
"finish / fi: Finish execution of a stack frame.";
const char kFinishHelp[] =
R"(finish / fi
Alias: "fi"
Resume thread execution until the selected stack frame returns. This means
that the current function call will execute normally until it finished.
See also "until".
Examples
fi
finish
Exit the currently selected stack frame (see "frame").
pr 1 t 4 fi
process 1 thead 4 finish
Applies "finish" to process 1, thread 4.
f 2 fi
frame 2 finish
Exit frame 2, leaving program execution in what was frame 3. Try also
"frame 3 until" which will do the same thing when the function is not
recursive.
)";
Err DoFinish(ConsoleContext* context, const Command& cmd) {
// This command allows "frame" which AssertStoppedThreadCommand doesn't,
// so pass "false" to disabled noun checking and manually check ourselves.
Err err = AssertStoppedThreadCommand(context, cmd, false, "finish");
if (err.has_error())
return err;
err = cmd.ValidateNouns({Noun::kProcess, Noun::kThread, Noun::kFrame});
if (err.has_error())
return err;
auto controller = std::make_unique<FinishThreadController>(
FinishThreadController::FromFrame(), cmd.frame());
cmd.thread()->ContinueWith(std::move(controller), [](const Err& err) {
if (err.has_error())
Console::get()->Output(err);
});
return Err();
}
// jump ------------------------------------------------------------------------
const char kJumpShortHelp[] =
"jump / jmp: Set the instruction pointer to a different address.";
const char kJumpHelp[] =
R"(jump <location>
Alias: "jmp"
Sets the instruction pointer of the thread to the given address. It does not
continue execution. You can "step" or "continue" from the new location.
You are responsible for what this means semantically since one can't
generally change the instruction flow and expect things to work.
Location arguments
)" LOCATION_ARG_HELP("jump");
Err DoJump(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadCommand(context, cmd, false, "jump");
if (err.has_error())
return err;
if (cmd.args().size() != 1)
return Err("The 'jump' command requires one argument for the location.");
InputLocation input_location;
err = ParseInputLocation(cmd.frame(), cmd.args()[0], &input_location);
if (err.has_error())
return err;
Location location;
err = ResolveUniqueInputLocation(cmd.target()->GetProcess()->GetSymbols(),
input_location, true, &location);
if (err.has_error())
return err;
cmd.thread()->JumpTo(
location.address(),
[thread = cmd.thread()->GetWeakPtr()](const Err& err) {
Console* console = Console::get();
if (err.has_error()) {
console->Output(err);
} else if (thread) {
// Reset the current stack frame to the top to reflect the location
// the user has just jumped to.
console->context().SetActiveFrameIdForThread(thread.get(), 0);
// Tell the user where they are.
console->context().OutputThreadContext(
thread.get(), debug_ipc::NotifyException::Type::kNone, {});
}
});
return Err();
}
// locals ----------------------------------------------------------------------
const char kLocalsShortHelp[] =
"locals: Print local variables and function args.";
const char kLocalsHelp[] =
R"(locals
Prints all local variables and the current function's arguments. By default
it will print the variables for the currently selected stack frame.
You can override the stack frame with the "frame" noun to get the locals
for any specific stack frame of thread.
Arguments
)" FORMAT_VALUE_SWITCHES
R"(
Examples
locals
Prints locals and args for the current stack frame.
f 4 locals
frame 4 locals
thread 2 frame 3 locals
Prints locals for a specific stack frame.
f 4 locals -t
Prints locals with types.
)";
Err DoLocals(ConsoleContext* context, const Command& cmd) {
// Don't have AssertStoppedThreadCommand check nouns because we additionally
// allow "frame", which we manually validate below.
Err err = AssertStoppedThreadCommand(context, cmd, false, "locals");
if (err.has_error())
return err;
err = cmd.ValidateNouns({Noun::kProcess, Noun::kThread, Noun::kFrame});
if (err.has_error())
return err;
if (!cmd.frame())
return Err("There isn't a current frame to read locals from.");
const Location& location = cmd.frame()->GetLocation();
if (!location.symbol())
return Err("There is no symbol information for the frame.");
const Function* function = location.symbol().Get()->AsFunction();
if (!function)
return Err("Symbols are corrupt.");
// Find the innermost lexical block for the current IP.
const CodeBlock* block = function->GetMostSpecificChild(
location.symbol_context(), location.address());
if (!block)
return Err("There is no symbol information for the current IP.");
// Walk upward in the hierarchy to collect local variables until hitting a
// function. Using the map allows collecting only the innermost version of
// a given name, and sorts them as we go.
std::map<std::string, const Variable*> vars;
while (block) {
for (const auto& lazy_var : block->variables()) {
const Variable* var = lazy_var.Get()->AsVariable();
if (!var)
continue; // Symbols are corrupt.
const std::string& name = var->GetAssignedName();
if (vars.find(name) == vars.end())
vars[name] = var; // New one.
}
if (block == function)
break;
block = block->parent().Get()->AsCodeBlock();
}
// Add function parameters. Don't overwrite existing names in case of
// duplicates to duplicate the shadowing rules of the language.
for (const auto& param : function->parameters()) {
const Variable* var = param.Get()->AsVariable();
if (!var)
continue; // Symbols are corrupt.
const std::string& name = var->GetAssignedName();
if (vars.find(name) == vars.end())
vars[name] = var; // New one.
}
if (vars.empty()) {
Console::get()->Output("No local variables in scope.");
return Err();
}
FormatExprValueOptions options;
err = GetFormatExprValueOptions(cmd, &options);
if (err.has_error())
return err;
auto helper = fxl::MakeRefCounted<FormatValue>(
std::make_unique<FormatValueProcessContextImpl>(cmd.target()));
for (const auto& pair : vars) {
helper->AppendVariable(location.symbol_context(),
cmd.frame()->GetSymbolDataProvider(), pair.second,
options);
helper->Append(OutputBuffer("\n"));
}
helper->Complete(
[helper](OutputBuffer out) { Console::get()->Output(std::move(out)); });
return Err();
}
// next ------------------------------------------------------------------------
const char kNextShortHelp[] = "next / n: Single-step over one source line.";
const char kNextHelp[] =
R"(next / n
When a thread is stopped, "next" will execute one source line, stepping over
subroutine call instructions, and stop the thread again. If the thread is
running it will issue an error.
By default, "next" will operate on the current thread. If a thread context
is given, the specified thread will be single-stepped. You can't single-step
a process.
See also "step" to step into subroutine calls or "nexti" to step machine
instructions.
Examples
n
next
Step the current thread.
t 2 n
thread 2 next
Steps thread 2 in the current process.
pr 3 n
process 3 next
Steps the current thread in process 3 (regardless of which process is
the current process).
pr 3 t 2 n
process 3 thread 2 next
Steps thread 2 in process 3.
)";
Err DoNext(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadCommand(context, cmd, true, "next");
if (err.has_error())
return err;
auto controller =
std::make_unique<StepOverThreadController>(StepMode::kSourceLine);
cmd.thread()->ContinueWith(std::move(controller), [](const Err& err) {
if (err.has_error())
Console::get()->Output(err);
});
return Err();
}
// nexti -----------------------------------------------------------------------
const char kNextiShortHelp[] =
"nexti / ni: Single-step over one machine instruction.";
const char kNextiHelp[] =
R"(nexti / ni
When a thread is stopped, "nexti" will execute one machine instruction,
stepping over subroutine call instructions, and stop the thread again.
If the thread is running it will issue an error.
Only machine call instructions ("call" on x86 and "bl" on ARM) will be
stepped over with this command. This is not the only way to do a subroutine
call, as code can manually set up a call frame and jump. These jumps will not
count as a call and this command will step into the resulting frame.
By default, "nexti" will operate on the current thread. If a thread context
is given, the specified thread will be single-stepped. You can't single-step
a process.
See also "stepi" to step into subroutine calls.
Examples
ni
nexti
Step the current thread.
t 2 ni
thread 2 nexti
Steps thread 2 in the current process.
pr 3 ni
process 3 nexti
Steps the current thread in process 3 (regardless of which process is
the current process).
pr 3 t 2 ni
process 3 thread 2 nexti
Steps thread 2 in process 3.
)";
Err DoNexti(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadCommand(context, cmd, true, "nexti");
if (err.has_error())
return err;
auto controller =
std::make_unique<StepOverThreadController>(StepMode::kInstruction);
cmd.thread()->ContinueWith(std::move(controller), [](const Err& err) {
if (err.has_error())
Console::get()->Output(err);
});
return Err();
}
// pause -----------------------------------------------------------------------
const char kPauseShortHelp[] = "pause / pa: Pause a thread or process.";
const char kPauseHelp[] =
R"(pause / pa
When a thread or process is running, "pause" will stop execution so state
can be inspected or the thread single-stepped.
See "continue" to resume a paused thread or process.
The behavior will depend upon the context specified.
- By itself, "pause" will pause all threads of all processes that are
currently running.
- When a process is specified ("process 2 pause" for an explicit process
or "process pause" for the current process), only the threads in that
process will be paused. Other debugged processes currently running will
remain so.
- When a thread is specified ("thread 1 pause" for an explicit thread
or "thread pause" for the current thread), only that thread will be
paused. Other threads in that process and other processes currently
running will remain so.
TODO(brettw) it might be nice to have a --other flag that would pause
all threads other than the specified one.
Examples
pa
pause
Pause all processes and threads.
pr pa
process pause
process 4 pause
Pause all threads of a process (the current process is implicit if
no process index is specified).
t pa
thread pause
pr 2 t 4 pa
process 2 thread 4 pause
Pause only one thread (the current process and thread are implicit
if no index is specified).
)";
Err DoPause(ConsoleContext* context, const Command& cmd) {
Err err = cmd.ValidateNouns({Noun::kProcess, Noun::kThread});
if (err.has_error())
return err;
if (cmd.HasNoun(Noun::kThread)) {
cmd.thread()->Pause();
} else if (cmd.HasNoun(Noun::kProcess)) {
Process* process = cmd.target()->GetProcess();
if (!process)
return Err("Process not running, can't pause.");
process->Pause();
} else {
if (!VerifySystemHasRunningProcess(&context->session()->system(), &err))
return err;
context->session()->system().Pause();
}
return Err();
}
// print -----------------------------------------------------------------------
const char kPrintShortHelp[] = "print / p: Print a variable or expression.";
const char kPrintHelp[] =
R"(print <expression>
Alias: p
Evaluates a simple expression or variable name and prints the result.
The expression is evaluated by default in the currently selected thread and
stack frame. You can override this with "frame <x> print ...".
Arguments
)" FORMAT_VALUE_SWITCHES
R"(
Expressions
The expression evaluator understands the following C/C++ things:
- Identifiers
- Struct and class member access: . ->
- Array access (for native arrays): [ <expression> ]
- Create or dereference pointers: & *
- Precedence: ( <expression> )
Not supported: function calls, overloaded operators, casting.
Examples
p foo
print foo
Print a variable
p *foo->bar
print &foo.bar[2]
Deal with structs and arrays.
f 2 p -t foo
frame 2 print -t foo
thread 1 frame 2 print -t foo
Print a variable with types in the context of a specific stack frame.
)";
Err DoPrint(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadCommand(context, cmd, false, "print");
if (err.has_error())
return err;
err = cmd.ValidateNouns({Noun::kProcess, Noun::kThread, Noun::kFrame});
if (err.has_error())
return err;
if (!cmd.frame())
return Err("There isn't a current frame for printing context.");
// This takes one expression that may have spaces, so concatenate everything
// the command parser has split apart back into one thing.
//
// If we run into limitations of this, we should add a "don't parse the args"
// flag to the command record.
std::string expr;
for (const auto& cur : cmd.args()) {
if (!expr.empty())
expr.push_back(' ');
expr += cur;
}
if (expr.empty())
return Err("Usage: print <expression>\nSee \"help print\" for more.");
FormatExprValueOptions options;
err = GetFormatExprValueOptions(cmd, &options);
if (err.has_error())
return err;
auto data_provider = cmd.frame()->GetSymbolDataProvider();
auto formatter = fxl::MakeRefCounted<FormatValue>(
std::make_unique<FormatValueProcessContextImpl>(cmd.target()));
EvalExpression(
expr, cmd.frame()->GetExprEvalContext(),
[formatter, options, data_provider](const Err& err, ExprValue value) {
if (err.has_error()) {
Console::get()->Output(err);
} else {
formatter->AppendValue(data_provider, value, options);
// Bind the formatter to keep it in scope across this
// async call.
formatter->Complete([formatter](OutputBuffer out) {
Console::get()->Output(std::move(out));
});
}
});
return Err();
}
// step ------------------------------------------------------------------------
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.
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 DoStep(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadCommand(context, cmd, true, "step");
if (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 over a single line.
auto controller =
std::make_unique<StepThreadController>(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();
}
// stepi -----------------------------------------------------------------------
const char kStepiShortHelp[] =
"stepi / si: Single-step a thread one machine instruction.";
const char kStepiHelp[] =
R"(stepi / si
When a thread is stopped, "stepi" will execute one machine instruction and
stop the thread again. If the thread is running it will issue an error.
By default, "stepi" will single-step the current thread. If a thread context
is given, the specified thread will be single-stepped. You can't single-step
a process.
See also "nexti" to step over subroutine calls.
Examples
si
stepi
Step the current thread.
t 2 si
thread 2 stepi
Steps thread 2 in the current process.
pr 3 si
process 3 stepi
Steps the current thread in process 3 (regardless of which process is
the current process).
pr 3 t 2 si
process 3 thread 2 stepi
Steps thread 2 in process 3.
)";
Err DoStepi(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadCommand(context, cmd, true, "stepi");
if (err.has_error())
return err;
cmd.thread()->StepInstruction();
return Err();
}
// regs ------------------------------------------------------------------------
using debug_ipc::RegisterCategory;
const char kRegsShortHelp[] =
"regs / rg: Show the current registers for a thread.";
const char kRegsHelp[] =
R"(regs [(--category|-c)=<category>] [(--extended|-e)] [<regexp>]
Alias: "rg"
Shows the current registers for a thread. The thread must be stopped.
By default the general purpose registers will be shown, but more can be
configures through switches.
NOTE: The values are displayed in the endianess of the target architecture.
The interpretation of which bits are the MSB will vary across different
endianess.
Arguments
--category=<category> | -c <category>
Which categories if registers to show.
The following options can be set:
- general: Show general purpose registers.
- fp: Show floating point registers.
- vector: Show vector registers.
- debug: Show debug registers (eg. The DR registers on x86).
- all: Show all the categories available.
NOTE: not all categories exist within all architectures. For example,
ARM64's fp category doesn't have any registers.
--extended | -e
Enables more verbose flag decoding. This will enable more information
that is not normally useful for everyday debugging. This includes
information such as the system level flags within the RFLAGS register for
x86.
<regexp>
Case insensitive regular expression. Any register that matches will be
shown. Uses POSIX Basic Regular Expression syntax. If not specified, it
will match all registers.
Examples
regs
thread 4 regs --category=vector
process 2 thread 1 regs -c all v*
)";
// Switches
constexpr int kRegsCategoriesSwitch = 1;
constexpr int kRegsExtendedSwitch = 2;
void OnRegsComplete(const Err& cmd_err, const RegisterSet& register_set,
FormatRegisterOptions options) {
Console* console = Console::get();
if (cmd_err.has_error()) {
console->Output(cmd_err);
return;
}
options.arch = register_set.arch();
FilteredRegisterSet filtered_set;
Err err = FilterRegisters(options, register_set, &filtered_set);
if (!err.ok()) {
console->Output(err);
return;
}
OutputBuffer out;
err = FormatRegisters(options, filtered_set, &out);
if (!err.ok()) {
console->Output(err);
return;
}
console->Output(out);
}
Err DoRegs(ConsoleContext* context, const Command& cmd) {
Err err = AssertStoppedThreadCommand(context, cmd, true, "regs");
if (err.has_error())
return err;
// When empty, we print all the registers.
std::string regex_filter;
if (!cmd.args().empty()) {
// We expect only one name.
if (cmd.args().size() > 1u) {
return Err("Only one register regular expression filter expected.");
}
regex_filter = cmd.args().front();
}
// General purpose are the default.
std::vector<RegisterCategory::Type> cats_to_show = {
RegisterCategory::Type::kGeneral};
if (cmd.HasSwitch(kRegsCategoriesSwitch)) {
auto option = cmd.GetSwitchValue(kRegsCategoriesSwitch);
if (option == "all") {
cats_to_show = {
debug_ipc::RegisterCategory::Type::kGeneral,
debug_ipc::RegisterCategory::Type::kFP,
debug_ipc::RegisterCategory::Type::kVector,
debug_ipc::RegisterCategory::Type::kDebug,
};
} else if (option == "general") {
cats_to_show = {RegisterCategory::Type::kGeneral};
} else if (option == "fp") {
cats_to_show = {RegisterCategory::Type::kFP};
} else if (option == "vector") {
cats_to_show = {RegisterCategory::Type::kVector};
} else if (option == "debug") {
cats_to_show = {RegisterCategory::Type::kDebug};
} else {
return Err(fxl::StringPrintf("Unknown category: %s", option.c_str()));
}
}
// Parse the switches
FormatRegisterOptions options;
options.categories = cats_to_show;
options.extended = cmd.HasSwitch(kRegsExtendedSwitch);
options.filter_regexp = std::move(regex_filter);
// We pass the given register name to the callback
auto regs_cb = [options = std::move(options)](const Err& err,
const RegisterSet& registers) {
OnRegsComplete(err, registers, std::move(options));
};
cmd.thread()->ReadRegisters(std::move(cats_to_show), regs_cb);
return Err();
}
// until -----------------------------------------------------------------------
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 DoUntil(ConsoleContext* context, const Command& cmd) {
Err err;
// 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.
InputLocation location;
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.");
}
location = InputLocation(cmd.frame()->GetAddress());
} else if (cmd.args().size() == 1) {
// One arg = normal location (ParseInputLocation can handle null frames).
Err err = ParseInputLocation(cmd.frame(), cmd.args()[0], &location);
if (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 ...").
err = AssertRunningTarget(context, "until", cmd.target());
if (err.has_error())
return err;
cmd.target()->GetProcess()->ContinueUntil(location, callback);
} else {
// Thread-specific.
err = cmd.ValidateNouns({Noun::kProcess, Noun::kThread, Noun::kFrame});
if (err.has_error())
return err;
err = AssertStoppedThreadCommand(context, cmd, false, "until");
if (err.has_error())
return err;
auto controller = std::make_unique<UntilThreadController>(location);
cmd.thread()->ContinueWith(std::move(controller), [](const Err& err) {
if (err.has_error())
Console::get()->Output(err);
});
}
return Err();
}
} // namespace
void AppendThreadVerbs(std::map<Verb, VerbRecord>* verbs) {
// Shared options for value printing.
SwitchRecord force_types(kForceAllTypes, false, "types", 't');
const std::vector<SwitchRecord> format_switches{
force_types,
SwitchRecord(kVerboseFormat, false, "verbose", 'v'),
SwitchRecord(kForceNumberChar, false, "", 'c'),
SwitchRecord(kForceNumberSigned, false, "", 'd'),
SwitchRecord(kForceNumberUnsigned, false, "", 'u'),
SwitchRecord(kForceNumberHex, false, "", 'x'),
SwitchRecord(kMaxArraySize, true, "max-array")};
VerbRecord backtrace(&DoBacktrace, {"backtrace", "bt"}, kBacktraceShortHelp,
kBacktraceHelp, CommandGroup::kQuery);
backtrace.switches = {force_types};
(*verbs)[Verb::kBacktrace] = std::move(backtrace);
(*verbs)[Verb::kContinue] =
VerbRecord(&DoContinue, {"continue", "cont", "c"}, kContinueShortHelp,
kContinueHelp, CommandGroup::kStep, SourceAffinity::kSource);
(*verbs)[Verb::kFinish] =
VerbRecord(&DoFinish, {"finish", "fi"}, kFinishShortHelp, kFinishHelp,
CommandGroup::kStep);
(*verbs)[Verb::kJump] = VerbRecord(&DoJump, {"jump", "jmp"}, kJumpShortHelp,
kJumpHelp, CommandGroup::kStep);
// locals
VerbRecord locals(&DoLocals, {"locals"}, kLocalsShortHelp, kLocalsHelp,
CommandGroup::kQuery);
locals.switches = format_switches;
(*verbs)[Verb::kLocals] = std::move(locals);
(*verbs)[Verb::kNext] =
VerbRecord(&DoNext, {"next", "n"}, kNextShortHelp, kNextHelp,
CommandGroup::kStep, SourceAffinity::kSource);
(*verbs)[Verb::kNexti] =
VerbRecord(&DoNexti, {"nexti", "ni"}, kNextiShortHelp, kNextiHelp,
CommandGroup::kAssembly, SourceAffinity::kAssembly);
(*verbs)[Verb::kPause] =
VerbRecord(&DoPause, {"pause", "pa"}, kPauseShortHelp, kPauseHelp,
CommandGroup::kProcess);
// print
VerbRecord print(&DoPrint, {"print", "p"}, kPrintShortHelp, kPrintHelp,
CommandGroup::kQuery);
print.switches = format_switches;
(*verbs)[Verb::kPrint] = std::move(print);
// regs
SwitchRecord regs_categories(kRegsCategoriesSwitch, true, "category", 'c');
SwitchRecord regs_extended(kRegsExtendedSwitch, false, "extended", 'e');
VerbRecord regs(&DoRegs, {"regs", "rg"}, kRegsShortHelp, kRegsHelp,
CommandGroup::kAssembly);
regs.switches.push_back(std::move(regs_categories));
regs.switches.push_back(std::move(regs_extended));
(*verbs)[Verb::kRegs] = std::move(regs);
// step
SwitchRecord step_force(kStepIntoUnsymbolized, false, "unsymbolized", 'u');
VerbRecord step(&DoStep, {"step", "s"}, kStepShortHelp, kStepHelp,
CommandGroup::kStep, SourceAffinity::kSource);
step.switches.push_back(step_force);
(*verbs)[Verb::kStep] = std::move(step);
(*verbs)[Verb::kStepi] =
VerbRecord(&DoStepi, {"stepi", "si"}, kStepiShortHelp, kStepiHelp,
CommandGroup::kAssembly, SourceAffinity::kAssembly);
(*verbs)[Verb::kUntil] = VerbRecord(&DoUntil, {"until", "u"}, kUntilShortHelp,
kUntilHelp, CommandGroup::kStep);
}
} // namespace zxdb