when typing a summary string you can use the %S symbol to explicitly indicate that you want the summary to be used to print the target object

(e.g. ${var%S}). this might already be the default if your variable is of an aggregate type
new feature: synthetic filters. you can restrict the number of children for your variables to only a meaningful subset
 - the restricted list of children obeys the typical rules (e.g. summaries prevail over children)
 - one-line summaries show only the filtered (synthetic) children, if you type an expanded summary string, or you use Python scripts, all the real children are accessible
 - to provide a synthetic children list use the "type synth add" command, as in:
   type synth add foo_type --child varA --child varB[0] --child varC->packet->flags[1-4]
   (you can use ., ->, single-item array operator [N] and bitfield operator [N-M]; array slice access is not supported, giving simplified names to expression paths is not supported)
 - a new -S option to frame variable and target variable lets you override synthetic children and instead show real ones

llvm-svn: 135731
This commit is contained in:
Enrico Granata 2011-07-22 00:16:08 +00:00
parent 1872173841
commit d55546b27a
28 changed files with 1663 additions and 75 deletions

View File

@ -505,6 +505,9 @@ public:
static bool
GetSummaryFormat(ValueObject& vobj,
lldb::SummaryFormatSP& entry);
static bool
GetSyntheticFilter(ValueObject& vobj,
lldb::SyntheticFilterSP& entry);
class NamedSummaryFormats
{

View File

@ -17,6 +17,7 @@
// C++ Includes
#include <string>
#include <vector>
// Other libraries and framework includes
@ -55,23 +56,23 @@ struct ValueFormat
}
bool
Cascades()
Cascades() const
{
return m_cascades;
}
bool
SkipsPointers()
SkipsPointers() const
{
return m_skip_pointers;
}
bool
SkipsReferences()
SkipsReferences() const
{
return m_skip_references;
}
lldb::Format
GetFormat()
GetFormat() const
{
return m_format;
}
@ -80,6 +81,57 @@ struct ValueFormat
FormatObject(lldb::ValueObjectSP object);
};
struct SyntheticFilter
{
bool m_cascades;
bool m_skip_pointers;
bool m_skip_references;
std::vector<std::string> m_expression_paths;
SyntheticFilter(bool casc = false,
bool skipptr = false,
bool skipref = false) :
m_cascades(casc),
m_skip_pointers(skipptr),
m_skip_references(skipref),
m_expression_paths()
{
}
void
AddExpressionPath(std::string path)
{
bool need_add_dot = true;
if (path[0] == '.' ||
(path[0] == '-' && path[1] == '>') ||
path[0] == '[')
need_add_dot = false;
// add a '.' symbol to help forgetful users
if(!need_add_dot)
m_expression_paths.push_back(path);
else
m_expression_paths.push_back(std::string(".") + path);
}
int
GetCount() const
{
return m_expression_paths.size();
}
const std::string&
GetExpressionPathAtIndex(int i) const
{
return m_expression_paths[i];
}
std::string
GetDescription();
typedef lldb::SharedPtr<SyntheticFilter>::Type SharedPointer;
typedef bool(*SyntheticFilterCallback)(void*, const char*, const SyntheticFilter::SharedPointer&);
};
struct SummaryFormat
{
@ -106,17 +158,17 @@ struct SummaryFormat
}
bool
Cascades()
Cascades() const
{
return m_cascades;
}
bool
SkipsPointers()
SkipsPointers() const
{
return m_skip_pointers;
}
bool
SkipsReferences()
SkipsReferences() const
{
return m_skip_references;
}
@ -174,7 +226,7 @@ struct StringSummaryFormat : public SummaryFormat
}
std::string
GetFormat()
GetFormat() const
{
return m_format;
}
@ -213,13 +265,13 @@ struct ScriptSummaryFormat : public SummaryFormat
}
std::string
GetFunctionName()
GetFunctionName() const
{
return m_function_name;
}
std::string
GetPythonScript()
GetPythonScript() const
{
return m_python_script;
}

View File

