diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index b06db9a090f6..7e374cbb2c82 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -505,6 +505,9 @@ public: static bool GetSummaryFormat(ValueObject& vobj, lldb::SummaryFormatSP& entry); + static bool + GetSyntheticFilter(ValueObject& vobj, + lldb::SyntheticFilterSP& entry); class NamedSummaryFormats { diff --git a/lldb/include/lldb/Core/FormatClasses.h b/lldb/include/lldb/Core/FormatClasses.h index a6002abb54e9..c67372138a7b 100644 --- a/lldb/include/lldb/Core/FormatClasses.h +++ b/lldb/include/lldb/Core/FormatClasses.h @@ -17,6 +17,7 @@ // C++ Includes #include +#include // 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 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::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; } diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h index 1a6f49859544..1dd97b04d263 100644 --- a/lldb/include/lldb/Core/FormatManager.h +++ b/lldb/include/lldb/Core/FormatManager.h @@ -415,12 +415,15 @@ class FormatCategory private: typedef FormatNavigator SummaryNavigator; typedef FormatNavigator RegexSummaryNavigator; + typedef FormatNavigator 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::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 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, diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 1c3eddc8b25d..8eb018e2113e 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -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 m_children; std::map 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 * diff --git a/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h b/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h new file mode 100644 index 000000000000..571fba37dc06 --- /dev/null +++ b/lldb/include/lldb/Core/ValueObjectSyntheticFilter.h @@ -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 +#include +// 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_ diff --git a/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h b/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h index 8237621abcd7..c714525c366d 100644 --- a/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h +++ b/lldb/include/lldb/Interpreter/OptionGroupValueObjectDisplay.h @@ -54,6 +54,7 @@ public: uint32_t max_depth; uint32_t ptr_depth; lldb::DynamicValueType use_dynamic; + bool use_synth; }; } // namespace lldb_private diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index c934073aa21d..e7961c88c727 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -324,6 +324,12 @@ namespace lldb { eDynamicDontRunTarget = 2 } DynamicValueType; + typedef enum SyntheticValueType + { + eNoSyntheticFilter = 0, + eUseSyntheticFilter = 1 + } SyntheticValueType; + typedef enum AccessType { eAccessNone, diff --git a/lldb/include/lldb/lldb-forward-rtti.h b/lldb/include/lldb/lldb-forward-rtti.h index e07771a8cb26..faedfe6a47cd 100644 --- a/lldb/include/lldb/lldb-forward-rtti.h +++ b/lldb/include/lldb/lldb-forward-rtti.h @@ -67,6 +67,7 @@ namespace lldb { typedef SharedPtr::Type SummaryFormatSP; typedef SharedPtr::Type SymbolFileSP; typedef SharedPtr::Type SymbolContextSpecifierSP; + typedef SharedPtr::Type SyntheticFilterSP; typedef SharedPtr::Type TargetSP; typedef SharedPtr::Type ThreadSP; typedef SharedPtr::Type ThreadPlanSP; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 4e4938bca7c0..412d9de8e10c 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -144,6 +144,7 @@ class SymbolContextSpecifier; class SymbolFile; class SymbolVendor; class Symtab; +class SyntheticFilter; class Target; class TargetList; class Thread; diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index a319f6c61bb8..18c9a3fe74c4 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -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 = ""; }; 94A9112B13D5DEF80046D8A6 /* FormatClasses.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FormatClasses.h; path = include/lldb/Core/FormatClasses.h; sourceTree = ""; }; 94A9112D13D5DF210046D8A6 /* FormatClasses.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FormatClasses.cpp; path = source/Core/FormatClasses.cpp; sourceTree = ""; }; + 94B6E76013D8833C005F417F /* ValueObjectSyntheticFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ValueObjectSyntheticFilter.h; path = include/lldb/Core/ValueObjectSyntheticFilter.h; sourceTree = ""; }; + 94B6E76113D88362005F417F /* ValueObjectSyntheticFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectSyntheticFilter.cpp; path = source/Core/ValueObjectSyntheticFilter.cpp; sourceTree = ""; }; 961FABB81235DE1600F93A47 /* FuncUnwinders.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FuncUnwinders.cpp; path = source/Symbol/FuncUnwinders.cpp; sourceTree = ""; }; 961FABB91235DE1600F93A47 /* UnwindPlan.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindPlan.cpp; path = source/Symbol/UnwindPlan.cpp; sourceTree = ""; }; 961FABBA1235DE1600F93A47 /* UnwindTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UnwindTable.cpp; path = source/Symbol/UnwindTable.cpp; sourceTree = ""; }; @@ -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; }; diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp index 26eaaf32209e..2528f4c361f5 100644 --- a/lldb/source/API/SBValue.cpp +++ b/lldb/source/API/SBValue.cpp @@ -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"); diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index cab8ed549fce..6109b643fdcb 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -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) diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index 12670a1e9e51..eedc0cc1ca02 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -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); diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index e91490ccf273..2a2c936e3520 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -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); diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 1af50f0b76e8..814a2ad3144d 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -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); diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index c7b74d7306ba..3a8b16cf352c 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -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 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 [] ") + { + 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))); } diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index cce768f86845..219fbfa89854 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -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 diff --git a/lldb/source/Core/FormatClasses.cpp b/lldb/source/Core/FormatClasses.cpp index 8ff237a603a3..f301a7bbc3a3 100644 --- a/lldb/source/Core/FormatClasses.cpp +++ b/lldb/source/Core/FormatClasses.cpp @@ -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; idxGetChildAtIndex(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(); +} diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 5721b40f548f..acc85df556ff 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -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; idxGetChildAtIndex(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); diff --git a/lldb/source/Core/ValueObjectSyntheticFilter.cpp b/lldb/source/Core/ValueObjectSyntheticFilter.cpp new file mode 100644 index 000000000000..4756b1cea29f --- /dev/null +++ b/lldb/source/Core/ValueObjectSyntheticFilter.cpp @@ -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(); +} + diff --git a/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp index b8a767c1780b..b2ca4ae7213c 100644 --- a/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp +++ b/lldb/source/Interpreter/OptionGroupValueObjectDisplay.cpp @@ -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) diff --git a/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py b/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py index b48462e69c50..ac370f70a184 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py @@ -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 diff --git a/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py b/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py index f5a206b39f45..97aae5c8e1cb 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py @@ -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 diff --git a/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp index 9224cb8a0a00..31f7c6bf9349 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp +++ b/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp @@ -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. } diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synth/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-synth/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-synth/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py b/lldb/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py new file mode 100644 index 000000000000..c542e6a7735a --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-synth/TestDataFormatterSynth.py @@ -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() diff --git a/lldb/test/functionalities/data-formatter/data-formatter-synth/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-synth/main.cpp new file mode 100644 index 000000000000..11e279391380 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-synth/main.cpp @@ -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 +#include +#include + +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; + +} + diff --git a/lldb/www/varformats.html b/lldb/www/varformats.html index 9260e0d65256..3b234c9c4a90 100755 --- a/lldb/www/varformats.html +++ b/lldb/www/varformats.html @@ -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).

-

There are two more special format symbols that you can - use only as part of a summary string: %V - and %@. 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.

+ object will be displyed using the chosen format.

+ +

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:

+ + + + + + + + + + + + + + + + + + + + + + + + +
SymbolDescription
%SUse this object's summary (the default for aggregate types)
%VUse this object's value (the default for non-aggregate types)
%@Use a language-runtime specific description (for C++ this does nothing, + for Objective-C it calls the NSPrintForDebugger API)
%LUse this object's location (memory address, register name, ...)
+

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