llvm-project/lldb/source/Commands/CommandObjectFrame.cpp

759 lines
29 KiB
C++
Raw Normal View History

//===-- CommandObjectFrame.cpp ----------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// C Includes
// C++ Includes
#include <string>
// Other libraries and framework includes
// Project includes
#include "CommandObjectFrame.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/Timer.h"
#include "lldb/Core/Value.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Core/ValueObjectVariable.h"
#include "lldb/DataFormatters/DataVisualization.h"
#include "lldb/DataFormatters/ValueObjectPrinter.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/StringConvert.h"
#include "lldb/Interpreter/Args.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/OptionGroupFormat.h"
#include "lldb/Interpreter/OptionGroupValueObjectDisplay.h"
#include "lldb/Interpreter/OptionGroupVariable.h"
#include "lldb/Interpreter/Options.h"
#include "lldb/Symbol/ClangASTContext.h"
#include "lldb/Symbol/CompilerType.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/StopInfo.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
#include "lldb/Utility/LLDBAssert.h"
using namespace lldb;
using namespace lldb_private;
#pragma mark CommandObjectFrameDiagnose
//-------------------------------------------------------------------------
// CommandObjectFrameInfo
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
// CommandObjectFrameDiagnose
//-------------------------------------------------------------------------
static OptionDefinition g_frame_diag_options[] = {
// clang-format off
{ LLDB_OPT_SET_1, false, "register", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeRegisterName, "A register to diagnose." },
{ LLDB_OPT_SET_1, false, "address", 'a', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeAddress, "An address to diagnose." },
{ LLDB_OPT_SET_1, false, "offset", 'o', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "An optional offset. Requires --register." }
// clang-format on
};
class CommandObjectFrameDiagnose : public CommandObjectParsed {
public:
class CommandOptions : public Options {
public:
CommandOptions() : Options() { OptionParsingStarting(nullptr); }
~CommandOptions() override = default;
Error SetOptionValue(uint32_t option_idx, const char *option_arg,
ExecutionContext *execution_context) override {
Error error;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'r':
reg = ConstString(option_arg);
break;
case 'a': {
bool success = false;
address = StringConvert::ToUInt64(option_arg, 0, 0, &success);
if (!success) {
address.reset();
error.SetErrorStringWithFormat("invalid address argument '%s'",
option_arg);
}
} break;
case 'o': {
bool success = false;
offset = StringConvert::ToSInt64(option_arg, 0, 0, &success);
if (!success) {
offset.reset();
error.SetErrorStringWithFormat("invalid offset argument '%s'",
option_arg);
}
} break;
default:
error.SetErrorStringWithFormat("invalid short option character '%c'",
short_option);
break;
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
address.reset();
reg.reset();
offset.reset();
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::makeArrayRef(g_frame_diag_options);
}
// Options.
llvm::Optional<lldb::addr_t> address;
llvm::Optional<ConstString> reg;
llvm::Optional<int64_t> offset;
};
CommandObjectFrameDiagnose(CommandInterpreter &interpreter)
: CommandObjectParsed(interpreter, "frame diagnose",
"Try to determine what path path the current stop "
"location used to get to a register or address",
nullptr,
eCommandRequiresThread | eCommandTryTargetAPILock |
eCommandProcessMustBeLaunched |
eCommandProcessMustBePaused),
m_options() {
CommandArgumentEntry arg;
CommandArgumentData index_arg;
// Define the first (and only) variant of this arg.
index_arg.arg_type = eArgTypeFrameIndex;
index_arg.arg_repetition = eArgRepeatOptional;
// There is only one variant this argument could be; put it into the
// argument entry.
arg.push_back(index_arg);
// Push the data for the first argument into the m_arguments vector.
m_arguments.push_back(arg);
}
~CommandObjectFrameDiagnose() override = default;
Options *GetOptions() override { return &m_options; }
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
Thread *thread = m_exe_ctx.GetThreadPtr();
StackFrameSP frame_sp = thread->GetSelectedFrame();
ValueObjectSP valobj_sp;
if (m_options.address.hasValue()) {
if (m_options.reg.hasValue() || m_options.offset.hasValue()) {
result.AppendError(
"`frame diagnose --address` is incompatible with other arguments.");
result.SetStatus(eReturnStatusFailed);
return false;
}
valobj_sp = frame_sp->GuessValueForAddress(m_options.address.getValue());
} else if (m_options.reg.hasValue()) {
valobj_sp = frame_sp->GuessValueForRegisterAndOffset(
m_options.reg.getValue(), m_options.offset.getValueOr(0));
} else {
StopInfoSP stop_info_sp = thread->GetStopInfo();
if (!stop_info_sp) {
result.AppendError("No arguments provided, and no stop info.");
result.SetStatus(eReturnStatusFailed);
return false;
}
valobj_sp = StopInfo::GetCrashingDereference(stop_info_sp);
}
if (!valobj_sp) {
result.AppendError("No diagnosis available.");
result.SetStatus(eReturnStatusFailed);
return false;
}
const bool qualify_cxx_base_classes = false;
DumpValueObjectOptions::DeclPrintingHelper helper =
[&valobj_sp, qualify_cxx_base_classes](
ConstString type, ConstString var,
const DumpValueObjectOptions &opts, Stream &stream) -> bool {
const ValueObject::GetExpressionPathFormat format = ValueObject::
GetExpressionPathFormat::eGetExpressionPathFormatHonorPointers;
valobj_sp->GetExpressionPath(stream, qualify_cxx_base_classes, format);
stream.PutCString(" =");
return true;
};
DumpValueObjectOptions options;
options.SetDeclPrintingHelper(helper);
ValueObjectPrinter printer(valobj_sp.get(), &result.GetOutputStream(),
options);
printer.PrintValueObject();
return true;
}
protected:
CommandOptions m_options;
};
#pragma mark CommandObjectFrameInfo
//-------------------------------------------------------------------------
// CommandObjectFrameInfo
//-------------------------------------------------------------------------
class CommandObjectFrameInfo : public CommandObjectParsed {
public:
CommandObjectFrameInfo(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "frame info", "List information about the current "
"stack frame in the current thread.",
"frame info",
eCommandRequiresFrame | eCommandTryTargetAPILock |
eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {}
~CommandObjectFrameInfo() override = default;
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
m_exe_ctx.GetFrameRef().DumpUsingSettingsFormat(&result.GetOutputStream());
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
};
#pragma mark CommandObjectFrameSelect
//-------------------------------------------------------------------------
// CommandObjectFrameSelect
//-------------------------------------------------------------------------
static OptionDefinition g_frame_select_options[] = {
// clang-format off
{ LLDB_OPT_SET_1, false, "relative", 'r', OptionParser::eRequiredArgument, nullptr, nullptr, 0, eArgTypeOffset, "A relative frame index offset from the current frame index." },
// clang-format on
};
class CommandObjectFrameSelect : public CommandObjectParsed {
public:
class CommandOptions : public Options {
public:
CommandOptions() : Options() { OptionParsingStarting(nullptr); }
~CommandOptions() override = default;
Error SetOptionValue(uint32_t option_idx, const char *option_arg,
ExecutionContext *execution_context) override {
Error error;
bool success = false;
const int short_option = m_getopt_table[option_idx].val;
switch (short_option) {
case 'r':
relative_frame_offset =
StringConvert::ToSInt32(option_arg, INT32_MIN, 0, &success);
if (!success)
error.SetErrorStringWithFormat("invalid frame offset argument '%s'",
option_arg);
break;
default:
error.SetErrorStringWithFormat("invalid short option character '%c'",
short_option);
break;
}
return error;
}
void OptionParsingStarting(ExecutionContext *execution_context) override {
relative_frame_offset = INT32_MIN;
}
llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
return llvm::makeArrayRef(g_frame_select_options);
}
int32_t relative_frame_offset;
};
CommandObjectFrameSelect(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "frame select", "Select the current stack frame by "
"index from within the current thread "
"(see 'thread backtrace'.)",
nullptr,
eCommandRequiresThread | eCommandTryTargetAPILock |
eCommandProcessMustBeLaunched | eCommandProcessMustBePaused),
m_options() {
CommandArgumentEntry arg;
CommandArgumentData index_arg;
// Define the first (and only) variant of this arg.
index_arg.arg_type = eArgTypeFrameIndex;
index_arg.arg_repetition = eArgRepeatOptional;
// There is only one variant this argument could be; put it into the
// argument entry.
arg.push_back(index_arg);
// Push the data for the first argument into the m_arguments vector.
m_arguments.push_back(arg);
}
~CommandObjectFrameSelect() override = default;
Options *GetOptions() override { return &m_options; }
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
// No need to check "thread" for validity as eCommandRequiresThread ensures
// it is valid
Thread *thread = m_exe_ctx.GetThreadPtr();
uint32_t frame_idx = UINT32_MAX;
if (m_options.relative_frame_offset != INT32_MIN) {
// The one and only argument is a signed relative frame index
frame_idx = thread->GetSelectedFrameIndex();
if (frame_idx == UINT32_MAX)
frame_idx = 0;
if (m_options.relative_frame_offset < 0) {
if (static_cast<int32_t>(frame_idx) >= -m_options.relative_frame_offset)
frame_idx += m_options.relative_frame_offset;
else {
if (frame_idx == 0) {
// If you are already at the bottom of the stack, then just warn and
// don't reset the frame.
result.AppendError("Already at the bottom of the stack.");
result.SetStatus(eReturnStatusFailed);
return false;
} else
frame_idx = 0;
Expanded the flags that can be set for a command object in lldb_private::CommandObject. This list of available flags are: enum { //---------------------------------------------------------------------- // eFlagRequiresTarget // // Ensures a valid target is contained in m_exe_ctx prior to executing // the command. If a target doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidTargetDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidTargetDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresTarget = (1u << 0), //---------------------------------------------------------------------- // eFlagRequiresProcess // // Ensures a valid process is contained in m_exe_ctx prior to executing // the command. If a process doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidProcessDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidProcessDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresProcess = (1u << 1), //---------------------------------------------------------------------- // eFlagRequiresThread // // Ensures a valid thread is contained in m_exe_ctx prior to executing // the command. If a thread doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidThreadDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidThreadDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresThread = (1u << 2), //---------------------------------------------------------------------- // eFlagRequiresFrame // // Ensures a valid frame is contained in m_exe_ctx prior to executing // the command. If a frame doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidFrameDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidFrameDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresFrame = (1u << 3), //---------------------------------------------------------------------- // eFlagRequiresRegContext // // Ensures a valid register context (from the selected frame if there // is a frame in m_exe_ctx, or from the selected thread from m_exe_ctx) // is availble from m_exe_ctx prior to executing the command. If a // target doesn't exist or is invalid, the command will fail and // CommandObject::GetInvalidRegContextDescription() will be returned as // the error. CommandObject subclasses can override the virtual function // for GetInvalidRegContextDescription() to provide custom strings when // needed. //---------------------------------------------------------------------- eFlagRequiresRegContext = (1u << 4), //---------------------------------------------------------------------- // eFlagTryTargetAPILock // // Attempts to acquire the target lock if a target is selected in the // command interpreter. If the command object fails to acquire the API // lock, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eFlagTryTargetAPILock = (1u << 5), //---------------------------------------------------------------------- // eFlagProcessMustBeLaunched // // Verifies that there is a launched process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eFlagProcessMustBeLaunched = (1u << 6), //---------------------------------------------------------------------- // eFlagProcessMustBePaused // // Verifies that there is a paused process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eFlagProcessMustBePaused = (1u << 7) }; Now each command object contains a "ExecutionContext m_exe_ctx;" member variable that gets initialized prior to running the command. The validity of the target objects in m_exe_ctx are checked to ensure that any target/process/thread/frame/reg context that are required are valid prior to executing the command. Each command object also contains a Mutex::Locker m_api_locker which gets used if eFlagTryTargetAPILock is set. This centralizes a lot of checking code that was previously and inconsistently implemented across many commands. llvm-svn: 171990
2013-01-10 03:44:40 +08:00
}
} else if (m_options.relative_frame_offset > 0) {
// I don't want "up 20" where "20" takes you past the top of the stack
// to produce
// an error, but rather to just go to the top. So I have to count the
// stack here...
const uint32_t num_frames = thread->GetStackFrameCount();
if (static_cast<int32_t>(num_frames - frame_idx) >
m_options.relative_frame_offset)
frame_idx += m_options.relative_frame_offset;
else {
if (frame_idx == num_frames - 1) {
// If we are already at the top of the stack, just warn and don't
// reset the frame.
result.AppendError("Already at the top of the stack.");
result.SetStatus(eReturnStatusFailed);
return false;
} else
frame_idx = num_frames - 1;
Expanded the flags that can be set for a command object in lldb_private::CommandObject. This list of available flags are: enum { //---------------------------------------------------------------------- // eFlagRequiresTarget // // Ensures a valid target is contained in m_exe_ctx prior to executing // the command. If a target doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidTargetDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidTargetDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresTarget = (1u << 0), //---------------------------------------------------------------------- // eFlagRequiresProcess // // Ensures a valid process is contained in m_exe_ctx prior to executing // the command. If a process doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidProcessDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidProcessDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresProcess = (1u << 1), //---------------------------------------------------------------------- // eFlagRequiresThread // // Ensures a valid thread is contained in m_exe_ctx prior to executing // the command. If a thread doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidThreadDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidThreadDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresThread = (1u << 2), //---------------------------------------------------------------------- // eFlagRequiresFrame // // Ensures a valid frame is contained in m_exe_ctx prior to executing // the command. If a frame doesn't exist or is invalid, the command // will fail and CommandObject::GetInvalidFrameDescription() will be // returned as the error. CommandObject subclasses can override the // virtual function for GetInvalidFrameDescription() to provide custom // strings when needed. //---------------------------------------------------------------------- eFlagRequiresFrame = (1u << 3), //---------------------------------------------------------------------- // eFlagRequiresRegContext // // Ensures a valid register context (from the selected frame if there // is a frame in m_exe_ctx, or from the selected thread from m_exe_ctx) // is availble from m_exe_ctx prior to executing the command. If a // target doesn't exist or is invalid, the command will fail and // CommandObject::GetInvalidRegContextDescription() will be returned as // the error. CommandObject subclasses can override the virtual function // for GetInvalidRegContextDescription() to provide custom strings when // needed. //---------------------------------------------------------------------- eFlagRequiresRegContext = (1u << 4), //---------------------------------------------------------------------- // eFlagTryTargetAPILock // // Attempts to acquire the target lock if a target is selected in the // command interpreter. If the command object fails to acquire the API // lock, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eFlagTryTargetAPILock = (1u << 5), //---------------------------------------------------------------------- // eFlagProcessMustBeLaunched // // Verifies that there is a launched process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eFlagProcessMustBeLaunched = (1u << 6), //---------------------------------------------------------------------- // eFlagProcessMustBePaused // // Verifies that there is a paused process in m_exe_ctx, if there // isn't, the command will fail with an appropriate error message. //---------------------------------------------------------------------- eFlagProcessMustBePaused = (1u << 7) }; Now each command object contains a "ExecutionContext m_exe_ctx;" member variable that gets initialized prior to running the command. The validity of the target objects in m_exe_ctx are checked to ensure that any target/process/thread/frame/reg context that are required are valid prior to executing the command. Each command object also contains a Mutex::Locker m_api_locker which gets used if eFlagTryTargetAPILock is set. This centralizes a lot of checking code that was previously and inconsistently implemented across many commands. llvm-svn: 171990
2013-01-10 03:44:40 +08:00
}
}
} else {
if (command.GetArgumentCount() == 1) {
const char *frame_idx_cstr = command.GetArgumentAtIndex(0);
bool success = false;
frame_idx =
StringConvert::ToUInt32(frame_idx_cstr, UINT32_MAX, 0, &success);
if (!success) {
result.AppendErrorWithFormat("invalid frame index argument '%s'.",
frame_idx_cstr);
result.SetStatus(eReturnStatusFailed);
return false;
}
} else if (command.GetArgumentCount() == 0) {
frame_idx = thread->GetSelectedFrameIndex();
if (frame_idx == UINT32_MAX) {
frame_idx = 0;
}
} else {
result.AppendErrorWithFormat(
"too many arguments; expected frame-index, saw '%s'.\n",
command.GetArgumentAtIndex(0));
m_options.GenerateOptionUsage(
result.GetErrorStream(), this,
GetCommandInterpreter().GetDebugger().GetTerminalWidth());
return false;
}
}
bool success = thread->SetSelectedFrameByIndexNoisily(
frame_idx, result.GetOutputStream());
if (success) {
m_exe_ctx.SetFrameSP(thread->GetSelectedFrame());
result.SetStatus(eReturnStatusSuccessFinishResult);
} else {
result.AppendErrorWithFormat("Frame index (%u) out of range.\n",
frame_idx);
result.SetStatus(eReturnStatusFailed);
}
return result.Succeeded();
}
protected:
CommandOptions m_options;
};
#pragma mark CommandObjectFrameVariable
//----------------------------------------------------------------------
// List images with associated information
//----------------------------------------------------------------------
class CommandObjectFrameVariable : public CommandObjectParsed {
public:
CommandObjectFrameVariable(CommandInterpreter &interpreter)
: CommandObjectParsed(
interpreter, "frame variable",
"Show variables for the current stack frame. Defaults to all "
"arguments and local variables in scope. Names of argument, "
"local, file static and file global variables can be specified. "
"Children of aggregate variables can be specified such as "
"'var->child.x'.",
nullptr, eCommandRequiresFrame | eCommandTryTargetAPILock |
eCommandProcessMustBeLaunched |
eCommandProcessMustBePaused | eCommandRequiresProcess),
m_option_group(),
m_option_variable(
true), // Include the frame specific options by passing "true"
m_option_format(eFormatDefault),
m_varobj_options() {
CommandArgumentEntry arg;
CommandArgumentData var_name_arg;
// Define the first (and only) variant of this arg.
var_name_arg.arg_type = eArgTypeVarName;
var_name_arg.arg_repetition = eArgRepeatStar;
// There is only one variant this argument could be; put it into the
// argument entry.
arg.push_back(var_name_arg);
// Push the data for the first argument into the m_arguments vector.
m_arguments.push_back(arg);
m_option_group.Append(&m_option_variable, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Append(&m_option_format,
OptionGroupFormat::OPTION_GROUP_FORMAT |
OptionGroupFormat::OPTION_GROUP_GDB_FMT,
LLDB_OPT_SET_1);
m_option_group.Append(&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1);
m_option_group.Finalize();
}
~CommandObjectFrameVariable() override = default;
Options *GetOptions() override { return &m_option_group; }
int HandleArgumentCompletion(Args &input, int &cursor_index,
int &cursor_char_position,
OptionElementVector &opt_element_vector,
int match_start_point, int max_return_elements,
bool &word_complete,
StringList &matches) override {
// Arguments are the standard source file completer.
std::string completion_str(input.GetArgumentAtIndex(cursor_index));
completion_str.erase(cursor_char_position);
CommandCompletions::InvokeCommonCompletionCallbacks(
GetCommandInterpreter(), CommandCompletions::eVariablePathCompletion,
completion_str.c_str(), match_start_point, max_return_elements, nullptr,
word_complete, matches);
return matches.GetSize();
}
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
// No need to check "frame" for validity as eCommandRequiresFrame ensures it
// is valid
StackFrame *frame = m_exe_ctx.GetFramePtr();
Stream &s = result.GetOutputStream();
// Be careful about the stack frame, if any summary formatter runs code, it
// might clear the StackFrameList
// for the thread. So hold onto a shared pointer to the frame so it stays
// alive.
VariableList *variable_list =
frame->GetVariableList(m_option_variable.show_globals);
VariableSP var_sp;
ValueObjectSP valobj_sp;
const char *name_cstr = nullptr;
size_t idx;
TypeSummaryImplSP summary_format_sp;
if (!m_option_variable.summary.IsCurrentValueEmpty())
DataVisualization::NamedSummaryFormats::GetSummaryFormat(
ConstString(m_option_variable.summary.GetCurrentValue()),
summary_format_sp);
else if (!m_option_variable.summary_string.IsCurrentValueEmpty())
summary_format_sp.reset(new StringSummaryFormat(
TypeSummaryImpl::Flags(),
m_option_variable.summary_string.GetCurrentValue()));
DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(
eLanguageRuntimeDescriptionDisplayVerbosityFull, eFormatDefault,
summary_format_sp));
const SymbolContext &sym_ctx =
frame->GetSymbolContext(eSymbolContextFunction);
if (sym_ctx.function && sym_ctx.function->IsTopLevelFunction())
m_option_variable.show_globals = true;
if (variable_list) {
const Format format = m_option_format.GetFormat();
options.SetFormat(format);
if (!command.empty()) {
VariableList regex_var_list;
// If we have any args to the variable command, we will make
// variable objects from them...
for (idx = 0; (name_cstr = command.GetArgumentAtIndex(idx)) != nullptr;
++idx) {
if (m_option_variable.use_regex) {
const size_t regex_start_index = regex_var_list.GetSize();
llvm::StringRef name_str(name_cstr);
RegularExpression regex(name_str);
if (regex.Compile(name_str)) {
size_t num_matches = 0;
const size_t num_new_regex_vars =
variable_list->AppendVariablesIfUnique(regex, regex_var_list,
num_matches);
if (num_new_regex_vars > 0) {
for (size_t regex_idx = regex_start_index,
end_index = regex_var_list.GetSize();
regex_idx < end_index; ++regex_idx) {
var_sp = regex_var_list.GetVariableAtIndex(regex_idx);
if (var_sp) {
valobj_sp = frame->GetValueObjectForFrameVariable(
var_sp, m_varobj_options.use_dynamic);
if (valobj_sp) {
// if (format
// !=
// eFormatDefault)
// valobj_sp->SetFormat
// (format);
if (m_option_variable.show_decl &&
var_sp->GetDeclaration().GetFile()) {
bool show_fullpaths = false;
bool show_module = true;
if (var_sp->DumpDeclaration(&s, show_fullpaths,
show_module))
s.PutCString(": ");
}
valobj_sp->Dump(result.GetOutputStream(), options);
}
}
}
} else if (num_matches == 0) {
result.GetErrorStream().Printf("error: no variables matched "
"the regular expression '%s'.\n",
name_cstr);
}
} else {
char regex_error[1024];
if (regex.GetErrorAsCString(regex_error, sizeof(regex_error)))
result.GetErrorStream().Printf("error: %s\n", regex_error);
else
result.GetErrorStream().Printf(
"error: unknown regex error when compiling '%s'\n",
name_cstr);
}
} else // No regex, either exact variable names or variable
// expressions.
{
Error error;
uint32_t expr_path_options =
StackFrame::eExpressionPathOptionCheckPtrVsMember |
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess |
StackFrame::eExpressionPathOptionsInspectAnonymousUnions;
lldb::VariableSP var_sp;
valobj_sp = frame->GetValueForVariableExpressionPath(
name_cstr, m_varobj_options.use_dynamic, expr_path_options,
var_sp, error);
if (valobj_sp) {
// if (format != eFormatDefault)
// valobj_sp->SetFormat (format);
if (m_option_variable.show_decl && var_sp &&
var_sp->GetDeclaration().GetFile()) {
var_sp->GetDeclaration().DumpStopContext(&s, false);
s.PutCString(": ");
}
options.SetFormat(format);
options.SetVariableFormatDisplayLanguage(
valobj_sp->GetPreferredDisplayLanguage());
Stream &output_stream = result.GetOutputStream();
options.SetRootValueObjectName(valobj_sp->GetParent() ? name_cstr
: nullptr);
valobj_sp->Dump(output_stream, options);
} else {
const char *error_cstr = error.AsCString(nullptr);
if (error_cstr)
result.GetErrorStream().Printf("error: %s\n", error_cstr);
else
result.GetErrorStream().Printf("error: unable to find any "
"variable expression path that "
"matches '%s'.\n",
name_cstr);
}
}
}
} else // No command arg specified. Use variable_list, instead.
{
const size_t num_variables = variable_list->GetSize();
if (num_variables > 0) {
for (size_t i = 0; i < num_variables; i++) {
var_sp = variable_list->GetVariableAtIndex(i);
bool dump_variable = true;
std::string scope_string;
switch (var_sp->GetScope()) {
case eValueTypeVariableGlobal:
// Always dump globals since we only fetched them if
// m_option_variable.show_scope was true
if (dump_variable && m_option_variable.show_scope)
scope_string = "GLOBAL: ";
break;
case eValueTypeVariableStatic:
// Always dump globals since we only fetched them if
// m_option_variable.show_scope was true, or this is
// a static variable from a block in the current scope
if (dump_variable && m_option_variable.show_scope)
scope_string = "STATIC: ";
break;
case eValueTypeVariableArgument:
dump_variable = m_option_variable.show_args;
if (dump_variable && m_option_variable.show_scope)
scope_string = " ARG: ";
break;
case eValueTypeVariableLocal:
dump_variable = m_option_variable.show_locals;
if (dump_variable && m_option_variable.show_scope)
scope_string = " LOCAL: ";
break;
case eValueTypeVariableThreadLocal:
if (dump_variable && m_option_variable.show_scope)
scope_string = "THREAD: ";
break;
default:
break;
}
if (dump_variable) {
// Use the variable object code to make sure we are
// using the same APIs as the public API will be
// using...
valobj_sp = frame->GetValueObjectForFrameVariable(
var_sp, m_varobj_options.use_dynamic);
if (valobj_sp) {
// if (format != eFormatDefault)
// valobj_sp->SetFormat
// (format);
// When dumping all variables, don't print any variables
// that are not in scope to avoid extra unneeded output
if (valobj_sp->IsInScope()) {
if (!valobj_sp->GetTargetSP()
->GetDisplayRuntimeSupportValues() &&
valobj_sp->IsRuntimeSupportValue())
continue;
if (!scope_string.empty())
s.PutCString(scope_string.c_str());
if (m_option_variable.show_decl &&
var_sp->GetDeclaration().GetFile()) {
var_sp->GetDeclaration().DumpStopContext(&s, false);
s.PutCString(": ");
}
options.SetFormat(format);
options.SetVariableFormatDisplayLanguage(
valobj_sp->GetPreferredDisplayLanguage());
options.SetRootValueObjectName(name_cstr);
valobj_sp->Dump(result.GetOutputStream(), options);
}
}
}
}
}
}
result.SetStatus(eReturnStatusSuccessFinishResult);
}
if (m_interpreter.TruncationWarningNecessary()) {
result.GetOutputStream().Printf(m_interpreter.TruncationWarningText(),
m_cmd_name.c_str());
m_interpreter.TruncationWarningGiven();
}
return result.Succeeded();
}
protected:
OptionGroupOptions m_option_group;
OptionGroupVariable m_option_variable;
OptionGroupFormat m_option_format;
OptionGroupValueObjectDisplay m_varobj_options;
};
#pragma mark CommandObjectMultiwordFrame
//-------------------------------------------------------------------------
// CommandObjectMultiwordFrame
//-------------------------------------------------------------------------
CommandObjectMultiwordFrame::CommandObjectMultiwordFrame(
CommandInterpreter &interpreter)
: CommandObjectMultiword(interpreter, "frame", "Commands for selecting and "
"examing the current "
"thread's stack frames.",
"frame <subcommand> [<subcommand-options>]") {
LoadSubCommand("diagnose",
CommandObjectSP(new CommandObjectFrameDiagnose(interpreter)));
LoadSubCommand("info",
CommandObjectSP(new CommandObjectFrameInfo(interpreter)));
LoadSubCommand("select",
CommandObjectSP(new CommandObjectFrameSelect(interpreter)));
LoadSubCommand("variable",
CommandObjectSP(new CommandObjectFrameVariable(interpreter)));
}
CommandObjectMultiwordFrame::~CommandObjectMultiwordFrame() = default;