@ -415,12 +415,15 @@ class FormatCategory
private:
typedef FormatNavigator<const char*, SummaryFormat> SummaryNavigator;
typedef FormatNavigator<lldb::RegularExpressionSP, SummaryFormat> RegexSummaryNavigator;
typedef FormatNavigator<const char*, SyntheticFilter> FilterNavigator;
typedef SummaryNavigator::MapType SummaryMap;
typedef RegexSummaryNavigator::MapType RegexSummaryMap;
typedef FilterNavigator::MapType FilterMap;
SummaryNavigator::SharedPointer m_summary_nav;
RegexSummaryNavigator::SharedPointer m_regex_summary_nav;
FilterNavigator::SharedPointer m_filter_nav;
bool m_enabled;
@ -428,6 +431,8 @@ private:
Mutex m_mutex;
std::string m_name;
void
Enable(bool value = true)
{
@ -449,13 +454,17 @@ public:
typedef SummaryNavigator::SharedPointer SummaryNavigatorSP;
typedef RegexSummaryNavigator::SharedPointer RegexSummaryNavigatorSP;
typedef FilterNavigator::SharedPointer FilterNavigatorSP;
FormatCategory(IFormatChangeListener* clist) :
FormatCategory(IFormatChangeListener* clist,
std::string name) :
m_summary_nav(new SummaryNavigator(clist)),
m_regex_summary_nav(new RegexSummaryNavigator(clist)),
m_filter_nav(new FilterNavigator(clist)),
m_enabled(false),
m_change_listener(clist),
m_mutex(Mutex::eMutexTypeRecursive)
m_mutex(Mutex::eMutexTypeRecursive),
m_name(name)
{}
SummaryNavigatorSP
@ -470,6 +479,12 @@ public:
return RegexSummaryNavigatorSP(m_regex_summary_nav);
}
FilterNavigatorSP
Filter()
{
return FilterNavigatorSP(m_filter_nav);
}
bool
IsEnabled() const
{
@ -491,15 +506,27 @@ public:
return regex;
}
bool
Get(ValueObject& vobj,
lldb::SyntheticFilterSP& entry,
uint32_t* reason = NULL)
{
if (!IsEnabled())
return false;
return (Filter()->Get(vobj, entry, reason));
}
// just a shortcut for Summary()->Clear; RegexSummary()->Clear()
void
Clear()
ClearSummaries()
{
m_summary_nav->Clear();
m_regex_summary_nav->Clear();
}
// just a shortcut for (Summary()->Delete(name) || RegexSummary()->Delete(name))
bool
Delete(const char* name)
DeleteSummaries(const char* name)
{
bool del_sum = m_summary_nav->Delete(name);
bool del_rex = m_regex_summary_nav->Delete(name);
@ -513,6 +540,12 @@ public:
return Summary()->GetCount() + RegexSummary()->GetCount();
}
std::string
GetName()
{
return m_name;
}
typedef lldb::SharedPtr<FormatCategory>::Type SharedPointer;
};
@ -648,23 +681,6 @@ public:
return true;
}
class match_category_to_name
{
private:
FormatCategory* addr;
public:
match_category_to_name(FormatCategory* ptr) : addr(ptr)
{}
bool operator()(std::pair<const char*,FormatCategory::SharedPointer> map_entry)
{
if (addr == map_entry.second.get())
return true;
return false;
}
};
void
LoopThrough(CallbackType callback, void* param)
{
@ -678,13 +694,7 @@ public:
for (begin = m_active_categories.begin(); begin != end; begin++)
{
FormatCategory::SharedPointer category = *begin;
const char* type;
MapIterator type_position =
std::find_if(m_map.begin(),m_map.end(),match_category_to_name(category.get()));
if (type_position != m_map.end())
type = type_position->first;
else
continue;
const char* type = category->GetName().c_str();
if (!callback(param, type, category))
break;
}
@ -741,6 +751,38 @@ public:
}
return !first;
}
bool
Get(ValueObject& vobj,
lldb::SyntheticFilterSP& entry)
{
Mutex::Locker(m_map_mutex);
uint32_t reason_why;
bool first = true;
ActiveCategoriesIterator begin, end = m_active_categories.end();
for (begin = m_active_categories.begin(); begin != end; begin++)
{
FormatCategory::SharedPointer category = *begin;
lldb::SyntheticFilterSP current_format;
if (!category->Get(vobj, current_format, &reason_why))
continue;
if (reason_why == lldb::eFormatterDirectChoice)
{
entry = current_format;
return true;
}
else if (first)
{
entry = current_format;
first = false;
}
}
return !first;
}
};
@ -856,7 +898,7 @@ public:
lldb::FormatCategorySP category;
if (m_categories_map.Get(category_name, category))
return category;
Categories().Add(category_name,lldb::FormatCategorySP(new FormatCategory(this)));
Categories().Add(category_name,lldb::FormatCategorySP(new FormatCategory(this, category_name)));
return Category(category_name);
}
@ -866,6 +908,12 @@ public:
{
return m_categories_map.Get(vobj, entry);
}
bool
Get(ValueObject& vobj,
lldb::SyntheticFilterSP& entry)
{
return m_categories_map.Get(vobj, entry);
}
static bool
GetFormatFromCString (const char *format_cstr,

View File

@ -466,7 +466,7 @@ public:
const ConstString &
GetName() const;
lldb::ValueObjectSP
virtual lldb::ValueObjectSP
GetChildAtIndex (uint32_t idx, bool can_create);
virtual lldb::ValueObjectSP
@ -545,9 +545,15 @@ public:
lldb::ValueObjectSP
GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_create);
lldb::ValueObjectSP
GetSyntheticExpressionPathChild(const char* expression, bool can_create);
lldb::ValueObjectSP
GetDynamicValue (lldb::DynamicValueType valueType);
lldb::ValueObjectSP
GetSyntheticValue (lldb::SyntheticValueType use_synthetic);
virtual lldb::ValueObjectSP
CreateConstantValue (const ConstString &name);
@ -592,6 +598,7 @@ public:
bool show_location,
bool use_objc,
lldb::DynamicValueType use_dynamic,
bool use_synthetic,
bool scope_already_checked,
bool flat_output,
uint32_t omit_summary_depth);
@ -727,6 +734,7 @@ protected:
std::vector<ValueObject *> m_children;
std::map<ConstString, ValueObject *> m_synthetic_children;
ValueObject *m_dynamic_value;
ValueObject *m_synthetic_value;
lldb::ValueObjectSP m_addr_of_valobj_sp; // We have to hold onto a shared pointer to this one because it is created
// as an independent ValueObjectConstResult, which isn't managed by us.
ValueObject *m_deref_valobj;
@ -736,6 +744,7 @@ protected:
lldb::SummaryFormatSP m_last_summary_format;
lldb::SummaryFormatSP m_forced_summary_format;
lldb::ValueFormatSP m_last_value_format;
lldb::SyntheticFilterSP m_last_synthetic_filter;
lldb::user_id_t m_user_id_of_forced_summary;
bool m_value_is_valid:1,
m_value_did_change:1,
@ -744,7 +753,8 @@ protected:
m_pointers_point_to_load_addrs:1,
m_is_deref_of_parent:1,
m_is_array_item_for_pointer:1,
m_is_bitfield_for_scalar:1;
m_is_bitfield_for_scalar:1,
m_is_expression_path_child:1;
// used to prevent endless looping into GetpPrintableRepresentation()
uint32_t m_dump_printable_counter;
@ -782,6 +792,9 @@ protected:
virtual void
CalculateDynamicValue (lldb::DynamicValueType use_dynamic);
virtual void
CalculateSyntheticValue (lldb::SyntheticValueType use_synthetic);
// Should only be called by ValueObject::GetChildAtIndex()
// Returns a ValueObject managed by this ValueObject's manager.
virtual ValueObject *

View File

@ -0,0 +1,122 @@
//===-- ValueObjectSyntheticFilter.h -------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef liblldb_ValueObjectSyntheticFilter_h_
#define liblldb_ValueObjectSyntheticFilter_h_
// C Includes
// C++ Includes
#include <ostream>
#include <vector>
// Other libraries and framework includes
// Project includes
#include "lldb/Core/ValueObject.h"
namespace lldb_private {
//----------------------------------------------------------------------
// A ValueObject that represents memory at a given address, viewed as some
// set lldb type.
//----------------------------------------------------------------------
class ValueObjectSyntheticFilter : public ValueObject
{
public:
virtual
~ValueObjectSyntheticFilter();
virtual size_t
GetByteSize();
virtual clang::ASTContext *
GetClangAST ();
virtual lldb::clang_type_t
GetClangType ();
virtual ConstString
GetTypeName();
virtual uint32_t
CalculateNumChildren();
virtual lldb::ValueType
GetValueType() const;
virtual lldb::ValueObjectSP
GetChildAtIndex (uint32_t idx, bool can_create);
virtual lldb::ValueObjectSP
GetChildMemberWithName (const ConstString &name, bool can_create);
virtual uint32_t
GetIndexOfChildWithName (const ConstString &name);
virtual bool
IsInScope ();
virtual bool
IsDynamic ()
{
if (m_parent)
return m_parent->IsDynamic();
else
return false;
}
virtual ValueObject *
GetParent()
{
if (m_parent)
return m_parent->GetParent();
else
return NULL;
}
virtual const ValueObject *
GetParent() const
{
if (m_parent)
return m_parent->GetParent();
else
return NULL;
}
void
SetOwningSP (lldb::ValueObjectSP &owning_sp)
{
if (m_owning_valobj_sp == owning_sp)
return;
assert (m_owning_valobj_sp.get() == NULL);
m_owning_valobj_sp = owning_sp;
}
protected:
virtual bool
UpdateValue ();
Address m_address; ///< The variable that this value object is based upon
lldb::TypeSP m_type_sp;
lldb::ValueObjectSP m_owning_valobj_sp;
lldb::SyntheticValueType m_use_synthetic;
lldb::SyntheticFilterSP m_synth_filter;
private:
friend class ValueObject;
ValueObjectSyntheticFilter (ValueObject &parent, lldb::SyntheticFilterSP filter);
//------------------------------------------------------------------
// For ValueObject only
//------------------------------------------------------------------
DISALLOW_COPY_AND_ASSIGN (ValueObjectSyntheticFilter);
};
} // namespace lldb_private
#endif // liblldb_ValueObjectSyntheticFilter_h_

View File

@ -54,6 +54,7 @@ public:
uint32_t max_depth;
uint32_t ptr_depth;
lldb::DynamicValueType use_dynamic;
bool use_synth;
};
} // namespace lldb_private

View File

