From 1490c6fd8fa7a5a2cdfe2d6d0fb4eedf0d601a2d Mon Sep 17 00:00:00 2001 From: Enrico Granata Date: Tue, 19 Jul 2011 02:34:21 +0000 Subject: [PATCH] Fixed a bug where deleting a regex summary would not immediately reflect in the variables display The "systemwide summaries" feature has been removed and replaced with a more general and powerful mechanism. Categories: - summaries can now be grouped into buckets, called "categories" (it is expected that categories correspond to libraries and/or runtime environments) - to add a summary to a category, you can use the -w option to type summary add and give a category name (e.g. type summary add -f "foo" foo_t -w foo_category) - categories are by default disabled, which means LLDB will not look into them for summaries, to enable a category use "type category enable". once a category is enabled, LLDB will look into that category for summaries. the rules are quite trivial: every enabled category is searched for an exact match. if an exact match is nowhere to be found, any match is searched for in every enabled category (whether it involves cascading, going to base classes, ...). categories are searched into the order in which they were enabled (the most recently enabled category first, then the second most and so on..) - by default, most commands that deal with summaries, use a category named "default" if no explicit -w parameter is given (the observable behavior of LLDB should not change when categories are not explicitly used) - the systemwide summaries are now part of a "system" category llvm-svn: 135463 --- lldb/include/lldb/Core/Debugger.h | 235 ++++------ lldb/include/lldb/Core/FormatManager.h | 410 +++++++++++++--- lldb/include/lldb/lldb-enumerations.h | 14 + lldb/include/lldb/lldb-forward-rtti.h | 1 + lldb/include/lldb/lldb-forward.h | 1 + lldb/source/Commands/CommandObjectFrame.cpp | 2 +- lldb/source/Commands/CommandObjectType.cpp | 436 ++++++++++++++---- lldb/source/Commands/CommandObjectType.h | 11 +- lldb/source/Core/Debugger.cpp | 238 ++++------ lldb/source/Core/FormatManager.cpp | 22 + lldb/source/Core/ValueObject.cpp | 13 +- .../data-formatter-categories/Makefile | 5 + .../TestDataFormatterCategories.py | 252 ++++++++++ .../data-formatter-categories/main.cpp | 37 ++ .../data-formatter-globals/Makefile | 5 + .../TestDataFormatterGlobals.py | 83 ++++ .../data-formatter-globals/main.cpp | 27 ++ .../TestDataFormatterSkipSummary.py | 2 +- lldb/www/varformats.html | 135 +++++- 19 files changed, 1462 insertions(+), 467 deletions(-) create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-categories/Makefile create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-categories/main.cpp create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-globals/Makefile create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-globals/TestDataFormatterGlobals.py create mode 100644 lldb/test/functionalities/data-formatter/data-formatter-globals/main.cpp diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 7bbdf0632fbf..af8524144be8 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -474,159 +474,100 @@ private: public: - class ValueFormats + class Formatting { public: - static bool - Get(ValueObject& vobj, ValueFormat::SharedPointer &entry); + class ValueFormats + { + public: + static bool + Get(ValueObject& vobj, ValueFormat::SharedPointer &entry); + + static void + Add(const ConstString &type, const ValueFormat::SharedPointer &entry); + + static bool + Delete(const ConstString &type); + + static void + Clear(); + + static void + LoopThrough(ValueFormat::ValueCallback callback, void* callback_baton); + + static uint32_t + GetCurrentRevision(); + + static uint32_t + GetCount(); + }; - static void - Add(const ConstString &type, const ValueFormat::SharedPointer &entry); + static lldb::FormatCategorySP + SummaryFormats(const char* category_name = NULL); static bool - Delete(const ConstString &type); + GetSummaryFormat(ValueObject& vobj, + lldb::SummaryFormatSP& entry); - static void - Clear(); - - static void - LoopThrough(ValueFormat::ValueCallback callback, void* callback_baton); - - static uint32_t - GetCurrentRevision(); - - static uint32_t - GetCount(); - }; - - class SummaryFormats - { - public: - - static bool - Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry); - - static void - Add(const ConstString &type, const SummaryFormat::SharedPointer &entry); - - static bool - Delete(const ConstString &type); - - static void - Clear(); - - static void - LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton); - - static uint32_t - GetCurrentRevision(); - - static uint32_t - GetCount(); - }; - - class SystemSummaryFormats - { - public: - - static bool - Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry); - - static void - Add(const ConstString &type, const SummaryFormat::SharedPointer &entry); - - static bool - Delete(const ConstString &type); - - static void - Clear(); - - static void - LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton); - - static uint32_t - GetCurrentRevision(); - - static uint32_t - GetCount(); - }; - - class RegexSummaryFormats - { - public: - - static bool - Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry); - - static void - Add(const lldb::RegularExpressionSP &type, const SummaryFormat::SharedPointer &entry); - - static bool - Delete(const ConstString &type); - - static void - Clear(); - - static void - LoopThrough(SummaryFormat::RegexSummaryCallback callback, void* callback_baton); - - static uint32_t - GetCurrentRevision(); - - static uint32_t - GetCount(); - }; - - class SystemRegexSummaryFormats - { - public: - - static bool - Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry); - - static void - Add(const lldb::RegularExpressionSP &type, const SummaryFormat::SharedPointer &entry); - - static bool - Delete(const ConstString &type); - - static void - Clear(); - - static void - LoopThrough(SummaryFormat::RegexSummaryCallback callback, void* callback_baton); - - static uint32_t - GetCurrentRevision(); - - static uint32_t - GetCount(); - }; - - class NamedSummaryFormats - { - public: - - static bool - Get(const ConstString &type, SummaryFormat::SharedPointer &entry); - - static void - Add(const ConstString &type, const SummaryFormat::SharedPointer &entry); - - static bool - Delete(const ConstString &type); - - static void - Clear(); - - static void - LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton); - - static uint32_t - GetCurrentRevision(); - - static uint32_t - GetCount(); + class NamedSummaryFormats + { + public: + static bool + Get(const ConstString &type, SummaryFormat::SharedPointer &entry); + + static void + Add(const ConstString &type, const SummaryFormat::SharedPointer &entry); + + static bool + Delete(const ConstString &type); + + static void + Clear(); + + static void + LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton); + + static uint32_t + GetCurrentRevision(); + + static uint32_t + GetCount(); + }; + + class Categories + { + public: + + static bool + Get(const ConstString &category, lldb::FormatCategorySP &entry); + + static void + Add(const ConstString &category); + + static bool + Delete(const ConstString &category); + + static void + Clear(); + + static void + Clear(ConstString &category); + + static void + Enable(ConstString& category); + + static void + Disable(ConstString& category); + + static void + LoopThrough(FormatManager::CategoryCallback callback, void* callback_baton); + + static uint32_t + GetCurrentRevision(); + + static uint32_t + GetCount(); + }; }; }; diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h index 42579fc6b1cc..69bc63a3fb33 100644 --- a/lldb/include/lldb/Core/FormatManager.h +++ b/lldb/include/lldb/Core/FormatManager.h @@ -29,6 +29,7 @@ namespace std #include #endif +#include #include #include @@ -152,20 +153,24 @@ struct SummaryFormat bool m_show_members_oneliner; bool m_is_system; + uint32_t m_priority; + SummaryFormat(bool casc = false, bool skipptr = false, bool skipref = false, bool nochildren = true, bool novalue = true, bool oneliner = false, - bool system = false) : + bool system = false, + uint32_t priority = 2) : m_cascades(casc), m_skip_pointers(skipptr), m_skip_references(skipref), m_dont_show_children(nochildren), m_dont_show_value(novalue), m_show_members_oneliner(oneliner), - m_is_system(system) + m_is_system(system), + m_priority(priority) { } @@ -209,6 +214,18 @@ struct SummaryFormat return m_is_system; } + uint32_t + GetPriority() const + { + return m_priority; + } + + void + SetPriority(uint32_t newprio) + { + m_priority = newprio; + } + virtual ~SummaryFormat() { @@ -238,8 +255,9 @@ struct StringSummaryFormat : public SummaryFormat bool novalue = true, bool oneliner = false, bool system = false, - std::string f = "") : - SummaryFormat(casc,skipptr,skipref,nochildren,novalue,oneliner,system), + std::string f = "", + uint32_t priority = 2) : + SummaryFormat(casc,skipptr,skipref,nochildren,novalue,oneliner,system, priority), m_format(f) { } @@ -289,8 +307,9 @@ struct ScriptSummaryFormat : public SummaryFormat bool oneliner = false, bool system = false, std::string fname = "", - std::string pscri = "") : - SummaryFormat(casc,skipptr,skipref,nochildren,novalue,oneliner,system), + std::string pscri = "", + uint32_t priority = 2) : + SummaryFormat(casc,skipptr,skipref,nochildren,novalue,oneliner,system,priority), m_function_name(fname), m_python_script(pscri) { @@ -422,6 +441,8 @@ struct ScriptFormat template class FormatNavigator; +class FormatManager; + template class FormatMap { @@ -431,6 +452,7 @@ private: IFormatChangeListener* listener; friend class FormatNavigator; + friend class FormatManager; public: typedef std::map MapType; @@ -528,6 +550,8 @@ public: }; +class FormatCategory; + template class FormatNavigator { @@ -542,6 +566,10 @@ public: typedef typename MapType::key_type MapKeyType; typedef typename MapType::mapped_type MapValueType; typedef typename BackEndType::CallbackType CallbackType; + + typedef typename lldb::SharedPtr >::Type SharedPointer; + + friend class FormatCategory; FormatNavigator(IFormatChangeListener* lst = NULL) : m_format_map(lst) @@ -561,16 +589,21 @@ public: { return m_format_map.Delete(type); } - + bool - Get(ValueObject& vobj, MapValueType& entry) + Get(ValueObject& vobj, + MapValueType& entry, + uint32_t* why = NULL) { + uint32_t value = lldb::eFormatterDirectChoice; clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType()); - bool ret = Get(vobj, type, entry); - if(ret) + bool ret = Get(vobj, type, entry, value); + if (ret) entry = MapValueType(entry); else - entry = MapValueType(); + entry = MapValueType(); + if (why) + *why = value; return ret; } @@ -591,7 +624,7 @@ public: { return m_format_map.GetCount(); } - + private: DISALLOW_COPY_AND_ASSIGN(FormatNavigator); @@ -606,7 +639,8 @@ private: bool Get(ValueObject& vobj, clang::QualType type, - MapValueType& entry) + MapValueType& entry, + uint32_t& reason) { if (type.isNull()) return false; @@ -629,13 +663,19 @@ private: // look for a "base type", whatever that means if (typePtr->isReferenceType()) { - if (Get(vobj,type.getNonReferenceType(),entry) && !entry->m_skip_references) + if (Get(vobj,type.getNonReferenceType(),entry, reason) && !entry->m_skip_references) + { + reason |= lldb::eFormatterStrippedPointerReference; return true; + } } if (typePtr->isPointerType()) { - if (Get(vobj, typePtr->getPointeeType(), entry) && !entry->m_skip_pointers) + if (Get(vobj, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers) + { + reason |= lldb::eFormatterStrippedPointerReference; return true; + } } if (typePtr->isObjCObjectPointerType()) { @@ -649,8 +689,11 @@ private: ValueObject* target = vobj.Dereference(error).get(); if(error.Fail() || !target) return false; - if (Get(*target, typePtr->getPointeeType(), entry) && !entry->m_skip_pointers) + if (Get(*target, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers) + { + reason |= lldb::eFormatterStrippedPointerReference; return true; + } } const clang::ObjCObjectType *objc_class_type = typePtr->getAs(); if (objc_class_type) @@ -669,8 +712,11 @@ private: { //printf("the end is here\n"); clang::QualType ivar_qual_type(ast->getObjCInterfaceType(superclass_interface_decl)); - if (Get(vobj, ivar_qual_type, entry) && entry->m_cascades) + if (Get(vobj, ivar_qual_type, entry, reason) && entry->m_cascades) + { + reason |= lldb::eFormatterNavigatedBaseClasses; return true; + } } } } @@ -691,8 +737,11 @@ private: end = record->bases_end(); for (pos = record->bases_begin(); pos != end; pos++) { - if((Get(vobj, pos->getType(), entry)) && entry->m_cascades) - return true; // if it does not cascade, just move on to other base classes which might + if((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades) + { + reason |= lldb::eFormatterNavigatedBaseClasses; + return true; + } } } if (record->getNumVBases() > 0) @@ -700,8 +749,11 @@ private: end = record->vbases_end(); for (pos = record->vbases_begin(); pos != end; pos++) { - if((Get(vobj, pos->getType(), entry)) && entry->m_cascades) + if((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades) + { + reason |= lldb::eFormatterNavigatedBaseClasses; return true; + } } } } @@ -711,8 +763,11 @@ private: const clang::TypedefType* type_tdef = type->getAs(); if (type_tdef) { - if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry)) && entry->m_cascades) + if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry, reason)) && entry->m_cascades) + { + reason |= lldb::eFormatterNavigatedTypedefs; return true; + } } return false; } @@ -726,42 +781,194 @@ template<> bool FormatNavigator::Delete(const char* type); +class FormatCategory +{ +private: + typedef FormatNavigator SummaryNavigator; + typedef FormatNavigator RegexSummaryNavigator; + + typedef SummaryNavigator::MapType SummaryMap; + typedef RegexSummaryNavigator::MapType RegexSummaryMap; + + SummaryNavigator::SharedPointer m_summary_nav; + RegexSummaryNavigator::SharedPointer m_regex_summary_nav; + + bool m_enabled; + + IFormatChangeListener* m_change_listener; + + Mutex m_mutex; + +public: + + typedef SummaryNavigator::SharedPointer SummaryNavigatorSP; + typedef RegexSummaryNavigator::SharedPointer RegexSummaryNavigatorSP; + + FormatCategory(IFormatChangeListener* clist) : + m_summary_nav(new SummaryNavigator(clist)), + m_regex_summary_nav(new RegexSummaryNavigator(clist)), + m_enabled(false), + m_change_listener(clist), + m_mutex(Mutex::eMutexTypeRecursive) + {} + + SummaryNavigatorSP + Summary() + { + return SummaryNavigatorSP(m_summary_nav); + } + + RegexSummaryNavigatorSP + RegexSummary() + { + return RegexSummaryNavigatorSP(m_regex_summary_nav); + } + + bool + IsEnabled() const + { + return m_enabled; + } + + void + Enable(bool value = true) + { + Mutex::Locker(m_mutex); + m_enabled = value; + if(m_change_listener) + m_change_listener->Changed(); + } + + void + Disable() + { + Enable(false); + } + + bool + Get(ValueObject& vobj, + lldb::SummaryFormatSP& entry, + uint32_t* reason = NULL) + { + if (!IsEnabled()) + return false; + if (Summary()->Get(vobj, entry, reason)) + return true; + bool regex = RegexSummary()->Get(vobj, entry, reason); + if (regex && reason) + *reason |= lldb::eFormatterRegularExpressionSummary; // penalize regex summaries over normal ones + return regex; + } + + void + Clear() + { + m_summary_nav->Clear(); + m_regex_summary_nav->Clear(); + } + + bool + Delete(const char* name) + { + bool del_sum = m_summary_nav->Delete(name); + bool del_rex = m_regex_summary_nav->Delete(name); + + return (del_sum || del_rex); + } + + void + ChooseAsPreferential(const char* name); + + typedef lldb::SharedPtr::Type SharedPointer; +}; + class FormatManager : public IFormatChangeListener { private: typedef FormatNavigator ValueNavigator; - typedef FormatNavigator SummaryNavigator; - typedef FormatNavigator RegexSummaryNavigator; - typedef FormatNavigator ScriptNavigator; - + + typedef FormatMap CategoryMap; + typedef ValueNavigator::MapType ValueMap; - typedef SummaryNavigator::MapType SummaryMap; - typedef RegexSummaryNavigator::MapType RegexSummaryMap; typedef FormatMap NamedSummariesMap; - typedef ScriptNavigator::MapType ScriptMap; + + typedef std::list ActiveCategoriesList; + + typedef ActiveCategoriesList::iterator ActiveCategoriesIterator; ValueNavigator m_value_nav; - SummaryNavigator m_summary_nav; - SummaryNavigator m_system_summary_nav; - RegexSummaryNavigator m_regex_summary_nav; - RegexSummaryNavigator m_system_regex_summary_nav; - NamedSummariesMap m_named_summaries_map; - uint32_t m_last_revision; + CategoryMap m_categories_map; + ActiveCategoriesList m_active_categories; + + const char* m_default_category_name; + const char* m_system_category_name; + + typedef CategoryMap::MapType::iterator CategoryMapIterator; + + bool + Get_ExactMatch(ValueObject& vobj, + lldb::SummaryFormatSP& entry) + { + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + SummaryFormat::SharedPointer current_category_pick; + uint32_t reason_to_pick_current; + + for (begin = m_active_categories.begin(); begin != end; begin++) + { + FormatCategory::SharedPointer category = *begin; + if ( category->Get(vobj, current_category_pick, &reason_to_pick_current) && reason_to_pick_current == lldb::eFormatterDirectChoice ) + { + entry = SummaryFormat::SharedPointer(current_category_pick); + return true; + } + } + return false; + } + + bool + Get_AnyMatch(ValueObject& vobj, + lldb::SummaryFormatSP& entry) + { + ActiveCategoriesIterator begin, end = m_active_categories.end(); + + SummaryFormat::SharedPointer current_category_pick; + + for (begin = m_active_categories.begin(); begin != end; begin++) + { + FormatCategory::SharedPointer category = *begin; + if ( category->Get(vobj, current_category_pick, NULL) ) + { + entry = SummaryFormat::SharedPointer(current_category_pick); + return true; + } + } + return false; + } public: - + + typedef bool (*CategoryCallback)(void*, const char*, const FormatCategory::SharedPointer&); + FormatManager() : m_value_nav(this), - m_summary_nav(this), - m_system_summary_nav(this), - m_regex_summary_nav(this), - m_system_regex_summary_nav(this), m_named_summaries_map(this), - m_last_revision(0) + m_last_revision(0), + m_categories_map(this), + m_active_categories() { + + // build default categories + + m_default_category_name = ConstString("default").GetCString(); + m_system_category_name = ConstString("system").GetCString(); + + Category(m_default_category_name)->Enable(); + Category(m_system_category_name)->Enable(); + // add some default stuff // most formats, summaries, ... actually belong to the users' lldbinit file rather than here SummaryFormat::SharedPointer string_format(new StringSummaryFormat(false, @@ -771,34 +978,119 @@ public: false, false, true, - "${var%s}")); + "${var%s}", + 1)); SummaryFormat::SharedPointer string_array_format(new StringSummaryFormat(false, - true, - false, - false, - false, - false, - true, - "${var%s}")); + true, + false, + false, + false, + false, + true, + "${var%s}", + 1)); lldb::RegularExpressionSP any_size_char_arr(new RegularExpression("char \\[[0-9]+\\]")); - - SystemSummary().Add(ConstString("char *").GetCString(), string_format); - SystemSummary().Add(ConstString("const char *").GetCString(), string_format); - SystemRegexSummary().Add(any_size_char_arr, string_array_format); + + Summary(m_system_category_name)->Add(ConstString("char *").GetCString(), string_format); + Summary(m_system_category_name)->Add(ConstString("const char *").GetCString(), string_format); + RegexSummary(m_system_category_name)->Add(any_size_char_arr, string_array_format); + + m_active_categories.push_front(Category(m_system_category_name)); + m_active_categories.push_front(Category(m_default_category_name)); + } - - ValueNavigator& Value() { return m_value_nav; } - SummaryNavigator& Summary() { return m_summary_nav; } - SummaryNavigator& SystemSummary() { return m_system_summary_nav; } - RegexSummaryNavigator& RegexSummary() { return m_regex_summary_nav; } - RegexSummaryNavigator& SystemRegexSummary() { return m_system_regex_summary_nav; } - NamedSummariesMap& NamedSummary() { return m_named_summaries_map; } + CategoryMap& Categories() { return m_categories_map; } + ValueNavigator& Value() { return m_value_nav; } + NamedSummariesMap& NamedSummary() { return m_named_summaries_map; } + + void + EnableCategory(const char* category_name) + { + Category(category_name)->Enable(); + m_active_categories.push_front(Category(category_name)); + } + + class delete_matching_categories + { + FormatCategory::SharedPointer ptr; + public: + delete_matching_categories(FormatCategory::SharedPointer p) : ptr(p) + {} + + bool operator()(const FormatCategory::SharedPointer& other) + { + return ptr.get() == other.get(); + } + }; + + void + DisableCategory(const char* category_name) + { + Category(category_name)->Disable(); + m_active_categories.remove_if(delete_matching_categories(Category(category_name))); + } + + void + LoopThroughCategories(CategoryCallback callback, void* param) + { + CategoryMapIterator begin, end = m_categories_map.m_map.end(); + + for (begin = m_categories_map.m_map.begin(); begin != end; begin++) + { + if (!callback(param, begin->first, begin->second)) + return; + } + } + + FormatCategory::SummaryNavigatorSP + Summary(const char* category_name = NULL) + { + if (!category_name) + return Summary(m_default_category_name); + lldb::FormatCategorySP category; + if (m_categories_map.Get(category_name, category)) + return category->Summary(); + return FormatCategory::SummaryNavigatorSP(); + } + + FormatCategory::RegexSummaryNavigatorSP + RegexSummary(const char* category_name = NULL) + { + if (!category_name) + return RegexSummary(m_default_category_name); + lldb::FormatCategorySP category; + if (m_categories_map.Get(category_name, category)) + return category->RegexSummary(); + return FormatCategory::RegexSummaryNavigatorSP(); + } + + lldb::FormatCategorySP + Category(const char* category_name = NULL) + { + if (!category_name) + return Category(m_default_category_name); + lldb::FormatCategorySP category; + if (m_categories_map.Get(category_name, category)) + return category; + Categories().Add(category_name,lldb::FormatCategorySP(new FormatCategory(this))); + return Category(category_name); + } + + bool + Get(ValueObject& vobj, + lldb::SummaryFormatSP& entry) + { + if ( Get_ExactMatch(vobj,entry) ) + return true; + return Get_AnyMatch(vobj,entry); + } + static bool GetFormatFromCString (const char *format_cstr, bool partial_match_ok, diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 34e2a805734a..a631a215fd8d 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -490,6 +490,20 @@ namespace lldb { eFunctionNameTypeSelector = (1u << 5) // Find function by selector name (ObjC) names } FunctionNameType; + // this enum determines how a FormatNavigator picked a specific format for a datatype + // these values can be used together (e.g. eFormatterStrippedPointerReference | eFormatterNavigatedBaseClasses + // if you went from DerivedType& to BaseType to find a valid format). the priority rules are embedded in the + // ordering of these values (i.e. if you want to make a choice look really bad, give it a high value in this + // enum). eFormatterDirectChoice should always be left at 0 because it's our favorite choice all the time + typedef enum FormatterChoiceCriterion + { + eFormatterDirectChoice = 0x00000000, + eFormatterStrippedPointerReference = 0x00000001, + eFormatterNavigatedTypedefs = 0x00000002, + eFormatterNavigatedBaseClasses = 0x00000004, + eFormatterRegularExpressionSummary = 0x00000004 + } FormatterChoiceCriterion; + } // namespace lldb diff --git a/lldb/include/lldb/lldb-forward-rtti.h b/lldb/include/lldb/lldb-forward-rtti.h index 858e521ebd91..e07771a8cb26 100644 --- a/lldb/include/lldb/lldb-forward-rtti.h +++ b/lldb/include/lldb/lldb-forward-rtti.h @@ -38,6 +38,7 @@ namespace lldb { typedef SharedPtr::Type DisassemblerSP; typedef SharedPtr::Type DynamicLoaderSP; typedef SharedPtr::Type EventSP; + typedef SharedPtr::Type FormatCategorySP; typedef SharedPtr::Type FunctionSP; typedef SharedPtr::Type InlineFunctionInfoSP; typedef SharedPtr::Type InputReaderSP; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index c476a1d01c89..4e4938bca7c0 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -76,6 +76,7 @@ class ExecutionContextScope; class FileSpec; class FileSpecList; class Flags; +class FormatCategory; class FormatManager; class FuncUnwinders; class Function; diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index e99c1e09fd07..12670a1e9e51 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -442,7 +442,7 @@ public: SummaryFormatSP summary_format_sp; if (!m_option_variable.summary.empty()) - Debugger::NamedSummaryFormats::Get(ConstString(m_option_variable.summary.c_str()), summary_format_sp); + Debugger::Formatting::NamedSummaryFormats::Get(ConstString(m_option_variable.summary.c_str()), summary_format_sp); if (variable_list) { diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index d408a603e855..8db88e759ede 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -196,7 +196,7 @@ public: const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (typeCS) - Debugger::ValueFormats::Add(typeCS, entry); + Debugger::Formatting::ValueFormats::Add(typeCS, entry); else { result.AppendError("empty typenames not allowed"); @@ -273,7 +273,7 @@ public: } - if (Debugger::ValueFormats::Delete(typeCS)) + if (Debugger::Formatting::ValueFormats::Delete(typeCS)) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); @@ -311,7 +311,7 @@ public: bool Execute (Args& command, CommandReturnObject &result) { - Debugger::ValueFormats::Clear(); + Debugger::Formatting::ValueFormats::Clear(); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } @@ -372,7 +372,7 @@ public: } else param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result); - Debugger::ValueFormats::LoopThrough(CommandObjectTypeFormatList_LoopCallback, param); + Debugger::Formatting::ValueFormats::LoopThrough(CommandObjectTypeFormatList_LoopCallback, param); delete param; result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); @@ -549,7 +549,7 @@ public: CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), script_format, (options->m_regex ? CommandObjectTypeSummaryAdd::eRegexSummary : CommandObjectTypeSummaryAdd::eRegularSummary), - options->m_is_system, + options->m_category, &error); if (error.Fail()) { @@ -566,7 +566,7 @@ public: CommandObjectTypeSummaryAdd::AddSummary(*(options->m_name), script_format, CommandObjectTypeSummaryAdd::eNamedSummary, - options->m_is_system, + options->m_category, &error); if (error.Fail()) { @@ -635,7 +635,7 @@ CommandObjectTypeSummaryAdd::CommandOptions::SetOptionValue (uint32_t option_idx m_is_add_script = true; break; case 'w': - m_is_system = true; + m_category = ConstString(option_arg).GetCString(); break; default: error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); @@ -660,6 +660,7 @@ CommandObjectTypeSummaryAdd::CommandOptions::OptionParsingStarting () m_python_function = ""; m_is_add_script = false; m_is_system = false; + m_category = NULL; } void @@ -786,7 +787,8 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn m_options.m_one_liner, m_options.m_regex, m_options.m_is_system, - m_options.m_name); + m_options.m_name, + m_options.m_category); for(int i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); @@ -815,7 +817,7 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn CommandObjectTypeSummaryAdd::AddSummary(ConstString(type_name), script_format, (m_options.m_regex ? eRegexSummary : eRegularSummary), - m_options.m_is_system, + m_options.m_category, &error); if (error.Fail()) { @@ -829,7 +831,7 @@ CommandObjectTypeSummaryAdd::Execute_ScriptSummary (Args& command, CommandReturn { if ( (bool)(*(m_options.m_name)) ) { - AddSummary(*(m_options.m_name), script_format, eNamedSummary, m_options.m_is_system, &error); + AddSummary(*(m_options.m_name), script_format, eNamedSummary, m_options.m_category, &error); if (error.Fail()) { result.AppendError(error.AsCString()); @@ -903,7 +905,7 @@ CommandObjectTypeSummaryAdd::Execute_StringSummary (Args& command, CommandReturn AddSummary(typeCS, entry, (m_options.m_regex ? eRegexSummary : eRegularSummary), - m_options.m_is_system, + m_options.m_category, &error); if (error.Fail()) @@ -918,7 +920,7 @@ CommandObjectTypeSummaryAdd::Execute_StringSummary (Args& command, CommandReturn { if ( (bool)(*(m_options.m_name)) ) { - AddSummary(*(m_options.m_name), entry, eNamedSummary, m_options.m_is_system, &error); + AddSummary(*(m_options.m_name), entry, eNamedSummary, m_options.m_category, &error); if (error.Fail()) { result.AppendError(error.AsCString()); @@ -1037,7 +1039,7 @@ bool CommandObjectTypeSummaryAdd::AddSummary(const ConstString& type_name, SummaryFormatSP entry, SummaryFormatType type, - bool is_system, + const char* category, Error* error) { if (type == eRegexSummary) @@ -1050,30 +1052,20 @@ CommandObjectTypeSummaryAdd::AddSummary(const ConstString& type_name, return false; } - if (!is_system) - { - Debugger::RegexSummaryFormats::Delete(type_name); - Debugger::RegexSummaryFormats::Add(typeRX, entry); - } - else - { - Debugger::SystemRegexSummaryFormats::Delete(type_name); - Debugger::SystemRegexSummaryFormats::Add(typeRX, entry); - } + Debugger::Formatting::SummaryFormats(category)->RegexSummary()->Delete(type_name.GetCString()); + Debugger::Formatting::SummaryFormats(category)->RegexSummary()->Add(typeRX, entry); + return true; } else if (type == eNamedSummary) { // system named summaries do not exist (yet?) - Debugger::NamedSummaryFormats::Add(type_name,entry); + Debugger::Formatting::NamedSummaryFormats::Add(type_name,entry); return true; } else { - if (!is_system) - Debugger::SummaryFormats::Add(type_name,entry); - else - Debugger::SystemSummaryFormats::Add(type_name,entry); + Debugger::Formatting::SummaryFormats(category)->Summary()->Add(type_name.GetCString(), entry); return true; } } @@ -1081,7 +1073,7 @@ CommandObjectTypeSummaryAdd::AddSummary(const ConstString& type_name, OptionDefinition CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_ALL, false, "system", 'w', no_argument, NULL, 0, eArgTypeBoolean, "This is a system summary (makes it harder to delete it by accident)."}, + { LLDB_OPT_SET_ALL, false, "category", 'w', required_argument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."}, { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."}, { LLDB_OPT_SET_ALL, false, "no-value", 'v', no_argument, NULL, 0, eArgTypeBoolean, "Don't show the value, just show the summary, for this type."}, { LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for pointers-to-type objects."}, @@ -1126,7 +1118,10 @@ private: switch (short_option) { case 'a': - m_delete_system = true; + m_delete_all = true; + break; + case 'w': + m_category = ConstString(option_arg).GetCString(); break; default: error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); @@ -1139,7 +1134,8 @@ private: void OptionParsingStarting () { - m_delete_system = false; + m_delete_all = false; + m_category = NULL; } const OptionDefinition* @@ -1154,7 +1150,9 @@ private: // Instance variables to hold the values for command options. - bool m_delete_system; + bool m_delete_all; + const char* m_category; + }; CommandOptions m_options; @@ -1164,6 +1162,17 @@ private: { return &m_options; } + + static bool + PerCategoryCallback(void* param, + const char* cate_name, + const FormatCategory::SharedPointer& cate) + { + const char* name = (const char*)param; + cate->Summary()->Delete(name); + cate->RegexSummary()->Delete(name); + return true; + } public: CommandObjectTypeSummaryDelete (CommandInterpreter &interpreter) : @@ -1210,13 +1219,17 @@ public: return false; } - bool delete_summary = Debugger::SummaryFormats::Delete(typeCS); - bool delete_regex = Debugger::RegexSummaryFormats::Delete(typeCS); - bool delete_named = Debugger::NamedSummaryFormats::Delete(typeCS); - bool delete_sys = m_options.m_delete_system ? Debugger::SystemSummaryFormats::Delete(typeCS) : false; - bool delete_sys_regex = m_options.m_delete_system ? Debugger::SystemRegexSummaryFormats::Delete(typeCS) : false; + if (m_options.m_delete_all) + { + Debugger::Formatting::Categories::LoopThrough(PerCategoryCallback, (void*)typeCS.GetCString()); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } - if (delete_summary || delete_regex || delete_named || delete_sys || delete_sys_regex) + bool delete_category = Debugger::Formatting::SummaryFormats(m_options.m_category)->Delete(typeCS.GetCString()); + bool delete_named = Debugger::Formatting::NamedSummaryFormats::Delete(typeCS); + + if (delete_category || delete_named) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); @@ -1234,14 +1247,11 @@ public: OptionDefinition CommandObjectTypeSummaryDelete::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeBoolean, "Also delete system summaries (not recommended)."}, + { 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 } }; -//------------------------------------------------------------------------- -// CommandObjectTypeSummaryClear -//------------------------------------------------------------------------- - class CommandObjectTypeSummaryClear : public CommandObject { private: @@ -1267,7 +1277,7 @@ private: switch (short_option) { case 'a': - m_delete_system = true; + m_delete_all = true; break; default: error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); @@ -1280,7 +1290,7 @@ private: void OptionParsingStarting () { - m_delete_system = false; + m_delete_all = false; } const OptionDefinition* @@ -1295,7 +1305,8 @@ private: // Instance variables to hold the values for command options. - bool m_delete_system; + bool m_delete_all; + bool m_delete_named; }; CommandOptions m_options; @@ -1305,7 +1316,18 @@ private: { return &m_options; } - + + static bool + PerCategoryCallback(void* param, + const char* cate_name, + const FormatCategory::SharedPointer& cate) + { + cate->Summary()->Clear(); + cate->RegexSummary()->Clear(); + return true; + + } + public: CommandObjectTypeSummaryClear (CommandInterpreter &interpreter) : CommandObject (interpreter, @@ -1322,15 +1344,19 @@ public: bool Execute (Args& command, CommandReturnObject &result) { - Debugger::SummaryFormats::Clear(); - Debugger::RegexSummaryFormats::Clear(); - Debugger::NamedSummaryFormats::Clear(); - if (m_options.m_delete_system) + if (m_options.m_delete_all) + Debugger::Formatting::Categories::LoopThrough(PerCategoryCallback, NULL); + else if (command.GetArgumentCount() > 0) { - Debugger::SystemSummaryFormats::Clear(); - Debugger::SystemRegexSummaryFormats::Clear(); + const char* cat_name = command.GetArgumentAtIndex(0); + ConstString cat_nameCS(cat_name); + Debugger::Formatting::SummaryFormats(cat_nameCS.GetCString())->Clear(); } + else + Debugger::Formatting::SummaryFormats()->Clear(); + + Debugger::Formatting::NamedSummaryFormats::Clear(); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); @@ -1341,7 +1367,7 @@ public: OptionDefinition CommandObjectTypeSummaryClear::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeBoolean, "Also clear system summaries (not recommended)."}, + { LLDB_OPT_SET_ALL, false, "all", 'a', no_argument, NULL, 0, eArgTypeBoolean, "Clear every category."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; @@ -1362,14 +1388,6 @@ struct CommandObjectTypeSummaryList_LoopCallbackParam { RegularExpression* X = NULL) : self(S), result(R), regex(X) {} }; -struct CommandObjectTypeRXSummaryList_LoopCallbackParam { - CommandObjectTypeSummaryList* self; - CommandReturnObject* result; - RegularExpression* regex; - CommandObjectTypeRXSummaryList_LoopCallbackParam(CommandObjectTypeSummaryList* S, CommandReturnObject* R, - RegularExpression* X = NULL) : self(S), result(R), regex(X) {} -}; - class CommandObjectTypeSummaryList : public CommandObject { public: @@ -1400,7 +1418,6 @@ public: const size_t argc = command.GetArgumentCount(); CommandObjectTypeSummaryList_LoopCallbackParam *param; - CommandObjectTypeRXSummaryList_LoopCallbackParam *rxparam; if (argc == 1) { RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); @@ -1409,26 +1426,10 @@ public: } else param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result); - Debugger::SummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); - Debugger::SystemSummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); - delete param; - if (Debugger::RegexSummaryFormats::GetCount() > 0 || Debugger::SystemRegexSummaryFormats::GetCount() > 0 ) - { - result.GetOutputStream().Printf("Regex-based summaries (slower):\n"); - if (argc == 1) { - RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); - regex->Compile(command.GetArgumentAtIndex(0)); - rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result,regex); - } - else - rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result); - Debugger::RegexSummaryFormats::LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, rxparam); - Debugger::SystemRegexSummaryFormats::LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, rxparam); - delete rxparam; - } - - if (Debugger::NamedSummaryFormats::GetCount() > 0) + Debugger::Formatting::Categories::LoopThrough(PerCategoryCallback,param); + + if (Debugger::Formatting::NamedSummaryFormats::GetCount() > 0) { result.GetOutputStream().Printf("Named summaries:\n"); if (argc == 1) { @@ -1438,7 +1439,7 @@ public: } else param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result); - Debugger::NamedSummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); + Debugger::Formatting::NamedSummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); delete param; } @@ -1448,6 +1449,28 @@ public: private: + static bool + PerCategoryCallback(void* param, + const char* cate_name, + const FormatCategory::SharedPointer& cate) + { + + CommandReturnObject* result = ((CommandObjectTypeSummaryList_LoopCallbackParam*)param)->result; + + result->GetOutputStream().Printf("-----------------------\nCategory: %s (%s)\n-----------------------\n", + cate_name, + (cate->IsEnabled() ? "enabled" : "disabled")); + + cate->Summary()->LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); + + if (cate->RegexSummary()->GetCount() > 0) + { + result->GetOutputStream().Printf("Regex-based summaries (slower):\n"); + cate->RegexSummary()->LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, param); + } + return true; + } + bool LoopCallback (const char* type, const SummaryFormat::SharedPointer& entry, @@ -1479,10 +1502,233 @@ CommandObjectTypeRXSummaryList_LoopCallback ( lldb::RegularExpressionSP regex, const SummaryFormat::SharedPointer& entry) { - CommandObjectTypeRXSummaryList_LoopCallbackParam* param = (CommandObjectTypeRXSummaryList_LoopCallbackParam*)pt2self; + CommandObjectTypeSummaryList_LoopCallbackParam* param = (CommandObjectTypeSummaryList_LoopCallbackParam*)pt2self; return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); } +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryEnable +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryEnable : public CommandObject +{ +public: + CommandObjectTypeCategoryEnable (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type category enable", + "Enable a category as a source of summaries.", + NULL) + { + 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); + + } + + ~CommandObjectTypeCategoryEnable () + { + } + + 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 category name not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Debugger::Formatting::Categories::Enable(typeCS); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryDelete : public CommandObject +{ +public: + CommandObjectTypeCategoryDelete (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type category delete", + "Delete a category and all associated summaries.", + NULL) + { + 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); + + } + + ~CommandObjectTypeCategoryDelete () + { + } + + 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 category name not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + if (Debugger::Formatting::Categories::Delete(typeCS)) + { + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("cannot delete category %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryDisable +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryDisable : public CommandObject +{ +public: + CommandObjectTypeCategoryDisable (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type category disable", + "Disable a category as a source of summaries.", + NULL) + { + 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); + + } + + ~CommandObjectTypeCategoryDisable () + { + } + + 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 category name not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + Debugger::Formatting::Categories::Disable(typeCS); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeCategoryList +//------------------------------------------------------------------------- + +class CommandObjectTypeCategoryList : public CommandObject +{ +private: + static bool + PerCategoryCallback(void* param, + const char* cate_name, + const FormatCategory::SharedPointer& cate) + { + CommandReturnObject* result = (CommandReturnObject*)param; + result->GetOutputStream().Printf("Category %s is%s enabled\n", + cate_name, + (cate->IsEnabled() ? "" : " not")); + return true; + } +public: + CommandObjectTypeCategoryList (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type category list", + "Provide a list of all existing categories.", + NULL) + { + } + + ~CommandObjectTypeCategoryList () + { + } + + bool + Execute (Args& command, CommandReturnObject &result) + { + Debugger::Formatting::Categories::LoopThrough(PerCategoryCallback, (void*)&result); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + class CommandObjectTypeFormat : public CommandObjectMultiword { public: @@ -1504,6 +1750,27 @@ public: } }; +class CommandObjectTypeCategory : public CommandObjectMultiword +{ +public: + CommandObjectTypeCategory (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type category", + "A set of commands for operating on categories", + "type category [] ") + { + LoadSubCommand ("enable", CommandObjectSP (new CommandObjectTypeCategoryEnable (interpreter))); + LoadSubCommand ("disable", CommandObjectSP (new CommandObjectTypeCategoryDisable (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeCategoryDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeCategoryList (interpreter))); + } + + + ~CommandObjectTypeCategory () + { + } +}; + class CommandObjectTypeSummary : public CommandObjectMultiword { public: @@ -1535,6 +1802,7 @@ CommandObjectType::CommandObjectType (CommandInterpreter &interpreter) : "A set of commands for operating on the type system", "type []") { + LoadSubCommand ("category", CommandObjectSP (new CommandObjectTypeCategory (interpreter))); LoadSubCommand ("format", CommandObjectSP (new CommandObjectTypeFormat (interpreter))); LoadSubCommand ("summary", CommandObjectSP (new CommandObjectTypeSummary (interpreter))); } diff --git a/lldb/source/Commands/CommandObjectType.h b/lldb/source/Commands/CommandObjectType.h index 0c453785e41d..d9242879bea0 100644 --- a/lldb/source/Commands/CommandObjectType.h +++ b/lldb/source/Commands/CommandObjectType.h @@ -43,6 +43,8 @@ public: ConstString* m_name; + const char* m_category; + ScriptAddOptions(bool sptr, bool sref, bool casc, @@ -51,7 +53,8 @@ public: bool onel, bool regx, bool syst, - ConstString* name) : + ConstString* name, + const char* catg) : m_skip_pointers(sptr), m_skip_references(sref), m_cascade(casc), @@ -62,7 +65,8 @@ public: m_one_liner(onel), m_regex(regx), m_is_system(syst), - m_name(name) + m_name(name), + m_category(catg) { } @@ -127,6 +131,7 @@ private: std::string m_python_function; bool m_is_add_script; bool m_is_system; + const char* m_category; }; CommandOptions m_options; @@ -169,7 +174,7 @@ public: AddSummary(const ConstString& type_name, lldb::SummaryFormatSP entry, SummaryFormatType type, - bool is_system, + const char* category, Error* error = NULL); }; diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index cde5da77d053..1c62a8cb46f4 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1714,253 +1714,175 @@ GetFormatManager() { } bool -Debugger::ValueFormats::Get(ValueObject& vobj, ValueFormat::SharedPointer &entry) +Debugger::Formatting::ValueFormats::Get(ValueObject& vobj, ValueFormat::SharedPointer &entry) { return GetFormatManager().Value().Get(vobj,entry); } void -Debugger::ValueFormats::Add(const ConstString &type, const ValueFormat::SharedPointer &entry) +Debugger::Formatting::ValueFormats::Add(const ConstString &type, const ValueFormat::SharedPointer &entry) { GetFormatManager().Value().Add(type.AsCString(),entry); } bool -Debugger::ValueFormats::Delete(const ConstString &type) +Debugger::Formatting::ValueFormats::Delete(const ConstString &type) { return GetFormatManager().Value().Delete(type.AsCString()); } void -Debugger::ValueFormats::Clear() +Debugger::Formatting::ValueFormats::Clear() { GetFormatManager().Value().Clear(); } void -Debugger::ValueFormats::LoopThrough(ValueFormat::ValueCallback callback, void* callback_baton) +Debugger::Formatting::ValueFormats::LoopThrough(ValueFormat::ValueCallback callback, void* callback_baton) { GetFormatManager().Value().LoopThrough(callback, callback_baton); } uint32_t -Debugger::ValueFormats::GetCurrentRevision() +Debugger::Formatting::ValueFormats::GetCurrentRevision() { return GetFormatManager().GetCurrentRevision(); } uint32_t -Debugger::ValueFormats::GetCount() +Debugger::Formatting::ValueFormats::GetCount() { return GetFormatManager().Value().GetCount(); } -bool -Debugger::SummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry) +lldb::FormatCategorySP +Debugger::Formatting::SummaryFormats(const char* category_name) { - return GetFormatManager().Summary().Get(vobj,entry); -} - -void -Debugger::SummaryFormats::Add(const ConstString &type, const SummaryFormat::SharedPointer &entry) -{ - GetFormatManager().Summary().Add(type.AsCString(),entry); + return GetFormatManager().Category(category_name); } bool -Debugger::SummaryFormats::Delete(const ConstString &type) +Debugger::Formatting::GetSummaryFormat(ValueObject& vobj, + lldb::SummaryFormatSP& entry) { - return GetFormatManager().Summary().Delete(type.AsCString()); + return GetFormatManager().Get(vobj, entry); +} + +bool +Debugger::Formatting::Categories::Get(const ConstString &category, lldb::FormatCategorySP &entry) +{ + entry = GetFormatManager().Category(category.GetCString()); + return true; } void -Debugger::SummaryFormats::Clear() +Debugger::Formatting::Categories::Add(const ConstString &category) { - GetFormatManager().Summary().Clear(); + GetFormatManager().Category(category.GetCString()); +} + +bool +Debugger::Formatting::Categories::Delete(const ConstString &category) +{ + GetFormatManager().DisableCategory(category.GetCString()); + return GetFormatManager().Categories().Delete(category.GetCString()); } void -Debugger::SummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton) +Debugger::Formatting::Categories::Clear() { - GetFormatManager().Summary().LoopThrough(callback, callback_baton); + GetFormatManager().Categories().Clear(); +} + +void +Debugger::Formatting::Categories::Clear(ConstString &category) +{ + GetFormatManager().Category(category.GetCString())->Clear(); +} + +void +Debugger::Formatting::Categories::Enable(ConstString& category) +{ + if (GetFormatManager().Category(category.GetCString())->IsEnabled() == false) + { + //GetFormatManager().Category(category.GetCString())->Enable(); + GetFormatManager().EnableCategory(category.GetCString()); + } + else + { + //GetFormatManager().Category(category.GetCString())->Disable(); + GetFormatManager().DisableCategory(category.GetCString()); + //GetFormatManager().Category(category.GetCString())->Enable(); + GetFormatManager().EnableCategory(category.GetCString()); + } +} + +void +Debugger::Formatting::Categories::Disable(ConstString& category) +{ + if (GetFormatManager().Category(category.GetCString())->IsEnabled() == true) + { + //GetFormatManager().Category(category.GetCString())->Disable(); + GetFormatManager().DisableCategory(category.GetCString()); + } +} + +void +Debugger::Formatting::Categories::LoopThrough(FormatManager::CategoryCallback callback, void* callback_baton) +{ + GetFormatManager().LoopThroughCategories(callback, callback_baton); } uint32_t -Debugger::SummaryFormats::GetCurrentRevision() +Debugger::Formatting::Categories::GetCurrentRevision() { return GetFormatManager().GetCurrentRevision(); } uint32_t -Debugger::SummaryFormats::GetCount() +Debugger::Formatting::Categories::GetCount() { - return GetFormatManager().Summary().GetCount(); + return GetFormatManager().Categories().GetCount(); } bool -Debugger::SystemSummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry) -{ - return GetFormatManager().SystemSummary().Get(vobj,entry); -} - -void -Debugger::SystemSummaryFormats::Add(const ConstString &type, const SummaryFormat::SharedPointer &entry) -{ - GetFormatManager().SystemSummary().Add(type.AsCString(),entry); -} - -bool -Debugger::SystemSummaryFormats::Delete(const ConstString &type) -{ - return GetFormatManager().SystemSummary().Delete(type.AsCString()); -} - -void -Debugger::SystemSummaryFormats::Clear() -{ - GetFormatManager().SystemSummary().Clear(); -} - -void -Debugger::SystemSummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton) -{ - GetFormatManager().SystemSummary().LoopThrough(callback, callback_baton); -} - -uint32_t -Debugger::SystemSummaryFormats::GetCurrentRevision() -{ - return GetFormatManager().GetCurrentRevision(); -} - -uint32_t -Debugger::SystemSummaryFormats::GetCount() -{ - return GetFormatManager().SystemSummary().GetCount(); -} - -bool -Debugger::RegexSummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry) -{ - return GetFormatManager().RegexSummary().Get(vobj,entry); -} - -void -Debugger::RegexSummaryFormats::Add(const lldb::RegularExpressionSP &type, const SummaryFormat::SharedPointer &entry) -{ - GetFormatManager().RegexSummary().Add(type,entry); -} - -bool -Debugger::RegexSummaryFormats::Delete(const ConstString &type) -{ - return GetFormatManager().RegexSummary().Delete(type.AsCString()); -} - -void -Debugger::RegexSummaryFormats::Clear() -{ - GetFormatManager().RegexSummary().Clear(); -} - -void -Debugger::RegexSummaryFormats::LoopThrough(SummaryFormat::RegexSummaryCallback callback, void* callback_baton) -{ - GetFormatManager().RegexSummary().LoopThrough(callback, callback_baton); -} - -uint32_t -Debugger::RegexSummaryFormats::GetCurrentRevision() -{ - return GetFormatManager().GetCurrentRevision(); -} - -uint32_t -Debugger::RegexSummaryFormats::GetCount() -{ - return GetFormatManager().RegexSummary().GetCount(); -} - -bool -Debugger::SystemRegexSummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry) -{ - return GetFormatManager().SystemRegexSummary().Get(vobj,entry); -} - -void -Debugger::SystemRegexSummaryFormats::Add(const lldb::RegularExpressionSP &type, const SummaryFormat::SharedPointer &entry) -{ - GetFormatManager().SystemRegexSummary().Add(type,entry); -} - -bool -Debugger::SystemRegexSummaryFormats::Delete(const ConstString &type) -{ - return GetFormatManager().SystemRegexSummary().Delete(type.AsCString()); -} - -void -Debugger::SystemRegexSummaryFormats::Clear() -{ - GetFormatManager().SystemRegexSummary().Clear(); -} - -void -Debugger::SystemRegexSummaryFormats::LoopThrough(SummaryFormat::RegexSummaryCallback callback, void* callback_baton) -{ - GetFormatManager().SystemRegexSummary().LoopThrough(callback, callback_baton); -} - -uint32_t -Debugger::SystemRegexSummaryFormats::GetCurrentRevision() -{ - return GetFormatManager().GetCurrentRevision(); -} - -uint32_t -Debugger::SystemRegexSummaryFormats::GetCount() -{ - return GetFormatManager().SystemRegexSummary().GetCount(); -} - -bool -Debugger::NamedSummaryFormats::Get(const ConstString &type, SummaryFormat::SharedPointer &entry) +Debugger::Formatting::NamedSummaryFormats::Get(const ConstString &type, SummaryFormat::SharedPointer &entry) { return GetFormatManager().NamedSummary().Get(type.AsCString(),entry); } void -Debugger::NamedSummaryFormats::Add(const ConstString &type, const SummaryFormat::SharedPointer &entry) +Debugger::Formatting::NamedSummaryFormats::Add(const ConstString &type, const SummaryFormat::SharedPointer &entry) { GetFormatManager().NamedSummary().Add(type.AsCString(),entry); } bool -Debugger::NamedSummaryFormats::Delete(const ConstString &type) +Debugger::Formatting::NamedSummaryFormats::Delete(const ConstString &type) { return GetFormatManager().NamedSummary().Delete(type.AsCString()); } void -Debugger::NamedSummaryFormats::Clear() +Debugger::Formatting::NamedSummaryFormats::Clear() { GetFormatManager().NamedSummary().Clear(); } void -Debugger::NamedSummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton) +Debugger::Formatting::NamedSummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton) { GetFormatManager().NamedSummary().LoopThrough(callback, callback_baton); } uint32_t -Debugger::NamedSummaryFormats::GetCurrentRevision() +Debugger::Formatting::NamedSummaryFormats::GetCurrentRevision() { return GetFormatManager().GetCurrentRevision(); } uint32_t -Debugger::NamedSummaryFormats::GetCount() +Debugger::Formatting::NamedSummaryFormats::GetCount() { return GetFormatManager().NamedSummary().GetCount(); } diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp index 7be3791adbb7..2ce91aed8fd3 100644 --- a/lldb/source/Core/FormatManager.cpp +++ b/lldb/source/Core/FormatManager.cpp @@ -184,6 +184,8 @@ FormatNavigator::Delete(const char* ty if ( ::strcmp(type,regex->GetText()) == 0) { m_format_map.map().erase(pos); + if(m_format_map.listener) + m_format_map.listener->Changed(); return true; } } @@ -270,3 +272,23 @@ StringSummaryFormat::FormatObject(lldb::ValueObjectSP object) } } +void +FormatCategory::ChooseAsPreferential(const char* name) +{ + Mutex::Locker(m_mutex); + lldb::SummaryFormatSP format; + + uint32_t revision = Debugger::Formatting::ValueFormats::GetCurrentRevision(); + + if ( Summary()->Get(name, format) ) + format->SetPriority(revision); + + format.reset(); + + if ( RegexSummary()->Get(name, format) ) + format->SetPriority(revision); + + if(m_change_listener) + m_change_listener->Changed(); + +} diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 4f0c8dc9069f..fc89f0fd4a4a 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -202,22 +202,21 @@ ValueObject::UpdateFormatsIfNeeded() ClearCustomSummaryFormat(); m_summary_str.clear(); } - if (m_last_format_mgr_revision != Debugger::ValueFormats::GetCurrentRevision()) + if (m_last_format_mgr_revision != Debugger::Formatting::ValueFormats::GetCurrentRevision()) { 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::ValueFormats::Get(*this, m_last_value_format); + 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 - if (!Debugger::SummaryFormats::Get(*this, m_last_summary_format)) - if (!Debugger::RegexSummaryFormats::Get(*this, m_last_summary_format)) - if (!Debugger::SystemSummaryFormats::Get(*this, m_last_summary_format)) - Debugger::SystemRegexSummaryFormats::Get(*this, m_last_summary_format); - m_last_format_mgr_revision = Debugger::ValueFormats::GetCurrentRevision(); + + Debugger::Formatting::GetSummaryFormat(*this, m_last_summary_format); + + m_last_format_mgr_revision = Debugger::Formatting::ValueFormats::GetCurrentRevision(); ClearUserVisibleData(); } diff --git a/lldb/test/functionalities/data-formatter/data-formatter-categories/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-categories/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-categories/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-categories/TestDataFormatterCategories.py b/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py new file mode 100644 index 000000000000..266a12fdb5ec --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-categories/TestDataFormatterCategories.py @@ -0,0 +1,252 @@ +""" +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-categories") + + @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 category delete Category1', check=False) + self.runCmd('type category delete Category2', check=False) + self.runCmd('type category delete NewCategory', check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # Add a summary to a new category and check that it works + self.runCmd("type summary add Rectangle -f \"ARectangle\" -w NewCategory") + + self.expect("frame variable r1 r2 r3", matching=False, + substrs = ['r1 = ARectangle', + 'r2 = ARectangle', + 'r3 = ARectangle']) + + self.runCmd("type category enable NewCategory") + + self.expect("frame variable r1 r2 r3", matching=True, + substrs = ['r1 = ARectangle', + 'r2 = ARectangle', + 'r3 = ARectangle']) + + # Disable the category and check that the old stuff is there + self.runCmd("type category disable NewCategory") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = {', + 'r2 = {', + 'r3 = {']) + + # Re-enable the category and check that it works + self.runCmd("type category enable NewCategory") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = ARectangle', + 'r2 = ARectangle', + 'r3 = ARectangle']) + + # Delete the category and the old stuff should be there + self.runCmd("type category delete NewCategory") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = {', + 'r2 = {', + 'r3 = {']) + + # Add summaries to two different categories and check that we can switch + self.runCmd("type summary add -f \"Width = ${var.w}, Height = ${var.h}\" Rectangle -w Category1") + self.runCmd("type summary add -s \"return 'Area = ' + str( int(valobj.GetChildMemberWithName('w').GetValue()) * int(valobj.GetChildMemberWithName('h').GetValue()) );\" Rectangle -w Category2") + + self.runCmd("type category enable Category2") + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + self.runCmd("type category disable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Area = ', + 'r2 = Area = ', + 'r3 = Area = ']) + + # switch again + + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + # Re-enable the category and show that the preference is persisted + self.runCmd("type category disable Category2") + self.runCmd("type category enable Category2") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Area = ', + 'r2 = Area = ', + 'r3 = Area = ']) + + # Now delete the favorite summary + self.runCmd("type summary delete Rectangle -w Category2") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + # Delete the summary from the default category (that does not have it) + self.runCmd("type summary delete Rectangle", check=False) + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + # Now add another summary to another category and switch back and forth + self.runCmd("type category delete Category1") + self.runCmd("type category delete Category2") + + self.runCmd("type summary add Rectangle -f \"Category1\" -w Category1") + self.runCmd("type summary add Rectangle -f \"Category2\" -w Category2") + + self.runCmd("type category enable Category2") + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Category1', + 'r2 = Category1', + 'r3 = Category1']) + + self.runCmd("type category disable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Category2', + 'r2 = Category2', + 'r3 = Category2']) + + self.runCmd("type category disable Category1") + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Category1', + 'r2 = Category1', + 'r3 = Category1']) + + self.runCmd("type category delete Category1") + self.runCmd("type category delete Category2") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = {', + 'r2 = {', + 'r3 = {']) + + # Check that multiple summaries can go into one category + self.runCmd("type summary add -f \"Width = ${var.w}, Height = ${var.h}\" Rectangle -w Category1") + self.runCmd("type summary add -f \"Radius = ${var.r}\" Circle -w Category1") + + self.runCmd("type category enable Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = Radius = ', + 'c2 = Radius = ', + 'c3 = Radius = ']) + + self.runCmd("type summary delete Circle -w Category1") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = {', + 'c2 = {', + 'c3 = {']) + + # Add a regex based summary to a category + self.runCmd("type summary add -f \"Radius = ${var.r}\" -x Circle -w Category1") + + self.expect("frame variable r1 r2 r3", + substrs = ['r1 = Width = ', + 'r2 = Width = ', + 'r3 = Width = ']) + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = Radius = ', + 'c2 = Radius = ', + 'c3 = Radius = ']) + + # Delete it + self.runCmd("type summary delete Circle -w Category1") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = {', + 'c2 = {', + 'c3 = {']) + + # Change a summary inside a category and check that the change is reflected + self.runCmd("type summary add Circle -w Category1 -f \"summary1\"") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = summary1', + 'c2 = summary1', + 'c3 = summary1']) + + self.runCmd("type summary add Circle -w Category1 -f \"summary2\"") + + self.expect("frame variable c1 c2 c3", + substrs = ['c1 = summary2', + 'c2 = summary2', + 'c3 = summary2']) + +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-categories/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-categories/main.cpp new file mode 100644 index 000000000000..389adc9a25a1 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-categories/main.cpp @@ -0,0 +1,37 @@ +//===-- 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 Rectangle { + int w; + int h; + Rectangle(int W = 3, int H = 5) : w(W), h(H) {} +}; + +struct Circle { + int r; + Circle(int R = 6) : r(R) {} +}; + +int main (int argc, const char * argv[]) +{ + Rectangle r1(5,6); + Rectangle r2(9,16); + Rectangle r3(4,4); + + Circle c1(5); + Circle c2(6); + Circle c3(7); + + return 0; // Set break point at this line. +} + diff --git a/lldb/test/functionalities/data-formatter/data-formatter-globals/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-globals/Makefile new file mode 100644 index 000000000000..314f1cb2f077 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-globals/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-globals/TestDataFormatterGlobals.py b/lldb/test/functionalities/data-formatter/data-formatter-globals/TestDataFormatterGlobals.py new file mode 100644 index 000000000000..b56b48e23b0b --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-globals/TestDataFormatterGlobals.py @@ -0,0 +1,83 @@ +""" +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-globals") + + @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) + + # 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) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.runCmd("type summary add -f \"JustATest\" Point") + + # Simply check we can get at global variables + self.expect("target variable g_point", + substrs = ['JustATest']) + + self.expect("target variable g_point_pointer", + substrs = ['(Point *) g_point_pointer =']) + + # Print some information about the variables + # (we ignore the actual values) + self.runCmd("type summary add -f \"(x=${var.x},y=${var.y})\" Point") + + self.expect("target variable g_point", + substrs = ['x=', + 'y=']) + + self.expect("target variable g_point_pointer", + substrs = ['(Point *) g_point_pointer =']) + + # Test Python code on resulting SBValue + self.runCmd("type summary add -s \"return 'x=' + str(valobj.GetChildMemberWithName('x').GetValue());\" Point") + + self.expect("target variable g_point", + substrs = ['x=']) + + self.expect("target variable g_point_pointer", + substrs = ['(Point *) g_point_pointer =']) + + +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-globals/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-globals/main.cpp new file mode 100644 index 000000000000..521f7a6931e9 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-globals/main.cpp @@ -0,0 +1,27 @@ +//===-- 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 Point { + int x; + int y; + Point(int X = 3, int Y = 2) : x(X), y(Y) {} +}; + +Point g_point(3,4); +Point* g_point_pointer = new Point(7,5); + +int main (int argc, const char * argv[]) +{ + return 0; // Set break point at this line. +} + diff --git a/lldb/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py b/lldb/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py index f5d86d966550..55f7e947eca7 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-skip-summary/TestDataFormatterSkipSummary.py @@ -130,7 +130,7 @@ class DataFormatterTestCase(TestBase): '}']) # Expand same expression, skipping 3 layers of summaries - self.expect('frame variable data1.m_child1->m_child2 -Y3', + self.expect('frame variable data1.m_child1->m_child2 -T -Y3', substrs = ['(DeepData_3) data1.m_child1->m_child2 = {', 'm_some_text = "Just a test"', 'm_child2 = {', diff --git a/lldb/www/varformats.html b/lldb/www/varformats.html index 3fd647db18e7..9260e0d65256 100755 --- a/lldb/www/varformats.html +++ b/lldb/www/varformats.html @@ -401,8 +401,10 @@ (lldb) fr var one
(i_am_cool) one = int = 3, float = 3.14159, char = 69

