diff options
Diffstat (limited to 'lldb/source/Commands/CommandObjectVariable.cpp')
-rw-r--r-- | lldb/source/Commands/CommandObjectVariable.cpp | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/lldb/source/Commands/CommandObjectVariable.cpp b/lldb/source/Commands/CommandObjectVariable.cpp new file mode 100644 index 00000000000..6bde4829be0 --- /dev/null +++ b/lldb/source/Commands/CommandObjectVariable.cpp @@ -0,0 +1,801 @@ +//===-- CommandObjectVariable.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CommandObjectVariable.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +// Project includes +#include "lldb/Core/Options.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/Value.h" +#include "lldb/Core/ValueObject.h" +#include "lldb/Core/ValueObjectVariable.h" + +#include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/CommandReturnObject.h" + +#include "lldb/Symbol/ClangASTContext.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/Target.h" + +using namespace lldb; +using namespace lldb_private; + +//void +//DumpValueObjectValues (Stream *sout, const char *root_valobj_name, ValueObjectSP& valobj_sp, bool follow_ptrs_and_refs, uint32_t curr_depth, uint32_t max_depth) +//{ +// ValueObject *valobj = valobj_sp.get(); +// if (valobj) +// { +// const char *name_cstr = valobj->GetName().AsCString(NULL); +// const char *val_cstr = valobj->GetValueAsCString(); +// const char *loc_cstr = valobj->GetLocationAsCString(); +// const char *type_cstr = valobj->GetTypeName().AsCString(); +// const char *sum_cstr = valobj->GetSummaryAsCString(); +// const char *err_cstr = valobj->GetError().AsCString(); +// // Indent +// sout->Indent(); +// if (root_valobj_name) +// { +// sout->Printf ("%s = ", root_valobj_name); +// } +// +// if (name_cstr) +// sout->Printf ("%s => ", name_cstr); +// +// sout->Printf ("ValueObject{%u}", valobj->GetID()); +// const uint32_t num_children = valobj->GetNumChildren(); +// +// if (type_cstr) +// sout->Printf (", type = '%s'", type_cstr); +// +// if (loc_cstr) +// sout->Printf (", location = %s", loc_cstr); +// +// sout->Printf (", num_children = %u", num_children); +// +// if (val_cstr) +// sout->Printf (", value = %s", val_cstr); +// +// if (err_cstr) +// sout->Printf (", error = %s", err_cstr); +// +// if (sum_cstr) +// sout->Printf (", summary = %s", sum_cstr); +// +// sout->EOL(); +// bool is_ptr_or_ref = ClangASTContext::IsPointerOrReferenceType (valobj->GetOpaqueClangQualType()); +// if (!follow_ptrs_and_refs && is_ptr_or_ref) +// return; +// +// if (curr_depth < max_depth) +// { +// for (uint32_t idx=0; idx<num_children; ++idx) +// { +// ValueObjectSP child_sp(valobj->GetChildAtIndex(idx, true)); +// if (child_sp.get()) +// { +// sout->IndentMore(); +// DumpValueObjectValues (sout, NULL, child_sp, follow_ptrs_and_refs, curr_depth + 1, max_depth); +// sout->IndentLess(); +// } +// } +// } +// } +//} + +//---------------------------------------------------------------------- +// List images with associated information +//---------------------------------------------------------------------- +class CommandObjectVariableList : public CommandObject +{ +public: + + class CommandOptions : public Options + { + public: + + CommandOptions () : + Options() + { + ResetOptionValues (); + } + + virtual + ~CommandOptions () + { + } + + virtual Error + SetOptionValue (int option_idx, const char *option_arg) + { + Error error; + bool success; + char short_option = (char) m_getopt_table[option_idx].val; + switch (short_option) + { + case 'o': use_objc = true; break; + case 'n': name = option_arg; break; + case 'r': use_regex = true; break; + case 'a': show_args = false; break; + case 'l': show_locals = false; break; + case 'g': show_globals = false; break; + case 't': show_types = false; break; + case 'y': show_summary = false; break; + case 'L': show_location= true; break; + case 'D': debug = true; break; + case 'd': + max_depth = Args::StringToUInt32 (option_arg, UINT32_MAX, 0, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid max depth '%s'.\n", option_arg); + break; + + case 'p': + ptr_depth = Args::StringToUInt32 (option_arg, 0, 0, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid pointer depth '%s'.\n", option_arg); + break; + + case 'G': + { + ConstString const_string (option_arg); + globals.push_back(const_string); + } + break; + + case 's': + show_scope = true; + break; + + default: + error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); + break; + } + + return error; + } + + void + ResetOptionValues () + { + Options::ResetOptionValues(); + + name.clear(); + use_objc = false; + use_regex = false; + show_args = true; + show_locals = true; + show_globals = true; + show_types = true; + show_scope = false; + show_summary = true; + show_location = false; + debug = false; + max_depth = UINT32_MAX; + ptr_depth = 0; + globals.clear(); + } + + const lldb::OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static lldb::OptionDefinition g_option_table[]; + std::string name; + bool use_objc; + bool use_regex; + bool show_args; + bool show_locals; + bool show_globals; + bool show_types; + bool show_scope; // local/arg/global/static + bool show_summary; + bool show_location; + bool debug; + uint32_t max_depth; // The depth to print when dumping concrete (not pointers) aggreate values + uint32_t ptr_depth; // The default depth that is dumped when we find pointers + std::vector<ConstString> globals; + // Instance variables to hold the values for command options. + }; + + CommandObjectVariableList () : + CommandObject ( + "variable list", + "Show specified argument, local variable, static variable or global variable. If none specified, list them all.", + "variable list [<cmd-options>] [<var-name1> [<var-name2>...]]") + { + } + + virtual + ~CommandObjectVariableList () + { + } + + virtual + Options * + GetOptions () + { + return &m_options; + } + + void + DumpVariable (CommandReturnObject &result, ExecutionContext *exe_ctx, Variable *variable) + { + if (variable) + { + Stream &s = result.GetOutputStream(); + DWARFExpression &expr = variable->LocationExpression(); + Value expr_result; + Error expr_error; + Type *variable_type = variable->GetType(); + bool expr_success = expr.Evaluate(exe_ctx, NULL, NULL, expr_result, &expr_error); + + if (m_options.debug) + s.Printf ("Variable{0x%8.8x}: ", variable->GetID()); + + if (!expr_success) + s.Printf ("%s = ERROR (%s)", variable->GetName().AsCString(NULL), expr_error.AsCString()); + else + { + Value::ValueType expr_value_type = expr_result.GetValueType(); + switch (expr_value_type) + { + case Value::eValueTypeScalar: + s.Printf ("%s = ", variable->GetName().AsCString(NULL)); + if (variable_type) + { + DataExtractor data; + if (expr_result.ResolveValue (exe_ctx, NULL).GetData (data)) + variable_type->DumpValue (exe_ctx, &s, data, 0, m_options.show_types, m_options.show_summary, m_options.debug); + } + break; + + case Value::eValueTypeFileAddress: + case Value::eValueTypeLoadAddress: + case Value::eValueTypeHostAddress: + { + s.Printf ("%s = ", variable->GetName().AsCString(NULL)); + lldb::addr_t addr = LLDB_INVALID_ADDRESS; + lldb::AddressType addr_type = eAddressTypeLoad; + + if (expr_value_type == Value::eValueTypeFileAddress) + { + lldb::addr_t file_addr = expr_result.ResolveValue (exe_ctx, NULL).ULongLong(LLDB_INVALID_ADDRESS); + SymbolContext var_sc; + variable->CalculateSymbolContext(&var_sc); + if (var_sc.module_sp) + { + ObjectFile *objfile = var_sc.module_sp->GetObjectFile(); + if (objfile) + { + Address so_addr(file_addr, objfile->GetSectionList()); + addr = so_addr.GetLoadAddress(exe_ctx->process); + } + if (addr == LLDB_INVALID_ADDRESS) + { + result.GetErrorStream().Printf ("error: %s is not loaded", var_sc.module_sp->GetFileSpec().GetFilename().AsCString()); + } + } + else + { + result.GetErrorStream().Printf ("error: unable to resolve the variable address 0x%llx", file_addr); + } + } + else + { + if (expr_value_type == Value::eValueTypeHostAddress) + addr_type = eAddressTypeHost; + addr = expr_result.ResolveValue (exe_ctx, NULL).ULongLong(LLDB_INVALID_ADDRESS); + } + + if (addr != LLDB_INVALID_ADDRESS) + { + if (m_options.debug) + s.Printf("@ 0x%8.8llx, value = ", addr); + variable_type->DumpValueInMemory (exe_ctx, &s, addr, addr_type, m_options.show_types, m_options.show_summary, m_options.debug); + } + } + break; + } + } + s.EOL(); + } + } + + void + DumpValueObject (CommandReturnObject &result, + ExecutionContextScope *exe_scope, + ValueObject *valobj, + const char *root_valobj_name, + uint32_t ptr_depth, + uint32_t curr_depth, + uint32_t max_depth, + bool use_objc) + { + if (valobj) + { + Stream &s = result.GetOutputStream(); + + //const char *loc_cstr = valobj->GetLocationAsCString(); + if (m_options.show_location) + { + s.Printf("@ %s: ", valobj->GetLocationAsCString(exe_scope)); + } + if (m_options.debug) + s.Printf ("%p ValueObject{%u} ", valobj, valobj->GetID()); + + s.Indent(); + + if (m_options.show_types) + s.Printf("(%s) ", valobj->GetTypeName().AsCString()); + + const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString(""); + s.Printf ("%s = ", name_cstr); + + const char *val_cstr = valobj->GetValueAsCString(exe_scope); + const char *err_cstr = valobj->GetError().AsCString(); + + if (err_cstr) + { + s.Printf ("error: %s\n", err_cstr); + } + else + { + const char *sum_cstr = valobj->GetSummaryAsCString(exe_scope); + + const bool is_aggregate = ClangASTContext::IsAggregateType (valobj->GetOpaqueClangQualType()); + + if (val_cstr) + s.PutCString(val_cstr); + + if (sum_cstr) + s.Printf(" %s", sum_cstr); + + if (use_objc) + { + if (!ClangASTContext::IsPointerType (valobj->GetOpaqueClangQualType())) + return; + + if (!valobj->GetValueIsValid()) + return; + + Process *process = exe_scope->CalculateProcess(); + + if (!process) + return; + + Scalar scalar; + + if (!Type::GetValueAsScalar (valobj->GetClangAST(), + valobj->GetOpaqueClangQualType(), + valobj->GetDataExtractor(), + 0, + valobj->GetByteSize(), + scalar)) + return; + + ConstString po_output; + + ExecutionContext exe_ctx; + exe_scope->Calculate(exe_ctx); + + Value val(scalar); + val.SetContext(Value::eContextTypeOpaqueClangQualType, + ClangASTContext::GetVoidPtrType(valobj->GetClangAST(), false)); + + if (!process->GetObjCObjectPrinter().PrintObject(po_output, val, exe_ctx)) + return; + + s.Printf("\n%s\n", po_output.GetCString()); + + return; + } + + + if (curr_depth < max_depth) + { + if (is_aggregate) + s.PutChar('{'); + + bool is_ptr_or_ref = ClangASTContext::IsPointerOrReferenceType (valobj->GetOpaqueClangQualType()); + + if (is_ptr_or_ref && ptr_depth == 0) + return; + + const uint32_t num_children = valobj->GetNumChildren(); + if (num_children) + { + s.IndentMore(); + for (uint32_t idx=0; idx<num_children; ++idx) + { + ValueObjectSP child_sp(valobj->GetChildAtIndex(idx, true)); + if (child_sp.get()) + { + s.EOL(); + DumpValueObject (result, + exe_scope, + child_sp.get(), + NULL, + is_ptr_or_ref ? ptr_depth - 1 : ptr_depth, + curr_depth + 1, + max_depth, + false); + if (idx + 1 < num_children) + s.PutChar(','); + } + } + s.IndentLess(); + } + if (is_aggregate) + { + s.EOL(); + s.Indent("}"); + } + } + else + { + if (is_aggregate) + { + s.PutCString("{...}"); + } + } + + } + } + } + + virtual bool + Execute (Args& command, + CommandContext *context, + CommandInterpreter *interpreter, + CommandReturnObject &result) + { + ExecutionContext exe_ctx(context->GetExecutionContext()); + if (exe_ctx.frame == NULL) + { + result.AppendError ("invalid frame"); + result.SetStatus (eReturnStatusFailed); + return false; + } + else + { + VariableList variable_list; + + SymbolContext frame_sc = exe_ctx.frame->GetSymbolContext (eSymbolContextEverything); + if (exe_ctx.frame && frame_sc.block) + frame_sc.block->AppendVariables(true, true, &variable_list); + VariableSP var_sp; + ValueObjectSP valobj_sp; + //ValueObjectList &valobj_list = exe_ctx.frame->GetValueObjectList(); + const char *name_cstr = NULL; + size_t idx; + if (!m_options.globals.empty()) + { + uint32_t fail_count = 0; + Target *target = context->GetTarget(); + if (target) + { + const size_t num_globals = m_options.globals.size(); + for (idx = 0; idx < num_globals; ++idx) + { + VariableList global_var_list; + const uint32_t num_matching_globals = target->GetImages().FindGlobalVariables (m_options.globals[idx], true, UINT32_MAX, global_var_list); + + if (num_matching_globals == 0) + { + ++fail_count; + result.GetErrorStream().Printf ("error: can't find global variable '%s'\n", m_options.globals[idx].AsCString()); + } + else + { + for (uint32_t global_idx=0; global_idx<num_matching_globals; ++global_idx) + { + var_sp = global_var_list.GetVariableAtIndex(global_idx); + if (var_sp) + { + valobj_sp = exe_ctx.frame->GetValueObjectList().FindValueObjectByValueName (m_options.globals[idx].AsCString()); + if (!valobj_sp) + valobj_sp.reset (new ValueObjectVariable (var_sp)); + + if (valobj_sp) + { + exe_ctx.frame->GetValueObjectList().Append (valobj_sp); + DumpValueObject (result, exe_ctx.frame, valobj_sp.get(), name_cstr, m_options.ptr_depth, 0, m_options.max_depth, false); + result.GetOutputStream().EOL(); + } + } + } + } + } + } + if (fail_count) + { + result.SetStatus (eReturnStatusFailed); + } + } + + if (command.GetArgumentCount() > 0) + { + // If we have any args to the variable command, we will make + // variable objects from them... + for (idx = 0; (name_cstr = command.GetArgumentAtIndex(idx)) != NULL; ++idx) + { + uint32_t ptr_depth = m_options.ptr_depth; + // If first character is a '*', then show pointer contents + if (name_cstr[0] == '*') + { + ++ptr_depth; + name_cstr++; // Skip the '*' + } + + std::string var_path (name_cstr); + size_t separator_idx = var_path.find_first_of(".-["); + + ConstString name_const_string; + if (separator_idx == std::string::npos) + name_const_string.SetCString (var_path.c_str()); + else + name_const_string.SetCStringWithLength (var_path.c_str(), separator_idx); + + var_sp = variable_list.FindVariable(name_const_string); + if (var_sp) + { + //DumpVariable (result, &exe_ctx, var_sp.get()); + // TODO: redo history variables using a different map +// if (var_path[0] == '$') +// valobj_sp = valobj_list.FindValueObjectByValueObjectName (name_const_string.GetCString()); +// else + valobj_sp = exe_ctx.frame->GetValueObjectList().FindValueObjectByValueName (name_const_string.GetCString()); + + if (!valobj_sp) + { + valobj_sp.reset (new ValueObjectVariable (var_sp)); + exe_ctx.frame->GetValueObjectList().Append (valobj_sp); + } + + var_path.erase (0, name_const_string.GetLength ()); + // We are dumping at least one child + while (separator_idx != std::string::npos) + { + // Calculate the next separator index ahead of time + ValueObjectSP child_valobj_sp; + const char separator_type = var_path[0]; + switch (separator_type) + { + + case '-': + if (var_path.size() >= 2 && var_path[1] != '>') + { + result.GetErrorStream().Printf ("error: invalid character in variable path starting at '%s'\n", + var_path.c_str()); + var_path.clear(); + valobj_sp.reset(); + break; + } + var_path.erase (0, 1); // Remove the '-' + // Fall through + case '.': + { + var_path.erase (0, 1); // Remove the '.' or '>' + separator_idx = var_path.find_first_of(".-["); + ConstString child_name; + if (separator_idx == std::string::npos) + child_name.SetCString (var_path.c_str()); + else + child_name.SetCStringWithLength(var_path.c_str(), separator_idx); + + child_valobj_sp = valobj_sp->GetChildMemberWithName (child_name, true); + if (!child_valobj_sp) + { + result.GetErrorStream().Printf ("error: can't find child of '%s' named '%s'\n", + valobj_sp->GetName().AsCString(), + child_name.GetCString()); + var_path.clear(); + valobj_sp.reset(); + break; + } + // Remove the child name from the path + var_path.erase(0, child_name.GetLength()); + } + break; + + case '[': + // Array member access, or treating pointer as an array + if (var_path.size() > 2) // Need at least two brackets and a number + { + char *end = NULL; + int32_t child_index = ::strtol (&var_path[1], &end, 0); + if (end && *end == ']') + { + + if (valobj_sp->IsPointerType ()) + { + child_valobj_sp = valobj_sp->GetSyntheticArrayMemberFromPointer (child_index, true); + } + else + { + child_valobj_sp = valobj_sp->GetChildAtIndex (child_index, true); + } + + if (!child_valobj_sp) + { + result.GetErrorStream().Printf ("error: invalid array index %u in '%s'\n", + child_index, + valobj_sp->GetName().AsCString()); + var_path.clear(); + valobj_sp.reset(); + break; + } + + // Erase the array member specification '[%i]' where %i is the array index + var_path.erase(0, (end - var_path.c_str()) + 1); + separator_idx = var_path.find_first_of(".-["); + + // Break out early from the switch since we were able to find the child member + break; + } + } + result.GetErrorStream().Printf ("error: invalid array member specification for '%s' starting at '%s'\n", + valobj_sp->GetName().AsCString(), + var_path.c_str()); + var_path.clear(); + valobj_sp.reset(); + break; + + break; + + default: + result.GetErrorStream().Printf ("error: invalid character in variable path starting at '%s'\n", + var_path.c_str()); + var_path.clear(); + valobj_sp.reset(); + separator_idx = std::string::npos; + break; + } + + if (child_valobj_sp) + valobj_sp = child_valobj_sp; + + if (var_path.empty()) + break; + + } + + if (valobj_sp) + { + DumpValueObject (result, exe_ctx.frame, valobj_sp.get(), name_cstr, ptr_depth, 0, m_options.max_depth, m_options.use_objc); + result.GetOutputStream().EOL(); + } + } + else + { + result.GetErrorStream().Printf ("error: unable to find any variables named '%s'\n", name_cstr); + var_path.clear(); + } + } + } + else + { + + if (m_options.show_globals) + { + if (frame_sc.comp_unit) + { + variable_list.AddVariables (frame_sc.comp_unit->GetVariableList(true).get()); + } + } + + const uint32_t num_variables = variable_list.GetSize(); + + if (num_variables > 0) + { + for (uint32_t i=0; i<num_variables; i++) + { + Variable *variable = variable_list.GetVariableAtIndex(i).get(); + bool dump_variable = true; + + switch (variable->GetScope()) + { + case eValueTypeVariableGlobal: + dump_variable = m_options.show_globals; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString("GLOBAL: "); + break; + + case eValueTypeVariableStatic: + dump_variable = m_options.show_globals; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString("STATIC: "); + break; + + case eValueTypeVariableArgument: + dump_variable = m_options.show_args; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString(" ARG: "); + break; + + case eValueTypeVariableLocal: + dump_variable = m_options.show_locals; + if (dump_variable && m_options.show_scope) + result.GetOutputStream().PutCString(" LOCAL: "); + break; + + default: + break; + } + + if (dump_variable) + DumpVariable (result, &exe_ctx, variable); + } + } + } + result.SetStatus (eReturnStatusSuccessFinishResult); + } + return result.Succeeded(); + } +protected: + + CommandOptions m_options; +}; + +lldb::OptionDefinition +CommandObjectVariableList::CommandOptions::g_option_table[] = +{ +{ 0, false, "debug", 'D', no_argument, NULL, 0, NULL, "Show verbose debug information."}, +{ 0, false, "depth", 'd', required_argument, NULL, 0, "<count>", "Set the max recurse depth when dumping aggregate types (default is infinity)."}, +{ 0, false, "globals", 'g', no_argument, NULL, 0, NULL, "List global and static variables for the current stack frame source file."}, +{ 0, false, "global", 'G', required_argument, NULL, 0, NULL, "Find a global variable by name (which might not be in the current stack frame source file)."}, +{ 0, false, "location", 'L', no_argument, NULL, 0, NULL, "Show variable location information."}, +{ 0, false, "name", 'n', required_argument, NULL, 0, "<name>", "Lookup a variable by name or regex (--regex) for the current execution context."}, +{ 0, false, "no-args", 'a', no_argument, NULL, 0, NULL, "Omit function arguments."}, +{ 0, false, "no-locals", 'l', no_argument, NULL, 0, NULL, "Omit local variables."}, +{ 0, false, "no-types", 't', no_argument, NULL, 0, NULL, "Omit variable type names."}, +{ 0, false, "no-summary", 'y', no_argument, NULL, 0, NULL, "Omit summary information."}, +{ 0, false, "scope", 's', no_argument, NULL, 0, NULL, "Show variable scope (argument, local, global, static)."}, +{ 0, false, "objc", 'o', no_argument, NULL, 0, NULL, "When looking up a variable by name (--name), print as an Objective-C object."}, +{ 0, false, "ptr-depth", 'p', required_argument, NULL, 0, "<count>", "The number of pointers to be traversed when dumping values (default is zero)."}, +{ 0, false, "regex", 'r', no_argument, NULL, 0, NULL, "The <name> argument for name lookups are regular expressions."}, +{ 0, false, NULL, 0, 0, NULL, NULL, NULL, NULL } +}; + +//---------------------------------------------------------------------- +// CommandObjectVariable constructor +//---------------------------------------------------------------------- +CommandObjectVariable::CommandObjectVariable(CommandInterpreter *interpreter) : + CommandObjectMultiword ("variable", + "Access program arguments, locals, static and global variables.", + "variable [list] ...") +{ + LoadSubCommand (CommandObjectSP (new CommandObjectVariableList ()), "list", interpreter); +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +CommandObjectVariable::~CommandObjectVariable() +{ +} + + + + |