@ -324,6 +324,12 @@ namespace lldb {
eDynamicDontRunTarget = 2
} DynamicValueType;
typedef enum SyntheticValueType
{
eNoSyntheticFilter = 0,
eUseSyntheticFilter = 1
} SyntheticValueType;
typedef enum AccessType
{
eAccessNone,

View File

@ -67,6 +67,7 @@ namespace lldb {
typedef SharedPtr<lldb_private::SummaryFormat>::Type SummaryFormatSP;
typedef SharedPtr<lldb_private::SymbolFile>::Type SymbolFileSP;
typedef SharedPtr<lldb_private::SymbolContextSpecifier>::Type SymbolContextSpecifierSP;
typedef SharedPtr<lldb_private::SyntheticFilter>::Type SyntheticFilterSP;
typedef SharedPtr<lldb_private::Target>::Type TargetSP;
typedef SharedPtr<lldb_private::Thread>::Type ThreadSP;
typedef SharedPtr<lldb_private::ThreadPlan>::Type ThreadPlanSP;

View File

@ -144,6 +144,7 @@ class SymbolContextSpecifier;
class SymbolFile;
class SymbolVendor;
class Symtab;
class SyntheticFilter;
class Target;
class TargetList;
class Thread;

View File

@ -419,6 +419,7 @@
94611EB213CCA4A4003A22AF /* RefCounter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94611EB113CCA4A4003A22AF /* RefCounter.cpp */; };
9463D4CD13B1798800C230D4 /* CommandObjectType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9463D4CC13B1798800C230D4 /* CommandObjectType.cpp */; };
9467E65213C3D97600B3B6F3 /* TypeHierarchyNavigator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9467E65113C3D97600B3B6F3 /* TypeHierarchyNavigator.cpp */; };
94B6E76213D88365005F417F /* ValueObjectSyntheticFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */; };
9A19A6AF1163BBB200E0D453 /* SBValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 9A19A6A51163BB7E00E0D453 /* SBValue.h */; settings = {ATTRIBUTES = (Public, ); }; };
9A19A6B01163BBB300E0D453 /* SBValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A19A6AD1163BB9800E0D453 /* SBValue.cpp */; };
9A22A161135E30370024DDC3 /* EmulateInstructionARM.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A15D135E30370024DDC3 /* EmulateInstructionARM.cpp */; };
@ -1218,6 +1219,8 @@
9467E65413C3D98900B3B6F3 /* TypeHierarchyNavigator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TypeHierarchyNavigator.h; path = include/lldb/Symbol/TypeHierarchyNavigator.h; sourceTree = "<group>"; };
94A9112B13D5DEF80046D8A6 /* FormatClasses.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FormatClasses.h; path = include/lldb/Core/FormatClasses.h; sourceTree = "<group>"; };
94A9112D13D5DF210046D8A6 /* FormatClasses.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormatClasses.cpp; path = source/Core/FormatClasses.cpp; sourceTree = "<group>"; };
94B6E76013D8833C005F417F /* ValueObjectSyntheticFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectSyntheticFilter.h; path = include/lldb/Core/ValueObjectSyntheticFilter.h; sourceTree = "<group>"; };
94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectSyntheticFilter.cpp; path = source/Core/ValueObjectSyntheticFilter.cpp; sourceTree = "<group>"; };
961FABB81235DE1600F93A47 /* FuncUnwinders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FuncUnwinders.cpp; path = source/Symbol/FuncUnwinders.cpp; sourceTree = "<group>"; };
961FABB91235DE1600F93A47 /* UnwindPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindPlan.cpp; path = source/Symbol/UnwindPlan.cpp; sourceTree = "<group>"; };
961FABBA1235DE1600F93A47 /* UnwindTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindTable.cpp; path = source/Symbol/UnwindTable.cpp; sourceTree = "<group>"; };
@ -2079,6 +2082,8 @@
4CABA9DF134A8BCD00539BDD /* ValueObjectMemory.cpp */,
2643343A1110F63C00CDB6C6 /* ValueObjectRegister.h */,
264334381110F63100CDB6C6 /* ValueObjectRegister.cpp */,
94B6E76013D8833C005F417F /* ValueObjectSyntheticFilter.h */,
94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */,
26BC7D8510F1B77400F91463 /* ValueObjectVariable.h */,
26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */,
26BC7D8610F1B77400F91463 /* VMRange.h */,
@ -3361,6 +3366,7 @@
2676045A13D49D2300AB1B6A /* ProcessControl-mig.defs in Sources */,
26D7E45D13D5E30A007FD12B /* SocketAddress.cpp in Sources */,
B271B11413D6139300C3FEDB /* FormatClasses.cpp in Sources */,
94B6E76213D88365005F417F /* ValueObjectSyntheticFilter.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -648,6 +648,8 @@ SBValue::GetDescription (SBStream &description)
lldb::DynamicValueType use_dynamic = eNoDynamicValues;
bool scope_already_checked = false;
bool flat_output = false;
bool use_synthetic = true;
uint32_t no_summary_depth = 0;
ValueObject::DumpValueObject (description.ref(),
m_opaque_sp.get(),
m_opaque_sp->GetName().GetCString(),
@ -656,10 +658,11 @@ SBValue::GetDescription (SBStream &description)
max_depth,
show_types, show_location,
use_objc,
use_dynamic,
use_dynamic,
use_synthetic,
scope_already_checked,
flat_output,
0);
no_summary_depth);
}
else
description.Printf ("No value");

View File

@ -334,6 +334,7 @@ CommandObjectExpression::EvaluateExpression
false, // Show locations of variables, no since this is a host address which we don't care to see
m_options.print_object, // Print the objective C object?
use_dynamic,
true, // Use synthetic children if available
true, // Scope is already checked. Const results are always in scope.
false, // Don't flatten output
0); // Always use summaries (you might want an option --no-summary like there is for frame variable)

View File

@ -499,7 +499,8 @@ public:
m_varobj_options.show_types,
m_varobj_options.show_location,
m_varobj_options.use_objc,
m_varobj_options.use_dynamic,
m_varobj_options.use_dynamic,
m_varobj_options.use_synth,
false,
m_varobj_options.flat_output,
m_varobj_options.no_summary_depth);
@ -552,6 +553,7 @@ public:
m_varobj_options.show_location,
m_varobj_options.use_objc,
m_varobj_options.use_dynamic,
m_varobj_options.use_synth,
false,
m_varobj_options.flat_output,
m_varobj_options.no_summary_depth);
@ -643,6 +645,7 @@ public:
m_varobj_options.show_location,
m_varobj_options.use_objc,
m_varobj_options.use_dynamic,
m_varobj_options.use_synth,
false,
m_varobj_options.flat_output,
m_varobj_options.no_summary_depth);

View File

@ -657,6 +657,7 @@ public:
m_varobj_options.show_location,
m_varobj_options.use_objc,
m_varobj_options.use_dynamic,
m_varobj_options.use_synth,
scope_already_checked,
m_varobj_options.flat_output,
0);

View File

@ -488,6 +488,7 @@ public:
m_varobj_options.show_location,
m_varobj_options.use_objc,
m_varobj_options.use_dynamic,
m_varobj_options.use_synth,
false,
m_varobj_options.flat_output,
m_varobj_options.no_summary_depth);

View File

@ -1226,7 +1226,7 @@ public:
lldb::FormatCategorySP category;
Debugger::Formatting::Categories::Get(ConstString(m_options.m_category), category);
bool delete_category = category->Delete(typeCS.GetCString());
bool delete_category = category->DeleteSummaries(typeCS.GetCString());
bool delete_named = Debugger::Formatting::NamedSummaryFormats::Delete(typeCS);
if (delete_category || delete_named)
@ -1359,7 +1359,7 @@ public:
}
else
Debugger::Formatting::Categories::Get(ConstString(NULL), category);
category->Clear();
category->ClearSummaries();
}
Debugger::Formatting::NamedSummaryFormats::Clear();
@ -1885,6 +1885,670 @@ public:
};
//-------------------------------------------------------------------------
// CommandObjectTypeSynthList
//-------------------------------------------------------------------------
bool CommandObjectTypeSynthList_LoopCallback(void* pt2self, const char* type, const SyntheticFilter::SharedPointer& entry);
class CommandObjectTypeSynthList;
struct CommandObjectTypeSynthList_LoopCallbackParam {
CommandObjectTypeSynthList* self;
CommandReturnObject* result;
RegularExpression* regex;
RegularExpression* cate_regex;
CommandObjectTypeSynthList_LoopCallbackParam(CommandObjectTypeSynthList* S, CommandReturnObject* R,
RegularExpression* X = NULL,
RegularExpression* CX = NULL) : self(S), result(R), regex(X), cate_regex(CX) {}
};
class CommandObjectTypeSynthList : public CommandObject
{
class CommandOptions : public Options
{
public:
CommandOptions (CommandInterpreter &interpreter) :
Options (interpreter)
{
}
virtual
~CommandOptions (){}
virtual Error
SetOptionValue (uint32_t option_idx, const char *option_arg)
{
Error error;
char short_option = (char) m_getopt_table[option_idx].val;
switch (short_option)
{
case 'w':
m_category_regex = std::string(option_arg);
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
}
return error;
}
void
OptionParsingStarting ()
{
m_category_regex = "";
}
const OptionDefinition*
GetDefinitions ()
{
return g_option_table;
}
// Options table: Required for subclasses of Options.
static OptionDefinition g_option_table[];
// Instance variables to hold the values for command options.
std::string m_category_regex;
};
CommandOptions m_options;
virtual Options *
GetOptions ()
{
return &m_options;
}
public:
CommandObjectTypeSynthList (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type synth list",
"Show a list of current synthetic providers.",
NULL), m_options(interpreter)
{
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatOptional;
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
}
~CommandObjectTypeSynthList ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
const size_t argc = command.GetArgumentCount();
CommandObjectTypeSynthList_LoopCallbackParam *param;
RegularExpression* cate_regex =
m_options.m_category_regex.empty() ? NULL :
new RegularExpression(m_options.m_category_regex.c_str());
if (argc == 1) {
RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
regex->Compile(command.GetArgumentAtIndex(0));
param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,regex,cate_regex);
}
else
param = new CommandObjectTypeSynthList_LoopCallbackParam(this,&result,NULL,cate_regex);
Debugger::Formatting::Categories::LoopThrough(PerCategoryCallback,param);
if (cate_regex)
delete cate_regex;
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
private:
static bool
PerCategoryCallback(void* param_vp,
const char* cate_name,
const FormatCategory::SharedPointer& cate)
{
CommandObjectTypeSynthList_LoopCallbackParam* param =
(CommandObjectTypeSynthList_LoopCallbackParam*)param_vp;
CommandReturnObject* result = param->result;
// if the category is disabled or empty and there is no regex, just skip it
if ((cate->IsEnabled() == false || cate->Filter()->GetCount() == 0) && param->cate_regex == NULL)
return true;
// if we have a regex and this category does not match it, just skip it
if(param->cate_regex != NULL && param->cate_regex->Execute(cate_name) == false)
return true;
result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n",
cate_name,
(cate->IsEnabled() ? "enabled" : "disabled"));
cate->Filter()->LoopThrough(CommandObjectTypeSynthList_LoopCallback, param_vp);
return true;
}
bool
LoopCallback (const char* type,
const SyntheticFilter::SharedPointer& entry,
RegularExpression* regex,
CommandReturnObject *result)
{
if (regex == NULL || regex->Execute(type))
result->GetOutputStream().Printf ("%s: %s\n", type, entry->GetDescription().c_str());
return true;
}
friend bool CommandObjectTypeSynthList_LoopCallback(void* pt2self, const char* type, const SyntheticFilter::SharedPointer& entry);
};
bool
CommandObjectTypeSynthList_LoopCallback (void* pt2self,
const char* type,
const SyntheticFilter::SharedPointer& entry)
{
CommandObjectTypeSynthList_LoopCallbackParam* param = (CommandObjectTypeSynthList_LoopCallbackParam*)pt2self;
return param->self->LoopCallback(type, entry, param->regex, param->result);
}
OptionDefinition
CommandObjectTypeSynthList::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "category-regex", 'w', required_argument, NULL, 0, eArgTypeName, "Only show categories matching this filter."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
//-------------------------------------------------------------------------
// CommandObjectTypeSynthDelete
//-------------------------------------------------------------------------
class CommandObjectTypeSynthDelete : public CommandObject
{
private:
class CommandOptions : public Options
{
public:
CommandOptions (CommandInterpreter &interpreter) :
Options (interpreter)
{
}
virtual
~CommandOptions (){}
virtual Error
SetOptionValue (uint32_t option_idx, const char *option_arg)
{
Error error;
char short_option = (char) m_getopt_table[option_idx].val;
switch (short_option)
{
case 'a':
m_delete_all = true;
break;
case 'w':
m_category = ConstString(option_arg).GetCString();
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
}
return error;
}
void
OptionParsingStarting ()
{
m_delete_all = false;
m_category = NULL;
}
const OptionDefinition*
GetDefinitions ()
{
return g_option_table;
}
// Options table: Required for subclasses of Options.
static OptionDefinition g_option_table[];
// Instance variables to hold the values for command options.
bool m_delete_all;
const char* m_category;
};
CommandOptions m_options;
virtual Options *
GetOptions ()
{
return &m_options;
}
static bool
PerCategoryCallback(void* param,
const char* cate_name,
const FormatCategory::SharedPointer& cate)
{
const char* name = (const char*)param;
cate->Filter()->Delete(name);
return true;
}
public:
CommandObjectTypeSynthDelete (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type synth delete",
"Delete an existing synthetic provider for a type.",
NULL), m_options(interpreter)
{
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatPlain;
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
}
~CommandObjectTypeSynthDelete ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
const size_t argc = command.GetArgumentCount();
if (argc != 1)
{
result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
const char* typeA = command.GetArgumentAtIndex(0);
ConstString typeCS(typeA);
if (!typeCS)
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
if (m_options.m_delete_all)
{
Debugger::Formatting::Categories::LoopThrough(PerCategoryCallback, (void*)typeCS.GetCString());
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
lldb::FormatCategorySP category;
Debugger::Formatting::Categories::Get(ConstString(m_options.m_category), category);
bool delete_category = category->Filter()->Delete(typeCS.GetCString());
if (delete_category)
{
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
else
{
result.AppendErrorWithFormat ("no custom synthetic provider for %s.\n", typeA);
result.SetStatus(eReturnStatusFailed);
return false;
}
}
};
OptionDefinition
CommandObjectTypeSynthDelete::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_1, false, "all", 'a', no_argument, NULL, 0, eArgTypeBoolean, "Delete from every category."},
{ LLDB_OPT_SET_2, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Delete from given category."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
//-------------------------------------------------------------------------
// CommandObjectTypeSynthClear
//-------------------------------------------------------------------------
class CommandObjectTypeSynthClear : public CommandObject
{
private:
class CommandOptions : public Options
{
public:
CommandOptions (CommandInterpreter &interpreter) :
Options (interpreter)
{
}
virtual
~CommandOptions (){}
virtual Error
SetOptionValue (uint32_t option_idx, const char *option_arg)
{
Error error;
char short_option = (char) m_getopt_table[option_idx].val;
switch (short_option)
{
case 'a':
m_delete_all = true;
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
}
return error;
}
void
OptionParsingStarting ()
{
m_delete_all = false;
}
const OptionDefinition*
GetDefinitions ()
{
return g_option_table;
}
// Options table: Required for subclasses of Options.
static OptionDefinition g_option_table[];
// Instance variables to hold the values for command options.
bool m_delete_all;
bool m_delete_named;
};
CommandOptions m_options;
virtual Options *
GetOptions ()
{
return &m_options;
}
static bool
PerCategoryCallback(void* param,
const char* cate_name,
const FormatCategory::SharedPointer& cate)
{
cate->Filter()->Clear();
return true;
}
public:
CommandObjectTypeSynthClear (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type synth clear",
"Delete all existing synthetic providers.",
NULL), m_options(interpreter)
{
}
~CommandObjectTypeSynthClear ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
if (m_options.m_delete_all)
Debugger::Formatting::Categories::LoopThrough(PerCategoryCallback, NULL);
else
{
lldb::FormatCategorySP category;
if (command.GetArgumentCount() > 0)
{
const char* cat_name = command.GetArgumentAtIndex(0);
ConstString cat_nameCS(cat_name);
Debugger::Formatting::Categories::Get(cat_nameCS, category);
}
else
Debugger::Formatting::Categories::Get(ConstString(NULL), category);
category->Filter()->Clear();
}
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
};
OptionDefinition
CommandObjectTypeSynthClear::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeBoolean, "Clear every category."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
//-------------------------------------------------------------------------
// CommandObjectTypeSynthAdd
//-------------------------------------------------------------------------
class CommandObjectTypeSynthAdd : public CommandObject
{
private:
class CommandOptions : public Options
{
typedef std::vector<std::string> option_vector;
public:
CommandOptions (CommandInterpreter &interpreter) :
Options (interpreter)
{
}
virtual
~CommandOptions (){}
virtual Error
SetOptionValue (uint32_t option_idx, const char *option_arg)
{
Error error;
char short_option = (char) m_getopt_table[option_idx].val;
bool success;
switch (short_option)
{
case 'C':
m_cascade = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for cascade: %s.\n", option_arg);
break;
case 'c':
m_expr_paths.push_back(option_arg);
break;
case 'p':
m_skip_pointers = true;
break;
case 'r':
m_skip_references = true;
break;
case 'w':
m_category = ConstString(option_arg).GetCString();
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
}
return error;
}
void
OptionParsingStarting ()
{
m_cascade = true;
m_expr_paths.clear();
m_skip_pointers = false;
m_skip_references = false;
m_category = NULL;
}
const OptionDefinition*
GetDefinitions ()
{
return g_option_table;
}
// Options table: Required for subclasses of Options.
static OptionDefinition g_option_table[];
// Instance variables to hold the values for command options.
bool m_cascade;
bool m_skip_references;
bool m_skip_pointers;
option_vector m_expr_paths;
const char* m_category;
typedef option_vector::iterator ExpressionPathsIterator;
};
CommandOptions m_options;
virtual Options *
GetOptions ()
{
return &m_options;
}
public:
CommandObjectTypeSynthAdd (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type synth add",
"Add a new synthetic provider for a type.",
NULL), m_options (interpreter)
{
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatPlus;
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
}
~CommandObjectTypeSynthAdd ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
const size_t argc = command.GetArgumentCount();
if (argc < 1)
{
result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
if (m_options.m_expr_paths.size() == 0)
{
result.AppendErrorWithFormat ("%s needs one or more children.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
SyntheticFilterSP entry;
entry.reset(new SyntheticFilter(m_options.m_cascade,
m_options.m_skip_pointers,
m_options.m_skip_references));
// go through the expression paths
CommandOptions::ExpressionPathsIterator begin, end = m_options.m_expr_paths.end();
for (begin = m_options.m_expr_paths.begin(); begin != end; begin++)
entry->AddExpressionPath(*begin);
// now I have a valid provider, let's add it to every type
lldb::FormatCategorySP category;
Debugger::Formatting::Categories::Get(ConstString(m_options.m_category), category);
for (size_t i = 0; i < argc; i++) {
const char* typeA = command.GetArgumentAtIndex(i);
ConstString typeCS(typeA);
if (typeCS)
category->Filter()->Add(typeCS.GetCString(), entry);
else
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
};
OptionDefinition
CommandObjectTypeSynthAdd::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."},
{ LLDB_OPT_SET_ALL, false, "child", 'c', required_argument, NULL, 0, eArgTypeName, "Include this expression path in the synthetic view."},
{ LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for pointers-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for references-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
class CommandObjectTypeFormat : public CommandObjectMultiword
{
public:
@ -1906,6 +2570,27 @@ public:
}
};
class CommandObjectTypeSynth : public CommandObjectMultiword
{
public:
CommandObjectTypeSynth (CommandInterpreter &interpreter) :
CommandObjectMultiword (interpreter,
"type synth",
"A set of commands for operating on synthetic type representations",
"type synth [<sub-command-options>] ")
{
LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSynthAdd (interpreter)));
LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSynthClear (interpreter)));
LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSynthDelete (interpreter)));
LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSynthList (interpreter)));
}
~CommandObjectTypeSynth ()
{
}
};
class CommandObjectTypeCategory : public CommandObjectMultiword
{
public:
@ -1961,6 +2646,7 @@ CommandObjectType::CommandObjectType (CommandInterpreter &interpreter) :
LoadSubCommand ("category", CommandObjectSP (new CommandObjectTypeCategory (interpreter)));
LoadSubCommand ("format", CommandObjectSP (new CommandObjectTypeFormat (interpreter)));
LoadSubCommand ("summary", CommandObjectSP (new CommandObjectTypeSummary (interpreter)));
LoadSubCommand ("synth", CommandObjectSP (new CommandObjectTypeSynth (interpreter)));
}

View File

@ -733,8 +733,12 @@ ScanFormatDescriptor(const char* var_name_begin,
// if this is a V, print the value using the default format
if (*format_name == 'V')
*val_obj_display = ValueObject::eDisplayValue;
// if this is an L, print the location of the value
if (*format_name == 'L')
*val_obj_display = ValueObject::eDisplayLocation;
// if this is an S, print the summary after all
if (*format_name == 'S')
*val_obj_display = ValueObject::eDisplaySummary;
}
// a good custom format tells us to print the value using it
else
@ -1761,6 +1765,12 @@ Debugger::Formatting::GetSummaryFormat(ValueObject& vobj,
{
return GetFormatManager().Get(vobj, entry);
}
bool
Debugger::Formatting::GetSyntheticFilter(ValueObject& vobj,
lldb::SyntheticFilterSP& entry)
{
return GetFormatManager().Get(vobj, entry);
}
bool
Debugger::Formatting::Categories::Get(const ConstString &category, lldb::FormatCategorySP &entry)
@ -1791,7 +1801,7 @@ Debugger::Formatting::Categories::Clear()
void
Debugger::Formatting::Categories::Clear(ConstString &category)
{
GetFormatManager().Category(category.GetCString())->Clear();
GetFormatManager().Category(category.GetCString())->ClearSummaries();
}
void

View File

@ -66,14 +66,15 @@ StringSummaryFormat::FormatObject(lldb::ValueObjectSP object)
if (m_show_members_oneliner)
{
const uint32_t num_children = object->GetNumChildren();
ValueObjectSP synth_vobj = object->GetSyntheticValue(lldb::eUseSyntheticFilter);
const uint32_t num_children = synth_vobj->GetNumChildren();
if (num_children)
{
s.PutChar('(');
for (uint32_t idx=0; idx<num_children; ++idx)
{
lldb::ValueObjectSP child_sp(object->GetChildAtIndex(idx, true));
lldb::ValueObjectSP child_sp(synth_vobj->GetChildAtIndex(idx, true));
if (child_sp.get())
{
if (idx)
@ -137,4 +138,21 @@ ScriptSummaryFormat::GetDescription()
}
std::string
SyntheticFilter::GetDescription()
{
StreamString sstr;
sstr.Printf("%s%s%s {\n",
m_cascades ? "" : " (not cascading)",
m_skip_pointers ? " (skip pointers)" : "",
m_skip_references ? " (skip references)" : "");
for (int i = 0; i < GetCount(); i++)
{
sstr.Printf(" %s\n",
GetExpressionPathAtIndex(i).c_str());
}
sstr.Printf("}");
return sstr.GetString();
}

View File

@ -26,6 +26,7 @@
#include "lldb/Core/ValueObjectDynamicValue.h"
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Core/ValueObjectMemory.h"
#include "lldb/Core/ValueObjectSyntheticFilter.h"
#include "lldb/Host/Endian.h"
@ -70,12 +71,14 @@ ValueObject::ValueObject (ValueObject &parent) :
m_children (),
m_synthetic_children (),
m_dynamic_value (NULL),
m_synthetic_value(NULL),
m_deref_valobj(NULL),
m_format (eFormatDefault),
m_last_format_mgr_revision(0),
m_last_summary_format(),
m_forced_summary_format(),
m_last_value_format(),
m_last_synthetic_filter(),
m_user_id_of_forced_summary(0),
m_value_is_valid (false),
m_value_did_change (false),
@ -85,6 +88,7 @@ ValueObject::ValueObject (ValueObject &parent) :
m_is_deref_of_parent (false),
m_is_array_item_for_pointer(false),
m_is_bitfield_for_scalar(false),
m_is_expression_path_child(false),
m_dump_printable_counter(0)
{
m_manager->ManageObject(this);
@ -110,12 +114,14 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
m_children (),
m_synthetic_children (),
m_dynamic_value (NULL),
m_synthetic_value(NULL),
m_deref_valobj(NULL),
m_format (eFormatDefault),
m_last_format_mgr_revision(0),
m_last_summary_format(),
m_forced_summary_format(),
m_last_value_format(),
m_last_synthetic_filter(),
m_user_id_of_forced_summary(0),
m_value_is_valid (false),
m_value_did_change (false),
@ -125,6 +131,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
m_is_deref_of_parent (false),
m_is_array_item_for_pointer(false),
m_is_bitfield_for_scalar(false),
m_is_expression_path_child(false),
m_dump_printable_counter(0)
{
m_manager = new ValueObjectManager();
@ -209,14 +216,13 @@ ValueObject::UpdateFormatsIfNeeded()
if (m_last_summary_format.get())
m_last_summary_format.reset((StringSummaryFormat*)NULL);
if (m_last_value_format.get())
m_last_value_format.reset((ValueFormat*)NULL);
Debugger::Formatting::ValueFormats::Get(*this, m_last_value_format);
// to find a summary we look for a direct summary, then if there is none
// we look for a regex summary. if there is none we look for a system
// summary (direct), and if also that fails, we look for a system
// regex summary
m_last_value_format.reset(/*(ValueFormat*)NULL*/);
if (m_last_synthetic_filter.get())
m_last_synthetic_filter.reset(/*(SyntheticFilter*)NULL*/);
Debugger::Formatting::ValueFormats::Get(*this, m_last_value_format);
Debugger::Formatting::GetSummaryFormat(*this, m_last_summary_format);
Debugger::Formatting::GetSyntheticFilter(*this, m_last_synthetic_filter);
m_last_format_mgr_revision = Debugger::Formatting::ValueFormats::GetCurrentRevision();
@ -1423,6 +1429,63 @@ ValueObject::GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_cre
return synthetic_child_sp;
}
// your expression path needs to have a leading . or ->
// (unless it somehow "looks like" an array, in which case it has
// a leading [ symbol). while the [ is meaningful and should be shown
// to the user, . and -> are just parser design, but by no means
// added information for the user.. strip them off
static const char*
SkipLeadingExpressionPathSeparators(const char* expression)
{
if (!expression || !expression[0])
return expression;
if (expression[0] == '.')
return expression+1;
if (expression[0] == '-' && expression[1] == '>')
return expression+2;
return expression;
}
lldb::ValueObjectSP
ValueObject::GetSyntheticExpressionPathChild(const char* expression, bool can_create)
{
ValueObjectSP synthetic_child_sp;
ConstString name_const_string(expression);
// Check if we have already created a synthetic array member in this
// valid object. If we have we will re-use it.
synthetic_child_sp = GetSyntheticChild (name_const_string);
if (!synthetic_child_sp)
{
// We haven't made a synthetic array member for expression yet, so
// lets make one and cache it for any future reference.
synthetic_child_sp = GetValueForExpressionPath(expression);
// Cache the value if we got one back...
if (synthetic_child_sp.get())
{
AddSyntheticChild(name_const_string, synthetic_child_sp.get());
synthetic_child_sp->SetName(SkipLeadingExpressionPathSeparators(expression));
synthetic_child_sp->m_is_expression_path_child = true;
}
}
return synthetic_child_sp;
}
void
ValueObject::CalculateSyntheticValue (lldb::SyntheticValueType use_synthetic)
{
if (use_synthetic == lldb::eNoSyntheticFilter)
return;
UpdateFormatsIfNeeded();
if (m_last_synthetic_filter.get() == NULL)
return;
m_synthetic_value = new ValueObjectSyntheticFilter(*this, m_last_synthetic_filter);
}
void
ValueObject::CalculateDynamicValue (lldb::DynamicValueType use_dynamic)
{
@ -1483,6 +1546,29 @@ ValueObject::GetDynamicValue (DynamicValueType use_dynamic)
return ValueObjectSP();
}
// GetDynamicValue() returns a NULL SharedPointer if the object is not dynamic
// or we do not really want a dynamic VO. this method instead returns this object
// itself when making it synthetic has no meaning. this makes it much simpler
// to replace the SyntheticValue for the ValueObject
ValueObjectSP
ValueObject::GetSyntheticValue (SyntheticValueType use_synthetic)
{
if (use_synthetic == lldb::eNoSyntheticFilter)
return GetSP();
UpdateFormatsIfNeeded();
if (m_last_synthetic_filter.get() == NULL)
return GetSP();
CalculateSyntheticValue(use_synthetic);
if (m_synthetic_value)
return m_synthetic_value->GetSP();
else
return GetSP();
}
bool
ValueObject::GetBaseClassPath (Stream &s)
{
@ -2398,6 +2484,7 @@ ValueObject::DumpValueObject
bool show_location,
bool use_objc,
lldb::DynamicValueType use_dynamic,
bool use_synth,
bool scope_already_checked,
bool flat_output,
uint32_t omit_summary_depth
@ -2545,7 +2632,10 @@ ValueObject::DumpValueObject
if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr))
{
const uint32_t num_children = valobj->GetNumChildren();
ValueObjectSP synth_vobj = valobj->GetSyntheticValue(use_synth ?
lldb::eUseSyntheticFilter :
lldb::eNoSyntheticFilter);
const uint32_t num_children = synth_vobj->GetNumChildren();
if (num_children)
{
if (flat_output)
@ -2562,7 +2652,7 @@ ValueObject::DumpValueObject
for (uint32_t idx=0; idx<num_children; ++idx)
{
ValueObjectSP child_sp(valobj->GetChildAtIndex(idx, true));
ValueObjectSP child_sp(synth_vobj->GetChildAtIndex(idx, true));
if (child_sp.get())
{
DumpValueObject (s,
@ -2575,6 +2665,7 @@ ValueObject::DumpValueObject
show_location,
false,
use_dynamic,
use_synth,
true,
flat_output,
omit_summary_depth > 1 ? omit_summary_depth - 1 : 0);

View File

@ -0,0 +1,170 @@
//===-- ValueObjectSyntheticFilter.cpp -----------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "lldb/Core/ValueObjectSyntheticFilter.h"
// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/FormatClasses.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ValueObjectList.h"
#include "lldb/Core/Value.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/Variable.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h"
using namespace lldb_private;
ValueObjectSyntheticFilter::ValueObjectSyntheticFilter (ValueObject &parent, lldb::SyntheticFilterSP filter) :
ValueObject(parent),
m_address (),
m_type_sp(),
m_use_synthetic (lldb::eUseSyntheticFilter),
m_synth_filter(filter)
{
SetName (parent.GetName().AsCString());
}
ValueObjectSyntheticFilter::~ValueObjectSyntheticFilter()
{
m_owning_valobj_sp.reset();
}
lldb::clang_type_t
ValueObjectSyntheticFilter::GetClangType ()
{
if (m_type_sp)
return m_value.GetClangType();
else
return m_parent->GetClangType();
}
ConstString
ValueObjectSyntheticFilter::GetTypeName()
{
const bool success = UpdateValueIfNeeded();
if (success && m_type_sp)
return ClangASTType::GetConstTypeName (GetClangType());
else
return m_parent->GetTypeName();
}
uint32_t
ValueObjectSyntheticFilter::CalculateNumChildren()
{
const bool success = UpdateValueIfNeeded();
if (!success)
return 0;
if (m_synth_filter.get())
return m_synth_filter->GetCount();
return 0;
if (success && m_type_sp)
return ClangASTContext::GetNumChildren (GetClangAST (), GetClangType(), true);
else
return m_parent->GetNumChildren();
}
clang::ASTContext *
ValueObjectSyntheticFilter::GetClangAST ()
{
const bool success = UpdateValueIfNeeded(false);
if (success && m_type_sp)
return m_type_sp->GetClangAST();
else
return m_parent->GetClangAST ();
}
size_t
ValueObjectSyntheticFilter::GetByteSize()
{
const bool success = UpdateValueIfNeeded();
if (success && m_type_sp)
return m_value.GetValueByteSize(GetClangAST(), NULL);
else
return m_parent->GetByteSize();
}
lldb::ValueType
ValueObjectSyntheticFilter::GetValueType() const
{
return m_parent->GetValueType();
}
bool
ValueObjectSyntheticFilter::UpdateValue ()
{
SetValueIsValid (false);
m_error.Clear();
if (!m_parent->UpdateValueIfNeeded())
{
// our parent could not update.. as we are meaningless without a parent, just stop
if (m_error.Success() && m_parent->GetError().Fail())
m_error = m_parent->GetError();
return false;
}
SetValueIsValid(true);
return true;
}
lldb::ValueObjectSP
ValueObjectSyntheticFilter::GetChildAtIndex (uint32_t idx, bool can_create)
{
if (!m_synth_filter.get())
return lldb::ValueObjectSP();
if (idx >= m_synth_filter->GetCount())
return lldb::ValueObjectSP();
return m_parent->GetSyntheticExpressionPathChild(m_synth_filter->GetExpressionPathAtIndex(idx).c_str(), can_create);
}
lldb::ValueObjectSP
ValueObjectSyntheticFilter::GetChildMemberWithName (const ConstString &name, bool can_create)
{
if (!m_synth_filter.get())
return lldb::ValueObjectSP();
uint32_t idx = GetIndexOfChildWithName(name);
if (idx >= m_synth_filter->GetCount())
return lldb::ValueObjectSP();
return m_parent->GetSyntheticExpressionPathChild(name.GetCString(), can_create);
}
uint32_t
ValueObjectSyntheticFilter::GetIndexOfChildWithName (const ConstString &name)
{
const char* name_cstr = name.GetCString();
for (int i = 0; i < m_synth_filter->GetCount(); i++)
{
const char* expr_cstr = m_synth_filter->GetExpressionPathAtIndex(i).c_str();
if (::strcmp(name_cstr, expr_cstr))
return i;
}
return UINT32_MAX;
}
bool
ValueObjectSyntheticFilter::IsInScope ()
{
return m_parent->IsInScope();
}

View File

@ -31,7 +31,8 @@ static OptionDefinition
g_option_table[] =
{
{ LLDB_OPT_SET_1, false, "dynamic-type", 'd', required_argument, TargetInstanceSettings::g_dynamic_value_types,
0, eArgTypeNone, "Show the object as its full dynamic type, not its static type, if available."},
0, eArgTypeNone, "Show the object as its full dynamic type, not its static type, if available."},
{ LLDB_OPT_SET_1, false, "synthetic-type", 'S', required_argument, NULL, 0, eArgTypeBoolean, "Show the object obeying its synthetic provider, if available."},
{ LLDB_OPT_SET_1, false, "depth", 'D', required_argument, NULL, 0, eArgTypeCount, "Set the max recurse depth when dumping aggregate types (default is infinity)."},
{ LLDB_OPT_SET_1, false, "flat", 'F', no_argument, NULL, 0, eArgTypeNone, "Display results in a flat format that uses expression paths for each variable or member."},
{ LLDB_OPT_SET_1, false, "location", 'L', no_argument, NULL, 0, eArgTypeNone, "Show variable location information."},
@ -105,7 +106,12 @@ OptionGroupValueObjectDisplay::SetOptionValue (CommandInterpreter &interpreter,
else
no_summary_depth = 1;
break;
case 'S':
use_synth = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid synthetic-type '%s'.\n", option_arg);
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
@ -124,6 +130,7 @@ OptionGroupValueObjectDisplay::OptionParsingStarting (CommandInterpreter &interp
use_objc = false;
max_depth = UINT32_MAX;
ptr_depth = 0;
use_synth = true;
Target *target = interpreter.GetExecutionContext().target;
if (target != NULL)

View File

@ -319,8 +319,23 @@ class DataFormatterTestCase(TestBase):
'AShape',
'ARectangleStar'])
# check that synthetic children work into categories
self.runCmd("type synth add Rectangle --child w --category RectangleCategory")
self.runCmd("type category enable RectangleCategory")
self.runCmd("type summary add Rectangle -f \" \" -e --category RectangleCategory")
self.expect('frame variable r2',
substrs = ['w = 9'])
self.runCmd("type summary add Rectangle -f \" \" -e")
self.expect('frame variable r2', matching=False,
substrs = ['h = 16'])
# Now delete all categories
self.runCmd("type category delete CircleCategory RectangleStarCategory BaseCategory")
self.runCmd("type category delete CircleCategory RectangleStarCategory BaseCategory RectangleCategory")
# last of all, check that a deleted category with synth does not blow us up
self.expect('frame variable r2',
substrs = ['w = 9',
'h = 16'])
if __name__ == '__main__':
import atexit

View File

@ -182,7 +182,25 @@ class DataFormatterTestCase(TestBase):
self.expect("frame variable cool_pointer",
substrs = ['3,0,0'])
# test special symbols for formatting variables into summaries
self.runCmd("type summary add -f \"cool object @ ${var%L}\" i_am_cool")
self.runCmd("type summary delete \"i_am_cool [5]\"")
# this test might fail if the compiler tries to store
# these values into registers.. hopefully this is not
# going to be the case
self.expect("frame variable cool_array",
substrs = ['[0] = cool object @ 0x',
'[1] = cool object @ 0x',
'[2] = cool object @ 0x',
'[3] = cool object @ 0x',
'[4] = cool object @ 0x'])
self.runCmd("type summary add -f \"goofy\" i_am_cool")
self.runCmd("type summary add -f \"${var.second_cool%S}\" i_am_cooler")
self.expect("frame variable the_coolest_guy",
substrs = ['(i_am_cooler) the_coolest_guy = goofy'])
if __name__ == '__main__':
import atexit

View File

@ -110,6 +110,8 @@ int main (int argc, const char * argv[])
char strarr[32] = "Hello world!";
char* strptr = "Hello world!";
i_am_cooler the_coolest_guy(1,2,3.14,6.28,'E','G');
return 0; // Set break point at this line.
}

View File

@ -0,0 +1,5 @@
LEVEL = ../../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,203 @@
"""
Test lldb data formatter subsystem.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
class DataFormatterTestCase(TestBase):
mydir = os.path.join("functionalities", "data-formatter", "data-formatter-synth")
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
def test_with_dsym_and_run_command(self):
"""Test data formatter commands."""
self.buildDsym()
self.data_formatter_commands()
def test_with_dwarf_and_run_command(self):
"""Test data formatter commands."""
self.buildDwarf()
self.data_formatter_commands()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line number to break at.
self.line = line_number('main.cpp', '// Set break point at this line.')
def data_formatter_commands(self):
"""Test that that file and class static variables display correctly."""
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
self.expect("breakpoint set -f main.cpp -l %d" % self.line,
BREAKPOINT_CREATED,
startstr = "Breakpoint created: 1: file ='main.cpp', line = %d, locations = 1" %
self.line)
self.runCmd("run", RUN_SUCCEEDED)
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# This is the function to remove the custom formats in order to have a
# clean slate for the next test case.
def cleanup():
self.runCmd('type format clear', check=False)
self.runCmd('type summary clear', check=False)
self.runCmd('type synth clear', check=False)
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
# Pick some values and check that the basics work
self.runCmd("type synth add BagOfInts --child x --child z")
self.expect("frame variable int_bag",
substrs = ['x = 6',
'z = 8'])
# Check we can still access the missing child by summary
self.runCmd("type summary add BagOfInts -f \"y=${var.y}\"")
self.expect('frame variable int_bag',
substrs = ['y=7'])
# Even if we have synth children, the summary prevails
self.expect("frame variable int_bag", matching=False,
substrs = ['x = 6',
'z = 8'])
# Summary+Synth must work together
self.runCmd("type summary add BagOfInts -f \"y=${var.y}\" -e")
self.expect('frame variable int_bag',
substrs = ['y=7',
'x = 6',
'z = 8'])
# Same output, but using Python
self.runCmd("type summary add BagOfInts -s \"return 'y='+valobj.GetChildMemberWithName('y').GetValue()\" -e")
self.expect('frame variable int_bag',
substrs = ['y=7',
'x = 6',
'z = 8'])
# If I skip summaries, still give me the artificial children
self.expect("frame variable int_bag -Y1",
substrs = ['x = 6',
'z = 8'])
# Delete synth and check that the view reflects it immediately
self.runCmd("type synth delete BagOfInts")
self.expect("frame variable int_bag",
substrs = ['x = 6',
'y = 7',
'z = 8'])
# Add the synth again and check that it's honored deeper in the hierarchy
self.runCmd("type synth add BagOfInts --child x --child z")
self.expect('frame variable bag_bag',
substrs = ['x = y=70 {',
'x = 69',
'z = 71',
'y = y=67 {',
'x = 66',
'z = 68'])
self.expect('frame variable bag_bag', matching=False,
substrs = ['y = 70',
'y = 67'])
# Check that a synth can expand nested stuff
self.runCmd("type synth add BagOfBags --child x.y --child y.z")
self.expect('frame variable bag_bag',
substrs = ['x.y = 70',
'y.z = 68'])
# ...even if we get -> and . wrong
self.runCmd("type synth add BagOfBags --child x.y --child \"y->z\"")
self.expect('frame variable bag_bag',
substrs = ['x.y = 70',
'y->z = 68'])
# ...even bitfields
self.runCmd("type synth add BagOfBags --child x.y --child \"y->z[1-2]\"")
self.expect('frame variable bag_bag -T',
substrs = ['x.y = 70',
'(int:2) y->z[1-2] = 2'])
# ...even if we format the bitfields
self.runCmd("type synth add BagOfBags --child x.y --child \"y->y[0-0]\"")
self.runCmd("type format add \"int:1\" -f bool")
self.expect('frame variable bag_bag -T',
substrs = ['x.y = 70',
'(int:1) y->y[0-0] = true'])
# ...even if we use one-liner summaries
self.runCmd("type summary add -c BagOfBags")
self.expect('frame variable bag_bag',
substrs = ['(BagOfBags) bag_bag = (x.y=70, y->y[0-0]=true)'])
self.runCmd("type summary delete BagOfBags")
# now check we are dynamic (and arrays work)
self.runCmd("type synth add Plenty --child bitfield --child array[0] --child array[2]")
self.expect('frame variable plenty_of_stuff',
substrs = ['bitfield = 1',
'array[0] = 5',
'array[2] = 3'])
self.runCmd("n")
self.expect('frame variable plenty_of_stuff',
substrs = ['bitfield = 17',
'array[0] = 5',
'array[2] = 3'])
# skip synthetic children
self.expect('frame variable plenty_of_stuff -S no',
substrs = ['some_values = 0x0',
'array = 0x',
'array_size = 5'])
# check flat printing with synthetic children
self.expect('frame variable plenty_of_stuff --flat',
substrs = ['plenty_of_stuff.bitfield = 17',
'*(plenty_of_stuff.array) = 5',
'*(plenty_of_stuff.array) = 3'])
# check that we do not lose location information for our children
self.expect('frame variable plenty_of_stuff -L',
substrs = ['0x',
': bitfield = 17'])
# check we work across pointer boundaries
self.expect('frame variable plenty_of_stuff.some_values -P1',
substrs = ['(BagOfInts *) plenty_of_stuff.some_values',
'x = 5',
'z = 7'])
# but not if we don't want to
self.runCmd("type synth add BagOfInts --child x --child z -p")
self.expect('frame variable plenty_of_stuff.some_values -P1',
substrs = ['(BagOfInts *) plenty_of_stuff.some_values',
'x = 5',
'y = 6',
'z = 7'])
# check we're dynamic even if nested
self.runCmd("type synth add BagOfBags --child x.z")
self.expect('frame variable bag_bag',
substrs = ['x.z = 71'])
self.runCmd("n")
self.expect('frame variable bag_bag',
substrs = ['x.z = 12'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,83 @@
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
struct BagOfInts
{
int x;
int y;
int z;
BagOfInts(int X) :
x(X),
y(X+1),
z(X+2) {}
};
struct BagOfFloats
{
float x;
float y;
float z;
BagOfFloats(float X) :
x(X+0.334),
y(X+0.500),
z(X+0.667) {}
};
struct BagOfBags
{
BagOfInts x;
BagOfInts y;
BagOfFloats z;
BagOfFloats q;
BagOfBags() :
x('E'),
y('B'),
z(1.1),
q(20.11) {}
};
struct Plenty
{
BagOfInts *some_values;
int* array;
int array_size;
int bitfield;
Plenty(int N, bool flagA, bool flagB) :
some_values(new BagOfInts(N)),
array(new int[N]),
array_size(N),
bitfield( (flagA ? 0x01 : 0x00) | (flagB ? 0x10 : 0x00) )
{
for (int j = 0; j < N; j++)
array[j] = N-j;
}
};
int main (int argc, const char * argv[])
{
BagOfInts int_bag(6);
BagOfFloats float_bag(2.71);
BagOfBags bag_bag;
Plenty plenty_of_stuff(5,true,false);
plenty_of_stuff.bitfield = 0x11; // Set break point at this line.
bag_bag.x.z = 12;
return 0;
}

View File

@ -495,20 +495,38 @@
in fact it has a similar effect. If you add a % sign
followed by any one format name or abbreviation from the
above table after an expression path, the resulting
object will be displyed using the chosen format (this is
applicable to non-aggregate types only, with a few
special exceptions discussed below). </p>
<p>There are two more special format symbols that you can
use only as part of a summary string: <code>%V</code>
and <code>%@</code>. The first one tells LLDB to ignore
summary strings for the type of the object referred by
the expression path and instead print the object's
value. The second is only applicable to Objective-C
classes, and tells LLDB to get the object's description
from the Objective-C runtime. By default, if no format
is provided, LLDB will try to get the object's summary,
and if empty the object's value. If neither can be
obtained, nothing will be displayed.</p>
object will be displyed using the chosen format.</p>
<p>You can also use some other special format markers, not available
for type formatters, but which carry a special meaning when used in this
context:</p>
<table border="1">
<tbody>
<tr valign="top">
<td width="23%"><b>Symbol</b></td>
<td><b>Description</b></td>
</tr>
<tr valign="top">
<td><b>%S</b></td>
<td>Use this object's summary (the default for aggregate types)</td>
</tr>
<tr valign="top">
<td><b>%V</b></td>
<td>Use this object's value (the default for non-aggregate types)</td>
</tr>
<tr valign="top">
<td><b>%@</b></td>
<td>Use a language-runtime specific description (for C++ this does nothing,
for Objective-C it calls the NSPrintForDebugger API)</td>
</tr>
<tr valign="top">
<td><b>%L</b></td>
<td>Use this object's location (memory address, register name, ...)</td>
</tr>
</tbody>
</table>
<p>As previously said, pointers and values are treated the
same way when getting to their members in an expression
path. However, if your expression path leads to a