-

The way to obtain this effect is to bind a summary string to - the datatype using the type summary add + +

There are two ways to use type summaries: the first one is to bind a + summary string to the datatype; the second is to bind a Python script to the + datatype. Both options are enabled by the type summary add command.

In the example, the command we type was:

@@ -410,6 +412,10 @@ (lldb) type summary add -f "int = ${var.integer}, float = ${var.floating}, char = ${var.character%u}" i_am_cool
+ +

Initially, we will focus on summary strings, and then describe the Python binding + mechanism.

+
@@ -717,11 +723,128 @@
-

Regular expression typenames

+

Python scripting

+ +

Most of the times, summary strings prove good enough for the job of summarizing + the contents of a variable. However, as soon as you need to do more than picking + some values and rearranging them for display, summary strings stop being an + effective tool. This is because summary strings lack the power to actually perform + some computation on the value of variables.

+

To solve this issue, you can bind some Python scripting code as a summary for + your datatype, and that script has the ability to both extract children variables + as the summary strings do and to perform active computation on the extracted + values. As a small example, let's say we have a Rectangle class:

+ + +class Rectangle
+{
+private:
+     int height;
+     int width;
+public:
+     Rectangle() : height(3), width(5) {}
+     Rectangle(int H) : height(H), width(H*2-1) {}
+     Rectangle(int H, int W) : height(H), width(W) {}
+ +     int GetHeight() { return height; }
+     int GetWidth() { return width; }
+ +};
+
+ +

