blob: ce99546b35a7c62ae81f76e3034c4c22c914e3b6 [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_locals.h"
#include "src/developer/debug/zxdb/client/frame.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/print_command_utils.h"
#include "src/developer/debug/zxdb/console/verbs.h"
namespace zxdb {
namespace {
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
)" PRINT_COMMAND_SWITCH_HELP
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 RunVerbLocals(ConsoleContext* context, const Command& cmd) {
if (Err err = AssertStoppedThreadWithFrameCommand(context, cmd, "locals"); err.has_error())
return err;
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()->As<Function>();
if (!function)
return Err("Symbols are corrupt.");
// Walk upward from the innermost lexical block for the current IP to collect local variables.
// Using the map allows collecting only the innermost version of a given name, and sorts them as
// we go.
//
// Need owning variable references to copy data out.
//
// Note that this does NOT skip "artificial" variables. In the standard these are marked as
// compiler-generated and we should probably just skip them. The exception is for "this"
// variables which we do want to show.
//
// Be aware that as of this writing there is Clang bug https://bugs.llvm.org/show_bug.cgi?id=49565
// which marks the artifical flag on structured bindings incorrectly:
//
// auto [a, b] = GetSomePair();
//
// It generates an unnamed std::pair variable without the DW_AT_artificial tag, and "a" and "b"
// varialbles WITH the artificial tag. This is backwards from what one would expect and how GCC
// encodes this (the internal generated variable should be marked artificial, and the ones the
// user named should not be).
//
// Our behavior of showing artificial variables but hiding unnamed ones worked around this bug.
// It's not clear what other cases in C++ there might be for artificial variables.
std::map<std::string, fxl::RefPtr<Variable>> vars;
VisitLocalBlocks(function->GetMostSpecificChild(location.symbol_context(), location.address()),
[&vars](const CodeBlock* block) {
for (const auto& lazy_var : block->variables()) {
const Variable* var = lazy_var.Get()->As<Variable>();
if (!var)
continue; // Symbols are corrupt.
const std::string& name = var->GetAssignedName();
if (name.empty())
continue;
if (vars.find(name) == vars.end())
vars[name] = RefPtrTo(var); // New one.
}
return VisitResult::kContinue;
});
// 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()->As<Variable>();
if (!var)
continue; // Symbols are corrupt.
const std::string& name = var->GetAssignedName();
if (!name.empty() && vars.find(name) == vars.end())
vars[name] = RefPtrTo(var); // New one.
}
if (vars.empty()) {
Console::get()->Output("No local variables in scope.");
return Err();
}
ErrOr<ConsoleFormatOptions> options = GetPrintCommandFormatOptions(cmd);
if (options.has_error())
return options.err();
auto output = fxl::MakeRefCounted<AsyncOutputBuffer>();
for (const auto& pair : vars) {
output->Append(FormatVariableForConsole(pair.second.get(), options.value(),
cmd.frame()->GetEvalContext()));
output->Append("\n");
}
output->Complete();
Console::get()->Output(std::move(output));
return Err();
}
} // namespace
VerbRecord GetLocalsVerbRecord() {
VerbRecord locals(&RunVerbLocals, {"locals"}, kLocalsShortHelp, kLocalsHelp,
CommandGroup::kQuery);
AppendPrintCommandSwitches(&locals);
return locals;
}
} // namespace zxdb