diff options
-rw-r--r-- | lldb/include/lldb/Core/Debugger.h | 26 | ||||
-rw-r--r-- | lldb/include/lldb/Core/FormatManager.h | 198 | ||||
-rw-r--r-- | lldb/include/lldb/Core/ValueObject.h | 85 | ||||
-rw-r--r-- | lldb/include/lldb/Interpreter/OptionGroupVariable.h | 1 | ||||
-rw-r--r-- | lldb/include/lldb/Symbol/ClangASTContext.h | 3 | ||||
-rw-r--r-- | lldb/include/lldb/Utility/CleanUp.h | 136 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectFrame.cpp | 11 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectType.cpp | 69 | ||||
-rw-r--r-- | lldb/source/Core/Debugger.cpp | 42 | ||||
-rw-r--r-- | lldb/source/Core/FormatManager.cpp | 19 | ||||
-rw-r--r-- | lldb/source/Core/ValueObject.cpp | 615 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandInterpreter.cpp | 2 | ||||
-rw-r--r-- | lldb/source/Interpreter/OptionGroupVariable.cpp | 13 | ||||
-rw-r--r-- | lldb/source/Symbol/ClangASTContext.cpp | 29 | ||||
-rw-r--r-- | lldb/source/Symbol/SymbolContext.cpp | 2 | ||||
-rw-r--r-- | lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py | 31 | ||||
-rw-r--r-- | lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp | 3 |
17 files changed, 1156 insertions, 129 deletions
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 924fada5cc3..f43b842fd3b 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -550,6 +550,32 @@ public: 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(); + }; }; diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h index 01f4f4e6169..bd408a5697c 100644 --- a/lldb/include/lldb/Core/FormatManager.h +++ b/lldb/include/lldb/Core/FormatManager.h @@ -142,53 +142,66 @@ struct ValueFormat } }; + +template<typename KeyType, typename ValueType> +class FormatNavigator; -template<typename MapType, typename CallbackType> -class FormatNavigator +template<typename KeyType, typename ValueType> +class FormatMap { +private: + typedef typename ValueType::SharedPointer ValueSP; + Mutex m_map_mutex; + IFormatChangeListener* listener; + + friend class FormatNavigator<KeyType, ValueType>; + +public: + typedef std::map<KeyType, ValueSP> MapType; + +private: + MapType m_map; + + MapType& map() + { + return m_map; + } + + Mutex& mutex() + { + return m_map_mutex; + } + public: typedef typename MapType::iterator MapIterator; - typedef typename MapType::key_type MapKeyType; - typedef typename MapType::mapped_type MapValueType; + typedef bool(*CallbackType)(void*, KeyType, const ValueSP&); - FormatNavigator(IFormatChangeListener* lst = NULL) : + FormatMap(IFormatChangeListener* lst = NULL) : + m_map(), m_map_mutex(Mutex::eMutexTypeRecursive), - m_map(MapType()), listener(lst) { } - - bool - Get(ValueObject& vobj, MapValueType& entry) - { - Mutex::Locker(m_map_mutex); - clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType()); - bool ret = Get(vobj, type, entry); - if(ret) - entry = MapValueType(entry); - else - entry = MapValueType(); - return ret; - } void - Add(const MapKeyType &type, const MapValueType& entry) + Add(KeyType name, + const ValueSP& entry) { Mutex::Locker(m_map_mutex); - m_map[type] = MapValueType(entry); - if(listener) + m_map[name] = entry; + if (listener) listener->Changed(); } bool - Delete(const char* type) + Delete(KeyType name) { Mutex::Locker(m_map_mutex); - MapIterator iter = m_map.find(type); + MapIterator iter = m_map.find(name); if (iter == m_map.end()) return false; - m_map.erase(type); + m_map.erase(name); if(listener) listener->Changed(); return true; @@ -203,6 +216,18 @@ public: listener->Changed(); } + bool + Get(KeyType name, + ValueSP& entry) + { + Mutex::Locker(m_map_mutex); + MapIterator iter = m_map.find(name); + if (iter == m_map.end()) + return false; + entry = iter->second; + return true; + } + void LoopThrough(CallbackType callback, void* param) { @@ -212,8 +237,8 @@ public: MapIterator pos, end = m_map.end(); for (pos = m_map.begin(); pos != end; pos++) { - MapKeyType type = pos->first; - if(!callback(param, type, MapValueType(pos->second))) + KeyType type = pos->first; + if(!callback(param, type, pos->second)) break; } } @@ -225,36 +250,91 @@ public: return m_map.size(); } - ~FormatNavigator() +}; + +template<typename KeyType, typename ValueType> +class FormatNavigator +{ +private: + typedef FormatMap<KeyType,ValueType> BackEndType; + + BackEndType m_format_map; + +public: + typedef typename BackEndType::MapType MapType; + typedef typename MapType::iterator MapIterator; + typedef typename MapType::key_type MapKeyType; + typedef typename MapType::mapped_type MapValueType; + typedef typename BackEndType::CallbackType CallbackType; + + FormatNavigator(IFormatChangeListener* lst = NULL) : + m_format_map(lst) { } -private: + void + Add(const MapKeyType &type, const MapValueType& entry) + { + m_format_map.Add(type,entry); + } - Mutex m_map_mutex; - MapType m_map; - IFormatChangeListener* listener; + // using const char* instead of MapKeyType is necessary here + // to make the partial template specializations below work + bool + Delete(const char *type) + { + return m_format_map.Delete(type); + } + + bool + Get(ValueObject& vobj, MapValueType& entry) + { + clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType()); + bool ret = Get(vobj, type, entry); + if(ret) + entry = MapValueType(entry); + else + entry = MapValueType(); + return ret; + } + + void + Clear() + { + m_format_map.Clear(); + } + + void + LoopThrough(CallbackType callback, void* param) + { + m_format_map.LoopThrough(callback,param); + } + + uint32_t + GetCount() + { + return m_format_map.GetCount(); + } + +private: DISALLOW_COPY_AND_ASSIGN(FormatNavigator); + // using const char* instead of MapKeyType is necessary here + // to make the partial template specializations below work bool Get(const char* type, MapValueType& entry) { - Mutex::Locker(m_map_mutex); - MapIterator iter = m_map.find(type); - if (iter == m_map.end()) - return false; - entry = iter->second; - return true; + return m_format_map.Get(type, entry); } bool Get(ValueObject& vobj, - const clang::QualType& q_type, + clang::QualType type, MapValueType& entry) { - if (q_type.isNull()) + if (type.isNull()) return false; - clang::QualType type = q_type.getUnqualifiedType(); + // clang::QualType type = q_type.getUnqualifiedType(); type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict(); const clang::Type* typePtr = type.getTypePtrOrNull(); if (!typePtr) @@ -354,40 +434,41 @@ private: // try to strip typedef chains const clang::TypedefType* type_tdef = type->getAs<clang::TypedefType>(); if (type_tdef) + { if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry)) && entry->m_cascades) return true; + } return false; } - }; - + template<> bool -FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Get(const char* key, - SummaryFormat::SharedPointer& value); +FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Get(const char* key, SummaryFormat::SharedPointer& value); + template<> bool -FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type); - +FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Delete(const char* type); + class FormatManager : public IFormatChangeListener { - -public: - private: - typedef std::map<const char*, ValueFormat::SharedPointer> ValueMap; - typedef std::map<const char*, SummaryFormat::SharedPointer> SummaryMap; - typedef std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer> RegexSummaryMap; + typedef FormatNavigator<const char*, ValueFormat> ValueNavigator; + typedef FormatNavigator<const char*, SummaryFormat> SummaryNavigator; + typedef FormatNavigator<lldb::RegularExpressionSP, SummaryFormat> RegexSummaryNavigator; - typedef FormatNavigator<ValueMap, ValueFormat::ValueCallback> ValueNavigator; - typedef FormatNavigator<SummaryMap, SummaryFormat::SummaryCallback> SummaryNavigator; - typedef FormatNavigator<RegexSummaryMap, SummaryFormat::RegexSummaryCallback> RegexSummaryNavigator; + typedef ValueNavigator::MapType ValueMap; + typedef SummaryNavigator::MapType SummaryMap; + typedef RegexSummaryNavigator::MapType RegexSummaryMap; + typedef FormatMap<const char*, SummaryFormat> NamedSummariesMap; ValueNavigator m_value_nav; SummaryNavigator m_summary_nav; RegexSummaryNavigator m_regex_summary_nav; - + + NamedSummariesMap m_named_summaries_map; + uint32_t m_last_revision; public: @@ -396,6 +477,7 @@ public: m_value_nav(this), m_summary_nav(this), m_regex_summary_nav(this), + m_named_summaries_map(this), m_last_revision(0) { } @@ -404,7 +486,7 @@ public: ValueNavigator& Value() { return m_value_nav; } SummaryNavigator& Summary() { return m_summary_nav; } RegexSummaryNavigator& RegexSummary() { return m_regex_summary_nav; } - + NamedSummariesMap& NamedSummary() { return m_named_summaries_map; } static bool GetFormatFromCString (const char *format_cstr, diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index e87948c4711..182d078d9b3 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -93,6 +93,7 @@ public: eUnexpectedSymbol, // something is malformed in the expression eTakingAddressFailed, // impossible to apply & operator eDereferencingFailed, // impossible to apply * operator + eRangeOperatorExpanded, // [] was expanded into a VOList eUnknown = 0xFFFF }; @@ -102,6 +103,7 @@ public: eBitfield, // a bitfield eBoundedRange, // a range [low-high] eUnboundedRange, // a range [] + eValueObjectList, // several items in a VOList eInvalid = 0xFFFF }; @@ -392,6 +394,15 @@ public: const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(), ExpressionPathAftermath* final_task_on_target = NULL); + int + GetValuesForExpressionPath(const char* expression, + lldb::ValueObjectListSP& list, + const char** first_unparsed = NULL, + ExpressionPathScanEndReason* reason_to_stop = NULL, + ExpressionPathEndResultType* final_value_type = NULL, + const GetValueForExpressionPathOptions& options = GetValueForExpressionPathOptions::DefaultOptions(), + ExpressionPathAftermath* final_task_on_target = NULL); + virtual bool IsInScope () { @@ -581,6 +592,17 @@ public: lldb::DynamicValueType use_dynamic, bool scope_already_checked, bool flat_output); + + // returns true if this is a char* or a char[] + // if it is a char* and check_pointer is true, + // it also checks that the pointer is valid + bool + IsCStringContainer(bool check_pointer = false); + + void + ReadPointedString(Stream& s, + Error& error, + uint32_t max_length = 0); bool GetIsConstant () const @@ -609,6 +631,42 @@ public: m_value_str.clear(); m_format = format; } + + void + SetCustomSummaryFormat(lldb::SummaryFormatSP format) + { + m_forced_summary_format = format; + m_user_id_of_forced_summary = m_update_point.GetUpdateID(); + m_summary_str.clear(); + } + + lldb::SummaryFormatSP + GetCustomSummaryFormat() + { + return m_forced_summary_format; + } + + void + ClearCustomSummaryFormat() + { + m_forced_summary_format.reset(); + m_summary_str.clear(); + } + + bool + HasCustomSummaryFormat() + { + return (m_forced_summary_format.get()); + } + + lldb::SummaryFormatSP + GetSummaryFormat() + { + UpdateFormatsIfNeeded(); + if (HasCustomSummaryFormat()) + return m_forced_summary_format; + return m_last_summary_format; + } // Use GetParent for display purposes, but if you want to tell the parent to update itself // then use m_parent. The ValueObjectDynamicValue's parent is not the correct parent for @@ -668,10 +726,12 @@ protected: // as an independent ValueObjectConstResult, which isn't managed by us. ValueObject *m_deref_valobj; - lldb::Format m_format; - uint32_t m_last_format_mgr_revision; - lldb::SummaryFormatSP m_last_summary_format; - lldb::ValueFormatSP m_last_value_format; + lldb::Format m_format; + uint32_t m_last_format_mgr_revision; + lldb::SummaryFormatSP m_last_summary_format; + lldb::ValueFormatSP m_last_value_format; + lldb::SummaryFormatSP m_forced_summary_format; + lldb::user_id_t m_user_id_of_forced_summary; bool m_value_is_valid:1, m_value_did_change:1, m_children_count_valid:1, @@ -753,12 +813,27 @@ private: //------------------------------------------------------------------ lldb::ValueObjectSP - GetValueForExpressionPath_Impl(const char* expression, + GetValueForExpressionPath_Impl(const char* expression_cstr, const char** first_unparsed, ExpressionPathScanEndReason* reason_to_stop, ExpressionPathEndResultType* final_value_type, const GetValueForExpressionPathOptions& options, ExpressionPathAftermath* final_task_on_target); + + // this method will ONLY expand [] expressions into a VOList and return + // the number of elements it added to the VOList + // it will NOT loop through expanding the follow-up of the expression_cstr + // for all objects in the list + int + ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + lldb::ValueObjectSP root, + lldb::ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target); + DISALLOW_COPY_AND_ASSIGN (ValueObject); diff --git a/lldb/include/lldb/Interpreter/OptionGroupVariable.h b/lldb/include/lldb/Interpreter/OptionGroupVariable.h index e7dac2afc3e..387eeba36c5 100644 --- a/lldb/include/lldb/Interpreter/OptionGroupVariable.h +++ b/lldb/include/lldb/Interpreter/OptionGroupVariable.h @@ -53,6 +53,7 @@ namespace lldb_private { show_scope:1, show_decl:1; lldb::Format format; + std::string summary; private: DISALLOW_COPY_AND_ASSIGN(OptionGroupVariable); diff --git a/lldb/include/lldb/Symbol/ClangASTContext.h b/lldb/include/lldb/Symbol/ClangASTContext.h index 7159793133d..dd3c2f49d63 100644 --- a/lldb/include/lldb/Symbol/ClangASTContext.h +++ b/lldb/include/lldb/Symbol/ClangASTContext.h @@ -49,7 +49,8 @@ public: eTypeIsStructUnion = (1u << 13), eTypeIsTemplate = (1u << 14), eTypeIsTypedef = (1u << 15), - eTypeIsVector = (1u << 16) + eTypeIsVector = (1u << 16), + eTypeIsScalar = (1u << 17), }; typedef void (*CompleteTagDeclCallback)(void *baton, clang::TagDecl *); diff --git a/lldb/include/lldb/Utility/CleanUp.h b/lldb/include/lldb/Utility/CleanUp.h index 9f7b169f0ac..8597293e331 100644 --- a/lldb/include/lldb/Utility/CleanUp.h +++ b/lldb/include/lldb/Utility/CleanUp.h @@ -52,7 +52,7 @@ namespace lldb_utility { // // malloc/free example // CleanUp <void *, void> malloced_bytes(malloc(32), NULL, free); //---------------------------------------------------------------------- -template <typename T, typename R> +template <typename T, typename R = void> class CleanUp { public: @@ -182,6 +182,140 @@ private: // Outlaw default constructor, copy constructor and the assignment operator DISALLOW_COPY_AND_ASSIGN (CleanUp); }; + +template <typename T, typename R, typename A0> +class CleanUp2 +{ +public: + typedef T value_type; + typedef R (*CallbackType)(value_type, A0); + + //---------------------------------------------------------------------- + // Constructor that sets the current value only. No values are + // considered to be invalid and the cleanup function will be called + // regardless of the value of m_current_value. + //---------------------------------------------------------------------- + CleanUp2 (value_type value, CallbackType callback, A0 arg) : + m_current_value (value), + m_invalid_value (), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (false), + m_argument(arg) + { + } + + //---------------------------------------------------------------------- + // Constructor that sets the current value and also the invalid value. + // The cleanup function will be called on "m_value" as long as it isn't + // equal to "m_invalid_value". + //---------------------------------------------------------------------- + CleanUp2 (value_type value, value_type invalid, CallbackType callback, A0 arg) : + m_current_value (value), + m_invalid_value (invalid), + m_callback (callback), + m_callback_called (false), + m_invalid_value_is_valid (true), + m_argument(arg) + { + } + + //---------------------------------------------------------------------- + // Automatically cleanup when this object goes out of scope. + //---------------------------------------------------------------------- + ~CleanUp2 () + { + clean(); + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + value_type get() + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Access the value stored in this class + //---------------------------------------------------------------------- + const value_type + get() const + { + return m_current_value; + } + + //---------------------------------------------------------------------- + // Reset the owned value to "value". If a current value is valid and + // the cleanup callback hasn't been called, the previous value will + // be cleaned up (see void CleanUp::clean()). + //---------------------------------------------------------------------- + void + set (const value_type value) + { + // Cleanup the current value if needed + clean (); + // Now set the new value and mark our callback as not called + m_callback_called = false; + m_current_value = value; + } + + //---------------------------------------------------------------------- + // Checks is "m_current_value" is valid. The value is considered valid + // no invalid value was supplied during construction of this object or + // if an invalid value was supplied and "m_current_value" is not equal + // to "m_invalid_value". + // + // Returns true if "m_current_value" is valid, false otherwise. + //---------------------------------------------------------------------- + bool + is_valid() const + { + if (m_invalid_value_is_valid) + return m_current_value != m_invalid_value; + return true; + } + + //---------------------------------------------------------------------- + // This function will call the cleanup callback provided in the + // constructor one time if the value is considered valid (See is_valid()). + // This function sets m_callback_called to true so we don't call the + // cleanup callback multiple times on the same value. + //---------------------------------------------------------------------- + void + clean() + { + if (m_callback && !m_callback_called) + { + m_callback_called = true; + if (is_valid()) + m_callback(m_current_value, m_argument); + } + } + + //---------------------------------------------------------------------- + // Cancels the cleanup that would have been called on "m_current_value" + // if it was valid. This function can be used to release the value + // contained in this object so ownership can be transfered to the caller. + //---------------------------------------------------------------------- + value_type + release () + { + m_callback_called = true; + return m_current_value; + } + +private: + value_type m_current_value; + const value_type m_invalid_value; + CallbackType m_callback; + bool m_callback_called; + bool m_invalid_value_is_valid; + A0 m_argument; + + // Outlaw default constructor, copy constructor and the assignment operator + DISALLOW_COPY_AND_ASSIGN (CleanUp2); +}; } // namespace lldb_utility diff --git a/lldb/source/Commands/CommandObjectFrame.cpp b/lldb/source/Commands/CommandObjectFrame.cpp index f82c06bcc15..9d8f429cabc 100644 --- a/lldb/source/Commands/CommandObjectFrame.cpp +++ b/lldb/source/Commands/CommandObjectFrame.cpp @@ -439,6 +439,10 @@ public: const char *name_cstr = NULL; size_t idx; + + SummaryFormatSP summary_format_sp; + if (!m_option_variable.summary.empty()) + Debugger::NamedSummaryFormats::Get(ConstString(m_option_variable.summary.c_str()), summary_format_sp); if (variable_list) { @@ -484,7 +488,8 @@ public: if (var_sp->DumpDeclaration(&s, show_fullpaths, show_module)) s.PutCString (": "); } - + if (summary_format_sp) + valobj_sp->SetCustomSummaryFormat(summary_format_sp); ValueObject::DumpValueObject (result.GetOutputStream(), valobj_sp.get(), var_sp->GetName().AsCString(), @@ -534,6 +539,8 @@ public: var_sp->GetDeclaration ().DumpStopContext (&s, false); s.PutCString (": "); } + if (summary_format_sp) + valobj_sp->SetCustomSummaryFormat(summary_format_sp); ValueObject::DumpValueObject (result.GetOutputStream(), valobj_sp.get(), valobj_sp->GetParent() ? name_cstr : NULL, @@ -622,6 +629,8 @@ public: var_sp->GetDeclaration ().DumpStopContext (&s, false); s.PutCString (": "); } + if (summary_format_sp) + valobj_sp->SetCustomSummaryFormat(summary_format_sp); ValueObject::DumpValueObject (result.GetOutputStream(), valobj_sp.get(), name_cstr, diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 2e0aa5c0749..c0ffddb62ae 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -468,6 +468,9 @@ private: case 'x': m_regex = true; break; + case 'n': + m_name = new ConstString(option_arg); + break; default: error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); break; @@ -486,6 +489,7 @@ private: m_skip_references = false; m_skip_pointers = false; m_regex = false; + m_name = NULL; } const OptionDefinition* @@ -508,6 +512,7 @@ private: bool m_skip_pointers; bool m_regex; std::string m_format_string; + ConstString* m_name; }; CommandOptions m_options; @@ -601,7 +606,7 @@ public: { const size_t argc = command.GetArgumentCount(); - if (argc < 1) + if (argc < 1 && !m_options.m_name) { result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); @@ -636,14 +641,14 @@ public: for(int i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); - if(!typeA || typeA[0] == '\0') + if (!typeA || typeA[0] == '\0') { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } ConstString typeCS(typeA); - if(!m_options.m_regex) + if (!m_options.m_regex) { Debugger::SummaryFormats::Add(typeCS, entry); } @@ -660,6 +665,21 @@ public: Debugger::RegexSummaryFormats::Add(typeRX, entry); } } + + if (m_options.m_name) + { + if( (bool)(*(m_options.m_name)) ) + { + Debugger::NamedSummaryFormats::Add(*(m_options.m_name), entry); + } + else + { + result.AppendError("added to types, but not given a name"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } @@ -676,7 +696,8 @@ CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] = { LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeBoolean, "Type names are actually regular expressions."}, { LLDB_OPT_SET_1 , true, "inline-children", 'c', no_argument, NULL, 0, eArgTypeBoolean, "If true, inline all child values into summary string."}, { LLDB_OPT_SET_2 , true, "format-string", 'f', required_argument, NULL, 0, eArgTypeSummaryString, "Format string used to display text and object contents."}, - { LLDB_OPT_SET_2, false, "expand", 'e', no_argument, NULL, 0, eArgTypeBoolean, "Expand aggregate data types to show children on separate lines."}, + { LLDB_OPT_SET_2, false, "expand", 'e', no_argument, NULL, 0, eArgTypeBoolean, "Expand aggregate data types to show children on separate lines."}, + { LLDB_OPT_SET_2, false, "name", 'n', required_argument, NULL, 0, eArgTypeName, "A name for this summary string."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; @@ -734,7 +755,9 @@ public: bool delete_summary = Debugger::SummaryFormats::Delete(typeCS); bool delete_regex = Debugger::RegexSummaryFormats::Delete(typeCS); - if (delete_summary || delete_regex) + bool delete_named = Debugger::NamedSummaryFormats::Delete(typeCS); + + if (delete_summary || delete_regex || delete_named) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); @@ -774,6 +797,7 @@ public: { Debugger::SummaryFormats::Clear(); Debugger::RegexSummaryFormats::Clear(); + Debugger::NamedSummaryFormats::Clear(); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } @@ -847,22 +871,33 @@ public: Debugger::SummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); delete param; - if(Debugger::RegexSummaryFormats::GetCount() == 0) + if(Debugger::RegexSummaryFormats::GetCount() > 0) { - result.SetStatus(eReturnStatusSuccessFinishResult); - return result.Succeeded(); + 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); + delete rxparam; } - 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); + if(Debugger::NamedSummaryFormats::GetCount() > 0) + { + result.GetOutputStream().Printf("Named summaries:\n"); + if (argc == 1) { + RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); + regex->Compile(command.GetArgumentAtIndex(0)); + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,regex); + } + else + param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result); + Debugger::NamedSummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); + delete param; } - else - rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result); - Debugger::RegexSummaryFormats::LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, rxparam); - delete rxparam; result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index e7b97a59077..9b34331e84f 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -1821,6 +1821,48 @@ Debugger::RegexSummaryFormats::GetCount() return GetFormatManager().RegexSummary().GetCount(); } +bool +Debugger::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) +{ + GetFormatManager().NamedSummary().Add(type.AsCString(),entry); +} + +bool +Debugger::NamedSummaryFormats::Delete(const ConstString &type) +{ + return GetFormatManager().NamedSummary().Delete(type.AsCString()); +} + +void +Debugger::NamedSummaryFormats::Clear() +{ + GetFormatManager().NamedSummary().Clear(); +} + +void +Debugger::NamedSummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton) +{ + GetFormatManager().NamedSummary().LoopThrough(callback, callback_baton); +} + +uint32_t +Debugger::NamedSummaryFormats::GetCurrentRevision() +{ + return GetFormatManager().GetCurrentRevision(); +} + +uint32_t +Debugger::NamedSummaryFormats::GetCount() +{ + return GetFormatManager().NamedSummary().GetCount(); +} + #pragma mark Debugger::SettingsController //-------------------------------------------------- diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp index 24b565af996..ece30068e4d 100644 --- a/lldb/source/Core/FormatManager.cpp +++ b/lldb/source/Core/FormatManager.cpp @@ -154,12 +154,11 @@ FormatManager::GetFormatAsCString (Format format) template<> bool -FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Get(const char* key, - SummaryFormat::SharedPointer& value) +FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Get(const char* key, SummaryFormat::SharedPointer& value) { - Mutex::Locker(m_map_mutex); - MapIterator pos, end = m_map.end(); - for (pos = m_map.begin(); pos != end; pos++) + Mutex::Locker(m_format_map.mutex()); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) { lldb::RegularExpressionSP regex = pos->first; if (regex->Execute(key)) @@ -173,16 +172,16 @@ FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer template<> bool -FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type) +FormatNavigator<lldb::RegularExpressionSP, SummaryFormat>::Delete(const char* type) { - Mutex::Locker(m_map_mutex); - MapIterator pos, end = m_map.end(); - for (pos = m_map.begin(); pos != end; pos++) + Mutex::Locker(m_format_map.mutex()); + MapIterator pos, end = m_format_map.map().end(); + for (pos = m_format_map.map().begin(); pos != end; pos++) { lldb::RegularExpressionSP regex = pos->first; if ( ::strcmp(type,regex->GetText()) == 0) { - m_map.erase(pos); + m_format_map.map().erase(pos); return true; } } diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 5c548dfbb97..d39b836f021 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -77,7 +77,8 @@ ValueObject::ValueObject (ValueObject &parent) : m_is_bitfield_for_scalar(false), m_last_format_mgr_revision(0), m_last_summary_format(), - m_last_value_format() + m_last_value_format(), + m_forced_summary_format() { m_manager->ManageObject(this); } @@ -114,7 +115,8 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) : m_is_bitfield_for_scalar(false), m_last_format_mgr_revision(0), m_last_summary_format(), - m_last_value_format() + m_last_value_format(), + m_forced_summary_format() { m_manager = new ValueObjectManager(); m_manager->ManageObject (this); @@ -189,6 +191,11 @@ ValueObject::UpdateFormatsIfNeeded() /*printf("CHECKING FOR UPDATES. I am at revision %d, while the format manager is at revision %d\n", m_last_format_mgr_revision, Debugger::ValueFormats::GetCurrentRevision());*/ + if (HasCustomSummaryFormat() && m_update_point.GetUpdateID() != m_user_id_of_forced_summary) + { + ClearCustomSummaryFormat(); + m_summary_str.clear(); + } if (m_last_format_mgr_revision != Debugger::ValueFormats::GetCurrentRevision()) { if (m_last_summary_format.get()) @@ -495,7 +502,9 @@ ValueObject::GetSummaryAsCString () { if (m_summary_str.empty()) { - if (m_last_summary_format.get()) + SummaryFormat* summary_format = GetSummaryFormat().get(); + + if (summary_format) { StreamString s; ExecutionContext exe_ctx; @@ -504,7 +513,7 @@ ValueObject::GetSummaryAsCString () if (exe_ctx.frame) sc = exe_ctx.frame->GetSymbolContext(eSymbolContextEverything); - if (m_last_summary_format->m_show_members_oneliner) + if (summary_format->m_show_members_oneliner) { const uint32_t num_children = GetNumChildren(); if (num_children) @@ -536,7 +545,7 @@ ValueObject::GetSummaryAsCString () } else { - if (Debugger::FormatPrompt(m_last_summary_format->m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, this)) + if (Debugger::FormatPrompt(summary_format->m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, this)) { m_summary_str.swap(s.GetString()); return m_summary_str.c_str(); @@ -719,6 +728,157 @@ ValueObject::GetSummaryAsCString () return m_summary_str.c_str(); } +bool +ValueObject::IsCStringContainer(bool check_pointer) +{ + clang_type_t elem_or_pointee_clang_type; + const Flags type_flags (ClangASTContext::GetTypeInfo (GetClangType(), + GetClangAST(), + &elem_or_pointee_clang_type)); + bool is_char_arr_ptr (type_flags.AnySet (ClangASTContext::eTypeIsArray | ClangASTContext::eTypeIsPointer) && + ClangASTContext::IsCharType (elem_or_pointee_clang_type)); + if (!is_char_arr_ptr) + return false; + if (!check_pointer) + return true; + if (type_flags.Test(ClangASTContext::eTypeIsArray)) + return true; + lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + cstr_address = GetAddressOf (cstr_address_type, true); + return (cstr_address != LLDB_INVALID_ADDRESS); +} + +void +ValueObject::ReadPointedString(Stream& s, + Error& error, + uint32_t max_length) +{ + + if (max_length == 0) + max_length = 128; // this should be a setting, or a formatting parameter + + clang_type_t clang_type = GetClangType(); + clang_type_t elem_or_pointee_clang_type; + const Flags type_flags (ClangASTContext::GetTypeInfo (clang_type, + GetClangAST(), + &elem_or_pointee_clang_type)); + if (type_flags.AnySet (ClangASTContext::eTypeIsArray | ClangASTContext::eTypeIsPointer) && + ClangASTContext::IsCharType (elem_or_pointee_clang_type)) + { + ExecutionContextScope *exe_scope = GetExecutionContextScope(); + if (exe_scope) + { + Target *target = exe_scope->CalculateTarget(); + if (target != NULL) + { + lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + + size_t cstr_len = 0; + bool capped_data = false; + if (type_flags.Test (ClangASTContext::eTypeIsArray)) + { + // We have an array + cstr_len = ClangASTContext::GetArraySize (clang_type); + if (cstr_len > max_length) // TODO: make cap a setting + { + cstr_len = ClangASTContext::GetArraySize (clang_type); + if (cstr_len > max_length) // TODO: make cap a setting + { + capped_data = true; + cstr_len = max_length; + } + } + cstr_address = GetAddressOf (cstr_address_type, true); + } + else + { + // We have a pointer + cstr_address = GetPointerValue (cstr_address_type, true); + } + if (cstr_address != LLDB_INVALID_ADDRESS) + { + Address cstr_so_addr (NULL, cstr_address); + DataExtractor data; + size_t bytes_read = 0; + std::vector<char> data_buffer; + Error error; + bool prefer_file_cache = false; + if (cstr_len > 0) + { + data_buffer.resize(cstr_len); + data.SetData (&data_buffer.front(), data_buffer.size(), lldb::endian::InlHostByteOrder()); + bytes_read = target->ReadMemory (cstr_so_addr, + prefer_file_cache, + &data_buffer.front(), + cstr_len, + error); + if (bytes_read > 0) + { + s << '"'; + data.Dump (&s, + 0, // Start offset in "data" + eFormatCharArray, // Print as characters + 1, // Size of item (1 byte for a char!) + bytes_read, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + if (capped_data) + s << "..."; + s << '"'; + } + } + else + { + const size_t k_max_buf_size = 256; + data_buffer.resize (k_max_buf_size + 1); + // NULL terminate in case we don't get the entire C string + data_buffer.back() = '\0'; + + s << '"'; + + data.SetData (&data_buffer.front(), data_buffer.size(), endian::InlHostByteOrder()); + while ((bytes_read = target->ReadMemory (cstr_so_addr, + prefer_file_cache, + &data_buffer.front(), + k_max_buf_size, + error)) > 0) + { + size_t len = strlen(&data_buffer.front()); + if (len == 0) + break; + if (len > bytes_read) + len = bytes_read; + + data.Dump (&s, + 0, // Start offset in "data" + eFormatCharArray, // Print as characters + 1, // Size of item (1 byte for a char!) + len, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + + if (len < k_max_buf_size) + break; + cstr_so_addr.Slide (k_max_buf_size); + } + s << '"'; + } + } + } + } + } + else + { + error.SetErrorString("impossible to read a string from this object"); + } +} + const char * ValueObject::GetObjectDescription () { @@ -894,13 +1054,25 @@ ValueObject::DumpPrintableRepresentation(Stream& s, ValueObjectRepresentationStyle val_obj_display, lldb::Format custom_format) { - const char *targetvalue = GetPrintableRepresentation(val_obj_display, custom_format); - if(targetvalue) - s.PutCString(targetvalue); - bool var_success = (targetvalue != NULL); - if(custom_format != eFormatInvalid) - SetFormat(eFormatDefault); - return var_success; + + if (IsCStringContainer(true) && + val_obj_display == ValueObject::eDisplayValue && + custom_format == lldb::eFormatCString) + { + Error error; + ReadPointedString(s, error); + return error.Success(); + } + else + { + const char *targetvalue = GetPrintableRepresentation(val_obj_display, custom_format); + if(targetvalue) + s.PutCString(targetvalue); + bool var_success = (targetvalue != NULL); + if(custom_format != eFormatInvalid) + SetFormat(eFormatDefault); + return var_success; + } } addr_t @@ -1503,6 +1675,92 @@ ValueObject::GetValueForExpressionPath(const char* expression, return ret_val; // final_task_on_target will still have its original value, so you know I did not do it } +int +ValueObject::GetValuesForExpressionPath(const char* expression, + lldb::ValueObjectListSP& list, + const char** first_unparsed, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_value_type, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* final_task_on_target) +{ + const char* dummy_first_unparsed; + ExpressionPathScanEndReason dummy_reason_to_stop; + ExpressionPathEndResultType dummy_final_value_type; + ExpressionPathAftermath dummy_final_task_on_target = ValueObject::eNothing; + + ValueObjectSP ret_val = GetValueForExpressionPath_Impl(expression, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, + final_value_type ? final_value_type : &dummy_final_value_type, + options, + final_task_on_target ? final_task_on_target : &dummy_final_task_on_target); + + if (!ret_val.get()) // if there are errors, I add nothing to the list + return 0; + + if (*reason_to_stop != eArrayRangeOperatorMet) + { + // I need not expand a range, just post-process the final value and return + if (!final_task_on_target || *final_task_on_target == ValueObject::eNothing) + { + list->Append(ret_val); + return 1; + } + if (ret_val.get() && *final_value_type == ePlain) // I can only deref and takeaddress of plain objects + { + if (*final_task_on_target == ValueObject::eDereference) + { + Error error; + ValueObjectSP final_value = ret_val->Dereference(error); + if (error.Fail() || !final_value.get()) + { + *reason_to_stop = ValueObject::eDereferencingFailed; + *final_value_type = ValueObject::eInvalid; + return 0; + } + else + { + *final_task_on_target = ValueObject::eNothing; + list->Append(final_value); + return 1; + } + } + if (*final_task_on_target == ValueObject::eTakeAddress) + { + Error error; + ValueObjectSP final_value = ret_val->AddressOf(error); + if (error.Fail() || !final_value.get()) + { + *reason_to_stop = ValueObject::eTakingAddressFailed; + *final_value_type = ValueObject::eInvalid; + return 0; + } + else + { + *final_task_on_target = ValueObject::eNothing; + list->Append(final_value); + return 1; + } + } + } + } + else + { + return ExpandArraySliceExpression(first_unparsed ? *first_unparsed : dummy_first_unparsed, + first_unparsed ? first_unparsed : &dummy_first_unparsed, + ret_val, + list, + reason_to_stop ? reason_to_stop : &dummy_reason_to_stop, + final_value_type ? final_value_type : &dummy_final_value_type, + options, + final_task_on_target ? final_task_on_target : &dummy_final_task_on_target); + } + // in any non-covered case, just do the obviously right thing + list->Append(ret_val); + return 1; +} + lldb::ValueObjectSP ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, const char** first_unparsed, @@ -1524,6 +1782,12 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr lldb::clang_type_t root_clang_type = root->GetClangType(); + lldb::clang_type_t pointee_clang_type; + Flags root_clang_type_info,pointee_clang_type_info; + + root_clang_type_info = Flags(ClangASTContext::GetTypeInfo(root_clang_type, GetClangAST(), &pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info = Flags(ClangASTContext::GetTypeInfo(pointee_clang_type, GetClangAST(), NULL)); if (!expression_cstr || *expression_cstr == '\0') { @@ -1536,16 +1800,15 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, case '-': { if (options.m_check_dot_vs_arrow_syntax && - !ClangASTContext::IsPointerType(root_clang_type)) // if you are trying to use -> on a non-pointer and I must catch the error + root_clang_type_info.Test(ClangASTContext::eTypeIsPointer) ) // if you are trying to use -> on a non-pointer and I must catch the error { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eArrowInsteadOfDot; *final_result = ValueObject::eInvalid; return ValueObjectSP(); } - const uint32_t pointer_type_flags = ClangASTContext::GetTypeInfo (root_clang_type, NULL, NULL); - if ((pointer_type_flags & ClangASTContext::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden - (pointer_type_flags & ClangASTContext::eTypeIsPointer) && + if (root_clang_type_info.Test(ClangASTContext::eTypeIsObjC) && // if yo are trying to extract an ObjC IVar when this is forbidden + root_clang_type_info.Test(ClangASTContext::eTypeIsPointer) && options.m_no_fragile_ivar) { *first_unparsed = expression_cstr; @@ -1565,7 +1828,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, case '.': // or fallthrough from -> { if (options.m_check_dot_vs_arrow_syntax && *expression_cstr == '.' && - ClangASTContext::IsPointerType(root_clang_type)) // if you are trying to use . on a pointer and I must catch the error + root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) // if you are trying to use . on a pointer and I must catch the error { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eDotInsteadOfArrow; @@ -1616,9 +1879,9 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } case '[': { - if (!ClangASTContext::IsArrayType(root_clang_type) && !ClangASTContext::IsPointerType(root_clang_type)) // if this is not a T[] nor a T* + if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray) && !root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) // if this is not a T[] nor a T* { - if (!ClangASTContext::IsScalarType(root_clang_type)) // if this is not even a scalar, this syntax is just plain wrong! + if (!root_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eRangeOperatorInvalid; @@ -1635,7 +1898,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays { - if (!ClangASTContext::IsArrayType(root_clang_type)) + if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray)) { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eEmptyRangeNotAllowed; @@ -1672,7 +1935,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays { - if (ClangASTContext::IsArrayType(root_clang_type)) + if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray)) { *first_unparsed = expression_cstr+2; *reason_to_stop = ValueObject::eArrayRangeOperatorMet; @@ -1688,7 +1951,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } } // from here on we do have a valid index - if (ClangASTContext::IsArrayType(root_clang_type)) + if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray)) { ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); if (!child_valobj_sp) @@ -1708,10 +1971,10 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, return ValueObjectSP(); } } - else if (ClangASTContext::IsPointerType(root_clang_type)) + else if (root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) { if (*what_next == ValueObject::eDereference && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield - ClangASTContext::IsScalarType(clang::QualType::getFromOpaquePtr(root_clang_type).getTypePtr()->getPointeeType().getAsOpaquePtr())) + pointee_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) { Error error; root = root->Dereference(error); @@ -1790,7 +2053,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, index_lower = index_higher; index_higher = temp; } - if (ClangASTContext::IsScalarType(root_clang_type)) // expansion only works for scalars + if (root_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) // expansion only works for scalars { root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); if (!root.get()) @@ -1808,9 +2071,9 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, return root; } } - else if (ClangASTContext::IsPointerType(root_clang_type) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + else if (root_clang_type_info.Test(ClangASTContext::eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield *what_next == ValueObject::eDereference && - ClangASTContext::IsScalarType(clang::QualType::getFromOpaquePtr(root_clang_type).getTypePtr()->getPointeeType().getAsOpaquePtr())) + pointee_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) { Error error; root = root->Dereference(error); @@ -1849,6 +2112,302 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, } } +int +ValueObject::ExpandArraySliceExpression(const char* expression_cstr, + const char** first_unparsed, + lldb::ValueObjectSP root, + lldb::ValueObjectListSP& list, + ExpressionPathScanEndReason* reason_to_stop, + ExpressionPathEndResultType* final_result, + const GetValueForExpressionPathOptions& options, + ExpressionPathAftermath* what_next) +{ + if (!root.get()) + return 0; + + *first_unparsed = expression_cstr; + + while (true) + { + + const char* expression_cstr = *first_unparsed; // hide the top level expression_cstr + + lldb::clang_type_t root_clang_type = root->GetClangType(); + lldb::clang_type_t pointee_clang_type; + Flags root_clang_type_info,pointee_clang_type_info; + + root_clang_type_info = Flags(ClangASTContext::GetTypeInfo(root_clang_type, GetClangAST(), &pointee_clang_type)); + if (pointee_clang_type) + pointee_clang_type_info = Flags(ClangASTContext::GetTypeInfo(pointee_clang_type, GetClangAST(), NULL)); + + if (!expression_cstr || *expression_cstr == '\0') + { + *reason_to_stop = ValueObject::eEndOfString; + list->Append(root); + return 1; + } + + switch (*expression_cstr) + { + case '[': + { + if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray) && !root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) // if this is not a T[] nor a T* + { + if (!root_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) // if this is not even a scalar, this syntax is just plain wrong! + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eRangeOperatorInvalid; + *final_result = ValueObject::eInvalid; + return 0; + } + else if (!options.m_allow_bitfields_syntax) // if this is a scalar, check that we can expand bitfields + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eRangeOperatorNotAllowed; + *final_result = ValueObject::eInvalid; + return 0; + } + } + if (*(expression_cstr+1) == ']') // if this is an unbounded range it only works for arrays + { + if (!root_clang_type_info.Test(ClangASTContext::eTypeIsArray)) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eEmptyRangeNotAllowed; + *final_result = ValueObject::eInvalid; + return 0; + } + else // expand this into list + { + int max_index = root->GetNumChildren() - 1; + for (int index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eRangeOperatorExpanded; + *final_result = ValueObject::eValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + } + const char *separator_position = ::strchr(expression_cstr+1,'-'); + const char *close_bracket_position = ::strchr(expression_cstr+1,']'); + if (!close_bracket_position) // if there is no ], this is a syntax error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eUnexpectedSymbol; + *final_result = ValueObject::eInvalid; + return 0; + } + if (!separator_position || separator_position > close_bracket_position) // if no separator, this is either [] or [N] + { + char *end = NULL; + unsigned long index = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eUnexpectedSymbol; + *final_result = ValueObject::eInvalid; + return 0; + } + if (end - expression_cstr == 1) // if this is [], only return a valid value for arrays + { + if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray)) + { + int max_index = root->GetNumChildren() - 1; + for (int index = 0; index < max_index; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = expression_cstr+2; + *reason_to_stop = ValueObject::eRangeOperatorExpanded; + *final_result = ValueObject::eValueObjectList; + return max_index; // tell me number of items I added to the VOList + } + else + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eEmptyRangeNotAllowed; + *final_result = ValueObject::eInvalid; + return 0; + } + } + // from here on we do have a valid index + if (root_clang_type_info.Test(ClangASTContext::eTypeIsArray)) + { + root = root->GetChildAtIndex(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eNoSuchChild; + *final_result = ValueObject::eInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eRangeOperatorExpanded; + *final_result = ValueObject::eValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTContext::eTypeIsPointer)) + { + if (*what_next == ValueObject::eDereference && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + pointee_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eDereferencingFailed; + *final_result = ValueObject::eInvalid; + return 0; + } + else + { + *what_next = eNothing; + continue; + } + } + else + { + root = root->GetSyntheticArrayMemberFromPointer(index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eNoSuchChild; + *final_result = ValueObject::eInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eRangeOperatorExpanded; + *final_result = ValueObject::eValueObjectList; + return 1; + } + } + } + else /*if (ClangASTContext::IsScalarType(root_clang_type))*/ + { + root = root->GetSyntheticBitFieldChild(index, index, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eNoSuchChild; + *final_result = ValueObject::eInvalid; + return 0; + } + else // we do not know how to expand members of bitfields, so we just return and let the caller do any further processing + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eRangeOperatorExpanded; + *final_result = ValueObject::eValueObjectList; + return 1; + } + } + } + else // we have a low and a high index + { + char *end = NULL; + unsigned long index_lower = ::strtoul (expression_cstr+1, &end, 0); + if (!end || end != separator_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eUnexpectedSymbol; + *final_result = ValueObject::eInvalid; + return 0; + } + unsigned long index_higher = ::strtoul (separator_position+1, &end, 0); + if (!end || end != close_bracket_position) // if something weird is in our way return an error + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eUnexpectedSymbol; + *final_result = ValueObject::eInvalid; + return 0; + } + if (index_lower > index_higher) // swap indices if required + { + unsigned long temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + if (root_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) // expansion only works for scalars + { + root = root->GetSyntheticBitFieldChild(index_lower, index_higher, true); + if (!root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eNoSuchChild; + *final_result = ValueObject::eInvalid; + return 0; + } + else + { + list->Append(root); + *first_unparsed = end+1; // skip ] + *reason_to_stop = ValueObject::eRangeOperatorExpanded; + *final_result = ValueObject::eValueObjectList; + return 1; + } + } + else if (root_clang_type_info.Test(ClangASTContext::eTypeIsPointer) && // if this is a ptr-to-scalar, I am accessing it by index and I would have deref'ed anyway, then do it now and use this as a bitfield + *what_next == ValueObject::eDereference && + pointee_clang_type_info.Test(ClangASTContext::eTypeIsScalar)) + { + Error error; + root = root->Dereference(error); + if (error.Fail() || !root.get()) + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eDereferencingFailed; + *final_result = ValueObject::eInvalid; + return 0; + } + else + { + *what_next = ValueObject::eNothing; + continue; + } + } + else + { + for (int index = index_lower; + index <= index_higher; index++) + { + ValueObjectSP child = + root->GetChildAtIndex(index, true); + list->Append(child); + } + *first_unparsed = end+1; + *reason_to_stop = ValueObject::eRangeOperatorExpanded; + *final_result = ValueObject::eValueObjectList; + return index_higher-index_lower+1; // tell me number of items I added to the VOList + } + } + break; + } + default: // some non-[ separator, or something entirely wrong, is in the way + { + *first_unparsed = expression_cstr; + *reason_to_stop = ValueObject::eUnexpectedSymbol; + *final_result = ValueObject::eInvalid; + return 0; + break; + } + } + } +} + void ValueObject::DumpValueObject ( @@ -1921,7 +2480,7 @@ ValueObject::DumpValueObject const char *val_cstr = NULL; const char *sum_cstr = NULL; - SummaryFormat* entry = valobj->m_last_summary_format.get(); + SummaryFormat* entry = valobj->GetSummaryFormat().get(); if (err_cstr == NULL) { diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 8e499d7de43..bd934c3aa07 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -935,7 +935,7 @@ CommandInterpreter::HandleCommand (const char *command_line, // Make a scoped cleanup object that will clear the crash description string // on exit of this function. - lldb_utility::CleanUp <const char *, void> crash_description_cleanup(NULL, Host::SetCrashDescription); + lldb_utility::CleanUp <const char *> crash_description_cleanup(NULL, Host::SetCrashDescription); if (log) log->Printf ("Processing command: %s", command_line); diff --git a/lldb/source/Interpreter/OptionGroupVariable.cpp b/lldb/source/Interpreter/OptionGroupVariable.cpp index e553e1bea47..9cf8d417610 100644 --- a/lldb/source/Interpreter/OptionGroupVariable.cpp +++ b/lldb/source/Interpreter/OptionGroupVariable.cpp @@ -19,6 +19,7 @@ using namespace lldb; using namespace lldb_private; +// if you add any options here, remember to update the counters in OptionGroupVariable::GetNumDefinitions() static OptionDefinition g_option_table[] = { @@ -28,7 +29,8 @@ g_option_table[] = { LLDB_OPT_SET_1, false, "show-declaration",'c', no_argument, NULL, 0, eArgTypeNone, "Show variable declaration information (source file and line where the variable was declared)."}, { LLDB_OPT_SET_1, false, "format", 'f', required_argument, NULL, 0, eArgTypeExprFormat, "Specify the format that the variable output should use."}, { LLDB_OPT_SET_1, false, "regex", 'r', no_argument, NULL, 0, eArgTypeRegularExpression, "The <variable-name> argument for name lookups are regular expressions."}, - { LLDB_OPT_SET_1, false, "scope", 's', no_argument, NULL, 0, eArgTypeNone, "Show variable scope (argument, local, global, static)."} + { LLDB_OPT_SET_1, false, "scope", 's', no_argument, NULL, 0, eArgTypeNone, "Show variable scope (argument, local, global, static)."}, + { LLDB_OPT_SET_1, false, "summary", 'y', required_argument, NULL, 0, eArgTypeName, "Specify the summary that the variable output should use."}, }; @@ -62,7 +64,9 @@ OptionGroupVariable::SetOptionValue (CommandInterpreter &interpreter, case 's': show_scope = true; break; - + case 'y': + summary = std::string(option_arg); + break; default: error.SetErrorStringWithFormat("Invalid short option character '%c'.\n", short_option); break; @@ -81,6 +85,7 @@ OptionGroupVariable::OptionParsingStarting (CommandInterpreter &interpreter) format = lldb::eFormatDefault; use_regex = false; show_scope = false; + summary = ""; } @@ -101,9 +106,9 @@ uint32_t OptionGroupVariable::GetNumDefinitions () { if (include_frame_options) - return 7; + return 8; else - return 4; + return 5; } diff --git a/lldb/source/Symbol/ClangASTContext.cpp b/lldb/source/Symbol/ClangASTContext.cpp index 6e3a83e162c..8ccde5ce1e9 100644 --- a/lldb/source/Symbol/ClangASTContext.cpp +++ b/lldb/source/Symbol/ClangASTContext.cpp @@ -2077,7 +2077,30 @@ ClangASTContext::GetTypeInfo if (ast && pointee_or_element_clang_type) *pointee_or_element_clang_type = ast->ObjCBuiltinClassTy.getAsOpaquePtr(); return eTypeIsBuiltIn | eTypeIsPointer | eTypeHasValue; - + break; + case clang::BuiltinType::Bool: + case clang::BuiltinType::Char_U: + case clang::BuiltinType::UChar: + case clang::BuiltinType::WChar_U: + case clang::BuiltinType::Char16: + case clang::BuiltinType::Char32: + case clang::BuiltinType::UShort: + case clang::BuiltinType::UInt: + case clang::BuiltinType::ULong: + case clang::BuiltinType::ULongLong: + case clang::BuiltinType::UInt128: + case clang::BuiltinType::Char_S: + case clang::BuiltinType::SChar: + case clang::BuiltinType::WChar_S: + case clang::BuiltinType::Short: + case clang::BuiltinType::Int: + case clang::BuiltinType::Long: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::Int128: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::LongDouble: + return eTypeIsBuiltIn | eTypeHasValue | eTypeIsScalar; default: break; } @@ -4645,6 +4668,10 @@ ClangASTContext::GetArraySize (clang_type_t clang_type) case clang::Type::Typedef: return ClangASTContext::GetArraySize(cast<TypedefType>(qual_type)->getDecl()->getUnderlyingType().getAsOpaquePtr()); + break; + + default: + break; } } return 0; diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index 1f24f1339bd..0898b812552 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -164,7 +164,7 @@ SymbolContext::DumpStopContext dumped_something = true; s->PutCString(" at "); if (line_entry.DumpStopContext(s, show_fullpaths)) - return; + return dumped_something; } } } 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 68eab1897bd..12f6b6d0ba9 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-cpp/TestDataFormatterCpp.py @@ -73,7 +73,16 @@ class DataFormatterTestCase(TestBase): self.expect("frame variable", patterns = ['\(Speed\) SPILookHex = 0x[0-9a-f]+' # Speed should look hex-ish now. ]); - + + # gcc4.2 on Mac OS X skips typedef chains in the DWARF output + if self.getCompiler() in ['clang', 'llvm-gcc']: + self.expect("frame variable", + patterns = ['\(SignalMask\) SMILookHex = 0x[0-9a-f]+' # SignalMask should look hex-ish now. + ]); + self.expect("frame variable", matching=False, + patterns = ['\(Type4\) T4ILookChar = 0x[0-9a-f]+' # Type4 should NOT look hex-ish now. + ]); + # Now let's delete the 'Speed' custom format. self.runCmd("type format delete Speed") @@ -85,6 +94,26 @@ class DataFormatterTestCase(TestBase): self.expect("type format delete Speed", error=True, substrs = ['no custom format for Speed']) + self.runCmd("type summary add -f \"arr = ${var%s}\" -x \"char \\[[0-9]+\\]\" -v") + + self.expect("frame variable strarr", + substrs = ['arr = "Hello world!\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0"']) + + self.runCmd("type summary clear") + + self.runCmd("type summary add -f \"ptr = ${var%s}\" \"char *\" -v") + + self.expect("frame variable strptr", + substrs = ['ptr = "Hello world!"']) + + self.runCmd("type summary add -f \"arr = ${var%s}\" -x \"char \\[[0-9]+\\]\" -v") + + self.expect("frame variable strarr", + substrs = ['arr = "Hello world!\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0"']) + + self.expect("frame variable strptr", + substrs = ['ptr = "Hello world!"']) + self.runCmd("type summary add -c Point") self.expect("frame variable iAmSomewhere", 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 20cdc2c900e..9224cb8a0a0 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp +++ b/lldb/test/functionalities/data-formatter/data-formatter-cpp/main.cpp @@ -107,6 +107,9 @@ int main (int argc, const char * argv[]) int int_array[] = {1,2,3,4,5}; IUseCharStar iEncapsulateCharStar; + + char strarr[32] = "Hello world!"; + char* strptr = "Hello world!"; return 0; // Set break point at this line. } |