Summary strings are effective to reduce the screen real estate used by + the default viewing mode, but are not effective if we want to display the + area, perimeter and length of diagonal of Rectangle objects

+ +

To obtain this, we can simply attach a small Python script to the Rectangle + class, as shown in this example:

+ +
+ +
+ (lldb) type summary add -P Rectangle
+ Enter your Python command(s). Type 'DONE' to end.
+def function (valobj,dict):
+     height_val = valobj.GetChildMemberWithName('height')
+     width_val = valobj.GetChildMemberWithName('width')
+     height_str = height_val.GetValue()
+     width_str = width_val.GetValue()
+     height = int(height_str)
+     width = int(width_str)
+     area = height*width
+     perimeter = 2*height + 2*width
+     diag = sqrt(height*height+width*width)
+     return 'Area: ' + str(area) + ', Perimeter: ' + str(perimeter) + ', Diagonal: ' + str(diag)
+     DONE
+(lldb) script
+Python Interactive Interpreter. To exit, type 'quit()', 'exit()' or Ctrl-D.
+>>> from math import sqrt
+>>> quit()
+(lldb) frame variable
+(Rectangle) r1 = Area: 20, Perimeter: 18, Diagonal: 6.40312423743
+(Rectangle) r2 = Area: 72, Perimeter: 36, Diagonal: 13.416407865
+(Rectangle) r3 = Area: 16, Perimeter: 16, Diagonal: 5.65685424949
+
+ +

In this scenario, you need to enter the interactive interpreter to import the + function sqrt() from the math library. As the example shows, everything you enter + into the interactive interpreter is saved for you to use it in scripts. This way + you can define your own utility functions and use them in your summary scripts if + necessary.

+ +

In order to write effective summary scripts, you need to know the LLDB public + API, which is the way Python code can access the LLDB object model. For further + details on the API you should look at this page, or at + the LLDB doxygen documentation when it becomes available.

+ +

As a brief introduction, your script is encapsulated into a function that is + passed two parameters: valobj and dict.

+ +

dict is an internal support parameter used by LLDB and you should + not use it.
valobj is the object encapsulating the actual + variable being displayed, and its type is SBValue. The most important thing you can + do with an SBValue is retrieve its children objects, by calling + GetChildMemberWithName(), passing it the child's name as a string, or ask + it for its value, by calling GetValue(), which returns a Python string. +

+ +

If you need to delve into several levels of hierarchy, as you can do with summary + strings, you must use the method GetValueForExpressionPath(), passing it + an expression path just like those you could use for summary strings. However, if you need + to access array slices, you cannot do that (yet) via this method call, and you must + use GetChildMemberWithName() querying it for the array items one by one. + +

Other than interactively typing a Python script there are two other ways for you + to input a Python script as a summary: + +

    +
  • using the -s option to type summary add and typing the script + code as an option argument; as in:
+ + + +
+ (lldb) type summary add -s "height = + int(valobj.GetChildMemberWithName('height').GetValue());width = + int(valobj.GetChildMemberWithName('width').GetValue()); + return 'Area: ' + str(height*width)" Rectangle
+
+
    +
  • using the -F option to type summary add and giving the name of a + Python function with the correct prototype. Most probably, you will define (or have + already defined) the function in the interactive interpreter, or somehow + loaded it from a file. +
+ +

+ +
+

Regular expression typenames

+

As you noticed, in order to associate the custom summary string to the array types, one must give the array size as part of the typename. This can long become @@ -845,12 +968,10 @@

  • There's no way to do multiple dereferencing, and you need to be careful what the dereferencing operation is binding to in complicated scenarios
  • -
  • There is no way to call functions inside summary - strings, not even const ones
  • type format add does not support the -x option
  • -
  • Object location cannot be printed in the summary - string
  • +
  • Object location cannot be printed in the summary + string