diff options
-rw-r--r-- | lldb/include/lldb/Core/Debugger.h | 104 | ||||
-rw-r--r-- | lldb/include/lldb/Core/FormatManager.h | 136 | ||||
-rw-r--r-- | lldb/include/lldb/Core/RegularExpression.h | 3 | ||||
-rw-r--r-- | lldb/include/lldb/Core/ValueObject.h | 16 | ||||
-rw-r--r-- | lldb/include/lldb/lldb-enumerations.h | 1 | ||||
-rw-r--r-- | lldb/include/lldb/lldb-forward-rtti.h | 1 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectType.cpp | 205 | ||||
-rw-r--r-- | lldb/source/Core/Debugger.cpp | 338 | ||||
-rw-r--r-- | lldb/source/Core/FormatManager.cpp | 37 | ||||
-rw-r--r-- | lldb/source/Core/RegularExpression.cpp | 5 | ||||
-rw-r--r-- | lldb/source/Core/ValueObject.cpp | 143 | ||||
-rw-r--r-- | lldb/source/Core/ValueObjectDynamicValue.cpp | 2 | ||||
-rw-r--r-- | lldb/source/Interpreter/CommandObject.cpp | 34 | ||||
-rw-r--r-- | lldb/source/Symbol/ClangASTContext.cpp | 23 | ||||
-rw-r--r-- | lldb/test/functionalities/data-formatter/Makefile | 5 | ||||
-rw-r--r-- | lldb/test/functionalities/data-formatter/TestDataFormatter.py | 120 | ||||
-rw-r--r-- | lldb/test/functionalities/data-formatter/main.cpp | 70 |
17 files changed, 815 insertions, 428 deletions
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 021a18f766c..924fada5cc3 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -476,47 +476,81 @@ public: 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(FormatManager::ValueCallback callback, void* callback_baton); - - static uint32_t GetCurrentRevision(); + 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(); }; class SummaryFormats { - public: + 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 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(FormatManager::SummaryCallback callback, void* callback_baton); - - static uint32_t - GetCurrentRevision(); + 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(); }; + }; diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h index 01914537d85..00d749260bc 100644 --- a/lldb/include/lldb/Core/FormatManager.h +++ b/lldb/include/lldb/Core/FormatManager.h @@ -33,6 +33,10 @@ namespace std #include <stack> // Other libraries and framework includes +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" +#include "clang/AST/DeclObjC.h" + // Project includes #include "lldb/lldb-public.h" #include "lldb/lldb-enumerations.h" @@ -40,6 +44,7 @@ namespace std #include "lldb/Core/Communication.h" #include "lldb/Core/InputReaderStack.h" #include "lldb/Core/Listener.h" +#include "lldb/Core/RegularExpression.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/UserID.h" @@ -49,9 +54,6 @@ namespace std #include "lldb/Target/Platform.h" #include "lldb/Target/TargetList.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/Type.h" - namespace lldb_private { class IFormatChangeListener @@ -72,12 +74,22 @@ struct SummaryFormat bool m_dont_show_value; bool m_show_members_oneliner; bool m_cascades; - SummaryFormat(std::string f = "", bool c = false, bool nochildren = true, bool novalue = true, bool oneliner = false) : + bool m_skip_references; + bool m_skip_pointers; + SummaryFormat(std::string f = "", + bool c = false, + bool nochildren = true, + bool novalue = true, + bool oneliner = false, + bool skipptr = false, + bool skipref = false) : m_format(f), m_dont_show_children(nochildren), m_dont_show_value(novalue), m_show_members_oneliner(oneliner), - m_cascades(c) + m_cascades(c), + m_skip_references(skipref), + m_skip_pointers(skipptr) { } @@ -100,6 +112,8 @@ struct SummaryFormat } typedef lldb::SharedPtr<SummaryFormat>::Type SharedPointer; + typedef bool(*SummaryCallback)(void*, const char*, const SummaryFormat::SharedPointer&); + typedef bool(*RegexSummaryCallback)(void*, lldb::RegularExpressionSP, const SummaryFormat::SharedPointer&); }; @@ -107,13 +121,21 @@ struct ValueFormat { lldb::Format m_format; bool m_cascades; - ValueFormat (lldb::Format f = lldb::eFormatInvalid, bool c = false) : + bool m_skip_references; + bool m_skip_pointers; + ValueFormat (lldb::Format f = lldb::eFormatInvalid, + bool c = false, + bool skipptr = false, + bool skipref = false) : m_format (f), - m_cascades (c) + m_cascades (c), + m_skip_references(skipref), + m_skip_pointers(skipptr) { } typedef lldb::SharedPtr<ValueFormat>::Type SharedPointer; + typedef bool(*ValueCallback)(void*, const char*, const ValueFormat::SharedPointer&); ~ValueFormat() { @@ -160,7 +182,7 @@ public: } bool - Delete(const MapKeyType& type) + Delete(const char* type) { Mutex::Locker(m_map_mutex); MapIterator iter = m_map.find(type); @@ -197,6 +219,12 @@ public: } } + uint32_t + GetCount() + { + return m_map.size(); + } + ~FormatNavigator() { } @@ -210,7 +238,7 @@ private: DISALLOW_COPY_AND_ASSIGN(FormatNavigator); bool - Get(const MapKeyType &type, MapValueType& entry) + Get(const char* type, MapValueType& entry) { Mutex::Locker(m_map_mutex); MapIterator iter = m_map.find(type); @@ -228,16 +256,62 @@ private: return false; clang::QualType type = q_type.getUnqualifiedType(); type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict(); - ConstString name(type.getAsString().c_str()); + const clang::Type* typePtr = type.getTypePtrOrNull(); + if (!typePtr) + return false; + ConstString name(ClangASTType::GetTypeNameForQualType(type).c_str()); //printf("trying to get format for VO name %s of type %s\n",vobj.GetName().AsCString(),name.AsCString()); if (Get(name.GetCString(), entry)) return true; // look for a "base type", whatever that means - const clang::Type* typePtr = type.getTypePtrOrNull(); - if (!typePtr) - return false; if (typePtr->isReferenceType()) - return Get(vobj,type.getNonReferenceType(),entry); + { + if (Get(vobj,type.getNonReferenceType(),entry) && !entry->m_skip_references) + return true; + } + if (typePtr->isPointerType()) + { + if (Get(vobj, typePtr->getPointeeType(), entry) && !entry->m_skip_pointers) + return true; + } + if (typePtr->isObjCObjectPointerType()) + { + /* + for some reason, C++ can quite easily obtain the type hierarchy for a ValueObject + even if the VO represent a pointer-to-class, as long as the typePtr is right + Objective-C on the other hand cannot really complete an @interface when + the VO refers to a pointer-to-@interface + */ + Error error; + ValueObject* target = vobj.Dereference(error).get(); + if(error.Fail() || !target) + return false; + if (Get(*target, typePtr->getPointeeType(), entry) && !entry->m_skip_pointers) + return true; + } + const clang::ObjCObjectType *objc_class_type = typePtr->getAs<clang::ObjCObjectType>(); + if (objc_class_type) + { + //printf("working with ObjC\n"); + clang::ASTContext *ast = vobj.GetClangAST(); + if (ClangASTContext::GetCompleteType(ast, vobj.GetClangType()) && !objc_class_type->isObjCId()) + { + clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); + if(class_interface_decl) + { + //printf("down here\n"); + clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); + //printf("one further step and we're there...\n"); + if(superclass_interface_decl) + { + //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) + return true; + } + } + } + } // for C++ classes, navigate up the hierarchy if (typePtr->isRecordType()) { @@ -245,15 +319,7 @@ private: if (record) { if (!record->hasDefinition()) - // dummy call to do the complete - ClangASTContext::GetNumChildren(vobj.GetClangAST(), vobj.GetClangType(), false); - clang::IdentifierInfo *info = record->getIdentifier(); - if (info) { - // this is the class name, plain and simple - ConstString id_info(info->getName().str().c_str()); - if (Get(id_info.GetCString(), entry)) - return true; - } + ClangASTContext::GetCompleteType(vobj.GetClangAST(), vobj.GetClangType()); if (record->hasDefinition()) { clang::CXXRecordDecl::base_class_iterator pos,end; @@ -287,25 +353,34 @@ private: } }; - + +template<> +bool +FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Get(const char* key, + SummaryFormat::SharedPointer& value); + +template<> +bool +FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type); + class FormatManager : public IFormatChangeListener { public: - typedef bool(*ValueCallback)(void*, const char*, const ValueFormat::SharedPointer&); - typedef bool(*SummaryCallback)(void*, const char*, const SummaryFormat::SharedPointer&); - 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<ValueMap, ValueCallback> ValueNavigator; - typedef FormatNavigator<SummaryMap, SummaryCallback> SummaryNavigator; + typedef FormatNavigator<ValueMap, ValueFormat::ValueCallback> ValueNavigator; + typedef FormatNavigator<SummaryMap, SummaryFormat::SummaryCallback> SummaryNavigator; + typedef FormatNavigator<RegexSummaryMap, SummaryFormat::RegexSummaryCallback> RegexSummaryNavigator; ValueNavigator m_value_nav; SummaryNavigator m_summary_nav; + RegexSummaryNavigator m_regex_summary_nav; uint32_t m_last_revision; @@ -314,6 +389,7 @@ public: FormatManager() : m_value_nav(this), m_summary_nav(this), + m_regex_summary_nav(this), m_last_revision(0) { } @@ -321,6 +397,8 @@ public: ValueNavigator& Value() { return m_value_nav; } SummaryNavigator& Summary() { return m_summary_nav; } + RegexSummaryNavigator& RegexSummary() { return m_regex_summary_nav; } + static bool GetFormatFromCString (const char *format_cstr, diff --git a/lldb/include/lldb/Core/RegularExpression.h b/lldb/include/lldb/Core/RegularExpression.h index f71b24a5d62..3b2688feb41 100644 --- a/lldb/include/lldb/Core/RegularExpression.h +++ b/lldb/include/lldb/Core/RegularExpression.h @@ -149,6 +149,9 @@ public: //------------------------------------------------------------------ bool IsValid () const; + + bool + operator < (const RegularExpression& rhs) const; private: //------------------------------------------------------------------ diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 5242c1fb258..095066fa6b6 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -70,6 +70,13 @@ public: eDereferencePointers = 1, eHonorPointers, }; + + enum ValueObjectRepresentationStyle + { + eDisplayValue, + eDisplaySummary, + eDisplayLanguageSpecific + }; class EvaluationPoint { @@ -344,6 +351,10 @@ public: const char * GetObjectDescription (); + + const char * + GetPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display = eDisplaySummary, + lldb::Format custom_format = lldb::eFormatInvalid); bool GetValueIsValid () const; @@ -352,7 +363,7 @@ public: GetValueDidChange (); bool - UpdateValueIfNeeded (); + UpdateValueIfNeeded (bool update_format = true); void UpdateFormatsIfNeeded(); @@ -525,7 +536,8 @@ protected: m_children_count_valid:1, m_old_value_valid:1, m_pointers_point_to_load_addrs:1, - m_is_deref_of_parent:1; + m_is_deref_of_parent:1, + m_is_array_item_for_pointer:1; friend class ClangExpressionDeclMap; // For GetValue friend class ClangExpressionVariable; // For SetName diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index af331cb7f20..2c136b161f7 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -351,6 +351,7 @@ namespace lldb { eArgTypeExprFormat, eArgTypeFilename, eArgTypeFormat, + eArgTypeFormatString, eArgTypeFrameIndex, eArgTypeFullName, eArgTypeFunctionName, diff --git a/lldb/include/lldb/lldb-forward-rtti.h b/lldb/include/lldb/lldb-forward-rtti.h index 766407753b7..b5a02d6d9c0 100644 --- a/lldb/include/lldb/lldb-forward-rtti.h +++ b/lldb/include/lldb/lldb-forward-rtti.h @@ -53,6 +53,7 @@ namespace lldb { typedef SharedPtr<lldb_private::Platform>::Type PlatformSP; typedef SharedPtr<lldb_private::Process>::Type ProcessSP; typedef SharedPtr<lldb_private::RegisterContext>::Type RegisterContextSP; + typedef SharedPtr<lldb_private::RegularExpression>::Type RegularExpressionSP; typedef SharedPtr<lldb_private::Section>::Type SectionSP; typedef SharedPtr<lldb_private::SearchFilter>::Type SearchFilterSP; typedef SharedPtr<lldb_private::StackFrame>::Type StackFrameSP; diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 7d3b83b8cba..492b015935d 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -15,6 +15,7 @@ #include "lldb/Core/ConstString.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatManager.h" +#include "lldb/Core/RegularExpression.h" #include "lldb/Core/State.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/CommandObject.h" @@ -54,11 +55,14 @@ private: switch (short_option) { - case 'c': + case 'C': m_cascade = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat("Invalid value for cascade: %s.\n", option_arg); break; + case 'f': + error = Args::StringToFormat(option_arg, m_format, NULL); + break; default: error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); break; @@ -71,6 +75,7 @@ private: OptionParsingStarting () { m_cascade = true; + m_format = eFormatInvalid; } const OptionDefinition* @@ -86,6 +91,7 @@ private: // Instance variables to hold the values for command options. bool m_cascade; + lldb::Format m_format; }; CommandOptions m_options; @@ -103,21 +109,14 @@ public: "Add a new formatting style for a type.", NULL), m_options (interpreter) { - CommandArgumentEntry format_arg; - CommandArgumentData format_style_arg; CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; - format_style_arg.arg_type = eArgTypeFormat; - format_style_arg.arg_repetition = eArgRepeatPlain; - type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; - format_arg.push_back (format_style_arg); type_arg.push_back (type_style_arg); - - m_arguments.push_back (format_arg); + m_arguments.push_back (type_arg); } @@ -130,40 +129,27 @@ public: { const size_t argc = command.GetArgumentCount(); - if (argc < 2) + if (argc < 1) { - result.AppendErrorWithFormat ("%s takes two or more args.\n", m_cmd_name.c_str()); + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } - const char* format_cstr = command.GetArgumentAtIndex(0); - - if (!format_cstr || !format_cstr[0]) + if(m_options.m_format == eFormatInvalid) { - result.AppendError("empty format strings not allowed"); + result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } - lldb::Format format; - Error error; - - error = Args::StringToFormat(format_cstr, format, NULL); - ValueFormat::SharedPointer entry; + ValueFormatSP entry; - entry.reset(new ValueFormat(format,m_options.m_cascade)); + entry.reset(new ValueFormat(m_options.m_format,m_options.m_cascade)); - if (error.Fail()) - { - result.AppendError(error.AsCString()); - result.SetStatus(eReturnStatusFailed); - return false; - } - // now I have a valid format, let's add it to every type - for(int i = 1; i < argc; i++) { + for(int i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); if (typeCS) @@ -179,13 +165,13 @@ public: result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); } - }; OptionDefinition CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_ALL, false, "cascade", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."}, + { LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."}, + { LLDB_OPT_SET_ALL, false, "format", 'f', required_argument, NULL, 0, eArgTypeFormat, "The format to use to display this type."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; @@ -411,25 +397,31 @@ private: switch (short_option) { - case 'c': + case 'C': m_cascade = Args::StringToBoolean(option_arg, true, &success); if (!success) error.SetErrorStringWithFormat("Invalid value for cascade: %s.\n", option_arg); break; - case 'h': - m_no_children = !Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat("Invalid value for nochildren: %s.\n", option_arg); + case 'e': + m_no_children = false; break; case 'v': - m_no_value = !Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat("Invalid value for novalue: %s.\n", option_arg); + m_no_value = true; break; - case 'o': - m_one_liner = Args::StringToBoolean(option_arg, true, &success); - if (!success) - error.SetErrorStringWithFormat("Invalid value for oneliner: %s.\n", option_arg); + case 'c': + m_one_liner = true; + break; + case 'f': + m_format_string = std::string(option_arg); + break; + case 'p': + m_skip_pointers = true; + break; + case 'r': + m_skip_references = true; + break; + case 'x': + m_regex = true; break; default: error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); @@ -446,6 +438,9 @@ private: m_no_children = true; m_no_value = false; m_one_liner = false; + m_skip_references = false; + m_skip_pointers = false; + m_regex = false; } const OptionDefinition* @@ -464,6 +459,10 @@ private: bool m_no_children; bool m_no_value; bool m_one_liner; + bool m_skip_references; + bool m_skip_pointers; + bool m_regex; + std::string m_format_string; }; CommandOptions m_options; @@ -481,21 +480,14 @@ public: "Add a new summary style for a type.", NULL), m_options (interpreter) { - CommandArgumentEntry format_arg; - CommandArgumentData format_style_arg; CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; - format_style_arg.arg_type = eArgTypeFormat; - format_style_arg.arg_repetition = eArgRepeatPlain; - type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlus; - format_arg.push_back (format_style_arg); type_arg.push_back (type_style_arg); - m_arguments.push_back (format_arg); m_arguments.push_back (type_arg); } @@ -508,34 +500,29 @@ public: { const size_t argc = command.GetArgumentCount(); - // we support just one custom syntax: type summary add -o yes typeName - // anything else, must take the usual route - // e.g. type summary add -o yes "" type1 type2 ... typeN - - bool isValidShortcut = m_options.m_one_liner && (argc == 1); - bool isValid = (argc >= 2); - - if (!isValidShortcut && !isValid) + if (argc < 1) { - result.AppendErrorWithFormat ("%s takes two or more args.\n", m_cmd_name.c_str()); + result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str()); result.SetStatus(eReturnStatusFailed); return false; } - const char* format_cstr = (isValidShortcut ? "" : command.GetArgumentAtIndex(0)); - - if ( (!format_cstr || !format_cstr[0]) && !m_options.m_one_liner ) + if(!m_options.m_one_liner && m_options.m_format_string.empty()) { result.AppendError("empty summary strings not allowed"); result.SetStatus(eReturnStatusFailed); return false; } + const char* format_cstr = (m_options.m_one_liner ? "" : m_options.m_format_string.c_str()); + Error error; SummaryFormat::SharedPointer entry(new SummaryFormat(format_cstr,m_options.m_cascade, m_options.m_no_children,m_options.m_no_value, - m_options.m_one_liner)); + m_options.m_one_liner, + m_options.m_skip_pointers, + m_options.m_skip_references)); if (error.Fail()) { @@ -546,17 +533,31 @@ public: // now I have a valid format, let's add it to every type - for(int i = (isValidShortcut ? 0 : 1); i < argc; i++) { + for(int i = 0; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); - ConstString typeCS(typeA); - if (typeCS) - Debugger::SummaryFormats::Add(typeCS, entry); - else + if(!typeA || typeA[0] == '\0') { result.AppendError("empty typenames not allowed"); result.SetStatus(eReturnStatusFailed); return false; } + ConstString typeCS(typeA); + if(!m_options.m_regex) + { + Debugger::SummaryFormats::Add(typeCS, entry); + } + else + { + RegularExpressionSP typeRX(new RegularExpression()); + if(!typeRX->Compile(typeA)) + { + result.AppendError("regex format error (maybe this is not really a regex?)"); + result.SetStatus(eReturnStatusFailed); + return false; + } + Debugger::RegexSummaryFormats::Delete(typeCS); + Debugger::RegexSummaryFormats::Add(typeRX, entry); + } } result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); @@ -567,10 +568,14 @@ public: OptionDefinition CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_ALL, false, "cascade", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."}, - { LLDB_OPT_SET_ALL, false, "show-children", 'h', required_argument, NULL, 0, eArgTypeBoolean, "If true, print children."}, - { LLDB_OPT_SET_ALL, false, "show-value", 'v', required_argument, NULL, 0, eArgTypeBoolean, "If true, print value."}, - { LLDB_OPT_SET_ALL, false, "one-liner", 'o', required_argument, NULL, 0, eArgTypeBoolean, "If true, just print a one-line preformatted summary."}, + { 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."}, + { LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for references-to-type objects."}, + { LLDB_OPT_SET_ALL, false, "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, eArgTypeFormatString, "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."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; @@ -626,8 +631,9 @@ public: return false; } - - if (Debugger::SummaryFormats::Delete(typeCS)) + bool delete_summary = Debugger::SummaryFormats::Delete(typeCS); + bool delete_regex = Debugger::RegexSummaryFormats::Delete(typeCS); + if (delete_summary || delete_regex) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); @@ -666,6 +672,7 @@ public: Execute (Args& command, CommandReturnObject &result) { Debugger::SummaryFormats::Clear(); + Debugger::RegexSummaryFormats::Clear(); result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } @@ -677,6 +684,7 @@ public: //------------------------------------------------------------------------- bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry); +bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SummaryFormat::SharedPointer& entry); class CommandObjectTypeSummaryList; @@ -688,6 +696,14 @@ 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: @@ -718,6 +734,7 @@ public: const size_t argc = command.GetArgumentCount(); CommandObjectTypeSummaryList_LoopCallbackParam *param; + CommandObjectTypeRXSummaryList_LoopCallbackParam *rxparam; if (argc == 1) { RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); @@ -728,6 +745,24 @@ public: param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result); Debugger::SummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); delete param; + + 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.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); } @@ -742,18 +777,21 @@ private: { if (regex == NULL || regex->Execute(type)) { - result->GetOutputStream().Printf ("%s: `%s`%s%s%s%s\n", type, + result->GetOutputStream().Printf ("%s: `%s`%s%s%s%s%s%s\n", type, entry->m_format.c_str(), entry->m_cascades ? "" : " (not cascading)", entry->m_dont_show_children ? "" : " (show children)", - entry->m_dont_show_value ? "" : " (show value)", - entry->m_show_members_oneliner ? " (one-line printout)" : ""); + entry->m_dont_show_value ? " (hide value)" : "", + entry->m_show_members_oneliner ? " (one-line printout)" : "", + entry->m_skip_pointers ? " (skip pointers)" : "", + entry->m_skip_references ? " (skip references)" : ""); } return true; } friend bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry); - + friend bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SummaryFormat::SharedPointer& entry); + }; bool @@ -766,6 +804,15 @@ CommandObjectTypeSummaryList_LoopCallback ( return param->self->LoopCallback(type, entry, param->regex, param->result); } +bool +CommandObjectTypeRXSummaryList_LoopCallback ( + void* pt2self, + lldb::RegularExpressionSP regex, + const SummaryFormat::SharedPointer& entry) +{ + CommandObjectTypeRXSummaryList_LoopCallbackParam* param = (CommandObjectTypeRXSummaryList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result); +} @@ -796,7 +843,7 @@ class CommandObjectTypeSummary : public CommandObjectMultiword public: CommandObjectTypeSummary (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, - "type format", + "type summary", "A set of commands for editing variable summary display options", "type summary [<sub-command-options>] ") { diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index fadeafdf813..39ac87abc62 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -791,15 +791,201 @@ Debugger::FormatPrompt case '*': { if (!vobj) break; - var_name_begin++; lldb::clang_type_t pointer_clang_type = vobj->GetClangType(); clang_type_t elem_or_pointee_clang_type; const Flags type_flags (ClangASTContext::GetTypeInfo (pointer_clang_type, vobj->GetClangAST(), &elem_or_pointee_clang_type)); - if (type_flags.Test (ClangASTContext::eTypeIsPointer)) + bool is_pointer = type_flags.Test (ClangASTContext::eTypeIsPointer), + is_array = type_flags.Test (ClangASTContext::eTypeIsArray); + if ( is_array || + ( is_pointer && ::strchr(var_name_begin,'[') && ::strchr(var_name_begin,'[') < var_name_end ) + ) { - if (ClangASTContext::IsCharType (elem_or_pointee_clang_type)) + const char* var_name_final; + char* close_bracket_position = NULL; + const char* percent_position = NULL; + const char* targetvalue; + lldb::Format custom_format = eFormatInvalid; + int index_lower = -1; + int index_higher = -1; + ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eDisplaySummary; + { + percent_position = ::strchr(var_name_begin,'%'); + if(!percent_position || percent_position > var_name_end) + var_name_final = var_name_end; + else + { + var_name_final = percent_position; + char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0'; + memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1); + if ( !FormatManager::GetFormatFromCString(format_name, + true, + custom_format) ) + { + // if this is an @ sign, print ObjC description + if(*format_name == '@') + val_obj_display = ValueObject::eDisplayLanguageSpecific; + // if this is a V, print the value using the default format + if(*format_name == 'V') + val_obj_display = ValueObject::eDisplayValue; + } + // a good custom format tells us to print the value using it + else + val_obj_display = ValueObject::eDisplayValue; + } + } + + { + const char* open_bracket_position = ::strchr(var_name_begin,'['); + if(open_bracket_position && open_bracket_position < var_name_final) + { + // TODO: pick a way to say "all entries". this will make more sense once + // regex typenames are in place. now, you need to be size-aware anyways + char* separator_position = ::strchr(open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield + close_bracket_position = ::strchr(open_bracket_position,']'); + // as usual, we assume that [] will come before % + //printf("trying to expand a []\n"); + var_name_final = open_bracket_position; + if(close_bracket_position - open_bracket_position == 1) + { + if(is_array) + { + index_lower = 0; + index_higher = vobj->GetNumChildren() - 1; + } + else + break; // cannot auto-determine size for pointers + } + else if (separator_position == NULL || separator_position > var_name_end) + { + char *end = NULL; + index_lower = ::strtoul (open_bracket_position+1, &end, 0); + index_higher = index_lower; + //printf("got to read low=%d high same\n",bitfield_lower); + } + else if(close_bracket_position && close_bracket_position < var_name_end) + { + char *end = NULL; + index_lower = ::strtoul (open_bracket_position+1, &end, 0); + index_higher = ::strtoul (separator_position+1, &end, 0); + //printf("got to read low=%d high=%d\n",bitfield_lower,bitfield_higher); + } + else + break; + if (index_lower > index_higher) + { + int temp = index_lower; + index_lower = index_higher; + index_higher = temp; + } + //*((char*)open_bracket_position) = '\0'; + //printf("variable name is %s\n",var_name_begin); + //*((char*)open_bracket_position) = '['; + } + } + + // if you just type a range, lldb will do the "right thing" in picking + // a reasonable display for the array entries. you can override this by + // giving other input (e.g. ${*var[1-3].member1%uint8_t[]}) and they + // will be honored + char* special_directions = NULL; + if (close_bracket_position && (var_name_end-close_bracket_position > 1)) + { + int base_len = var_name_end-close_bracket_position; + special_directions = new char[8+base_len]; + special_directions[0] = '$'; + special_directions[1] = '{'; + special_directions[2] = 'v'; + special_directions[3] = 'a'; + special_directions[4] = 'r'; + memcpy(special_directions+5, close_bracket_position+1, base_len); + special_directions[base_len+7] = '\0'; + printf("%s\n",special_directions); + } + + // let us display items index_lower thru index_higher of this array + s.PutChar('['); + var_success = true; + const char* expr_path = NULL; + const char* ptr_deref_format = "%s[%d]"; + char* ptr_deref_buffer = new char[1024]; + StreamString expr_path_string; + + if(is_pointer) + { + vobj->GetExpressionPath(expr_path_string, true, ValueObject::eHonorPointers); + expr_path = expr_path_string.GetData(); + } + + for(;index_lower<=index_higher;index_lower++) + { + ValueObject* item; + + if(is_array) + item = vobj->GetChildAtIndex(index_lower, true).get(); + else + { +#ifdef VERBOSE_FORMATPROMPT_OUTPUT + printf("name to deref in phase 0: %s\n",expr_path); +#endif //VERBOSE_FORMATPROMPT_OUTPUT + ::sprintf(ptr_deref_buffer, ptr_deref_format, expr_path, index_lower); +#ifdef VERBOSE_FORMATPROMPT_OUTPUT + printf("name to deref in phase 1: %s\n",ptr_deref_buffer); +#endif //VERBOSE_FORMATPROMPT_OUTPUT + lldb::VariableSP var_sp; + Error error; + item = exe_ctx->frame->GetValueForVariableExpressionPath (ptr_deref_buffer, + eNoDynamicValues, + 0, + var_sp, + error).get(); + if (error.Fail()) + { +#ifdef VERBOSE_FORMATPROMPT_OUTPUT + printf("ERROR: %s\n",error.AsCString("unknown")); +#endif //VERBOSE_FORMATPROMPT_OUTPUT + break; + } + } + + if (!special_directions) + { + targetvalue = item->GetPrintableRepresentation(val_obj_display, custom_format); + if(targetvalue) + s.PutCString(targetvalue); + var_success &= (targetvalue != NULL); + if(custom_format != eFormatInvalid) + item->SetFormat(eFormatDefault); + } + else + { + var_success &= FormatPrompt(special_directions, sc, exe_ctx, addr, s, NULL, item); + } + + if(index_lower < index_higher) + s.PutChar(','); + } + s.PutChar(']'); + break; + + } + else if (is_pointer) + { + var_name_begin++; + uint32_t offset = 0; + DataExtractor read_for_null = vobj->GetDataExtractor(); + if (read_for_null.GetPointer(&offset) == 0) + break; + if (ClangASTContext::IsAggregateType (elem_or_pointee_clang_type) ) + { + Error error; + realvobj = vobj; + vobj = vobj->Dereference(error).get(); + if(!vobj || error.Fail()) + break; + } + else if (ClangASTContext::IsCharType (elem_or_pointee_clang_type)) { StreamString sstr; ExecutionContextScope *exe_scope = vobj->GetExecutionContextScope(); @@ -849,7 +1035,7 @@ Debugger::FormatPrompt break; } } - else /*if (ClangASTContext::IsAggregateType (elem_or_pointee_clang_type)) or this is some other pointer type*/ + else /*some other pointer type*/ { Error error; realvobj = vobj; @@ -864,7 +1050,7 @@ Debugger::FormatPrompt case 'v': { const char* targetvalue; - bool use_summary = false; + ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eDisplaySummary; ValueObject* target; lldb::Format custom_format = eFormatInvalid; int bitfield_lower = -1; @@ -872,12 +1058,16 @@ Debugger::FormatPrompt if (!vobj) break; // simplest case ${var}, just print vobj's value if (::strncmp (var_name_begin, "var}", strlen("var}")) == 0) + { target = vobj; + val_obj_display = ValueObject::eDisplayValue; + } else if (::strncmp(var_name_begin,"var%",strlen("var%")) == 0) { // this is a variable with some custom format applied to it const char* var_name_final; target = vobj; + val_obj_display = ValueObject::eDisplayValue; { const char* percent_position = ::strchr(var_name_begin,'%'); // TODO: make this a constant //if(!percent_position || percent_position > var_name_end) @@ -887,9 +1077,14 @@ Debugger::FormatPrompt var_name_final = percent_position; char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0'; memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1); - FormatManager::GetFormatFromCString(format_name, + if ( !FormatManager::GetFormatFromCString(format_name, true, - custom_format); // if this fails, custom_format is reset to invalid + custom_format) ) + { + // if this is an @ sign, print ObjC description + if(*format_name == '@') + val_obj_display = ValueObject::eDisplayLanguageSpecific; + } delete format_name; //} } @@ -899,7 +1094,7 @@ Debugger::FormatPrompt // this is a bitfield variable const char *var_name_final; target = vobj; - + val_obj_display = ValueObject::eDisplayValue; { const char* percent_position = ::strchr(var_name_begin,'%'); if(!percent_position || percent_position > var_name_end) @@ -909,10 +1104,15 @@ Debugger::FormatPrompt var_name_final = percent_position; char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0'; memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1); - FormatManager::GetFormatFromCString(format_name, - true, - custom_format); // if this fails, custom_format is reset to invalid - delete format_name; + if ( !FormatManager::GetFormatFromCString(format_name, + true, + custom_format) ) + { + delete format_name; + break; + } + else + delete format_name; } } @@ -943,7 +1143,11 @@ Debugger::FormatPrompt else break; if(bitfield_lower > bitfield_higher) - break; + { + int temp = bitfield_lower; + bitfield_lower = bitfield_higher; + bitfield_higher = temp; + } } } } @@ -966,10 +1170,20 @@ Debugger::FormatPrompt var_name_final = percent_position; char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0'; memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1); - FormatManager::GetFormatFromCString(format_name, - true, - custom_format); // if this fails, custom_format is reset to invalid - delete format_name; + if ( !FormatManager::GetFormatFromCString(format_name, + true, + custom_format) ) + { + // if this is an @ sign, print ObjC description + if(*format_name == '@') + val_obj_display = ValueObject::eDisplayLanguageSpecific; + // if this is a V, print the value using the default format + if(*format_name == 'V') + val_obj_display = ValueObject::eDisplayValue; + } + // a good custom format tells us to print the value using it + else + val_obj_display = ValueObject::eDisplayValue; } } @@ -999,7 +1213,11 @@ Debugger::FormatPrompt else break; if(bitfield_lower > bitfield_higher) - break; + { + int temp = bitfield_lower; + bitfield_lower = bitfield_higher; + bitfield_higher = temp; + } //*((char*)open_bracket_position) = '\0'; //printf("variable name is %s\n",var_name_begin); //*((char*)open_bracket_position) = '['; @@ -1010,9 +1228,13 @@ Debugger::FormatPrompt lldb::VariableSP var_sp; StreamString sstring; vobj->GetExpressionPath(sstring, true, ValueObject::eHonorPointers); - //printf("name to expand in phase 0: %s\n",sstring.GetData()); +#ifdef VERBOSE_FORMATPROMPT_OUTPUT + printf("name to expand in phase 0: %s\n",sstring.GetData()); +#endif //VERBOSE_FORMATPROMPT_OUTPUT sstring.PutRawBytes(var_name_begin+3, var_name_final-var_name_begin-3); - //printf("name to expand in phase 1: %s\n",sstring.GetData()); +#ifdef VERBOSE_FORMATPROMPT_OUTPUT + printf("name to expand in phase 1: %s\n",sstring.GetData()); +#endif //VERBOSE_FORMATPROMPT_OUTPUT std::string name = std::string(sstring.GetData()); target = exe_ctx->frame->GetValueForVariableExpressionPath (name.c_str(), eNoDynamicValues, @@ -1021,17 +1243,14 @@ Debugger::FormatPrompt error).get(); if (error.Fail()) { - //printf("ERROR: %s\n",error.AsCString("unknown")); +#ifdef VERBOSE_FORMATPROMPT_OUTPUT + printf("ERROR: %s\n",error.AsCString("unknown")); +#endif //VERBOSE_FORMATPROMPT_OUTPUT break; } } else break; - if(*(var_name_end+1)=='s') - { - use_summary = true; - var_name_end++; - } if (bitfield_lower >= 0) { //printf("trying to print a []\n"); @@ -1046,16 +1265,8 @@ Debugger::FormatPrompt } else { - //printf("here I come 1\n"); // format this as usual - if(custom_format != eFormatInvalid) - target->SetFormat(custom_format); - //printf("here I come 2\n"); - if(!use_summary) - targetvalue = target->GetValueAsCString(); - else - targetvalue = target->GetSummaryAsCString(); - //printf("here I come 3\n"); + targetvalue = target->GetPrintableRepresentation(val_obj_display, custom_format); if(targetvalue) s.PutCString(targetvalue); var_success = targetvalue; @@ -1634,7 +1845,7 @@ Debugger::ValueFormats::Clear() } void -Debugger::ValueFormats::LoopThrough(FormatManager::ValueCallback callback, void* callback_baton) +Debugger::ValueFormats::LoopThrough(ValueFormat::ValueCallback callback, void* callback_baton) { GetFormatManager().Value().LoopThrough(callback, callback_baton); } @@ -1645,6 +1856,11 @@ Debugger::ValueFormats::GetCurrentRevision() return GetFormatManager().GetCurrentRevision(); } +uint32_t +Debugger::ValueFormats::GetCount() +{ + return GetFormatManager().Value().GetCount(); +} bool Debugger::SummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry) @@ -1671,7 +1887,7 @@ Debugger::SummaryFormats::Clear() } void -Debugger::SummaryFormats::LoopThrough(FormatManager::SummaryCallback callback, void* callback_baton) +Debugger::SummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton) { GetFormatManager().Summary().LoopThrough(callback, callback_baton); } @@ -1682,6 +1898,54 @@ Debugger::SummaryFormats::GetCurrentRevision() return GetFormatManager().GetCurrentRevision(); } +uint32_t +Debugger::SummaryFormats::GetCount() +{ + return GetFormatManager().Summary().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(); +} + #pragma mark Debugger::SettingsController //-------------------------------------------------- diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp index 8f8a60d3b49..75d025f6dfe 100644 --- a/lldb/source/Core/FormatManager.cpp +++ b/lldb/source/Core/FormatManager.cpp @@ -151,3 +151,40 @@ FormatManager::GetFormatAsCString (Format format) return g_format_infos[format].format_name; return NULL; } + +template<> +bool +FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::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++) + { + lldb::RegularExpressionSP regex = pos->first; + if (regex->Execute(key)) + { + value = pos->second; + return true; + } + } + return false; +} + +template<> +bool +FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type) +{ + Mutex::Locker(m_map_mutex); + MapIterator pos, end = m_map.end(); + for (pos = m_map.begin(); pos != end; pos++) + { + lldb::RegularExpressionSP regex = pos->first; + if ( ::strcmp(type,regex->GetText()) == 0) + { + m_map.erase(pos); + return true; + } + } + return false; +} diff --git a/lldb/source/Core/RegularExpression.cpp b/lldb/source/Core/RegularExpression.cpp index 7848931e392..6fb94d98bb5 100644 --- a/lldb/source/Core/RegularExpression.cpp +++ b/lldb/source/Core/RegularExpression.cpp @@ -176,4 +176,9 @@ RegularExpression::GetErrorAsCString (char *err_str, size_t err_str_max_len) con return ::regerror (m_comp_err, &m_preg, err_str, err_str_max_len); } +bool +RegularExpression::operator < (const RegularExpression& rhs) const +{ + return (m_re < rhs.m_re); +} diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 5177600b0fe..a8696be3f7f 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -73,6 +73,7 @@ ValueObject::ValueObject (ValueObject &parent) : m_old_value_valid (false), m_pointers_point_to_load_addrs (false), m_is_deref_of_parent (false), + m_is_array_item_for_pointer(false), m_last_format_mgr_revision(0), m_last_summary_format(), m_last_value_format() @@ -108,6 +109,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) : m_old_value_valid (false), m_pointers_point_to_load_addrs (false), m_is_deref_of_parent (false), + m_is_array_item_for_pointer(false), m_last_format_mgr_revision(0), m_last_summary_format(), m_last_value_format() @@ -124,10 +126,11 @@ ValueObject::~ValueObject () } bool -ValueObject::UpdateValueIfNeeded () +ValueObject::UpdateValueIfNeeded (bool update_format) { - UpdateFormatsIfNeeded(); + if (update_format) + UpdateFormatsIfNeeded(); // If this is a constant value, then our success is predicated on whether // we have an error or not @@ -191,7 +194,8 @@ ValueObject::UpdateFormatsIfNeeded() if (m_last_value_format.get()) m_last_value_format.reset((ValueFormat*)NULL); Debugger::ValueFormats::Get(*this, m_last_value_format); - Debugger::SummaryFormats::Get(*this, m_last_summary_format); + if (!Debugger::SummaryFormats::Get(*this, m_last_summary_format)) + Debugger::RegexSummaryFormats::Get(*this, m_last_summary_format); m_last_format_mgr_revision = Debugger::ValueFormats::GetCurrentRevision(); m_value_str.clear(); m_summary_str.clear(); @@ -493,12 +497,47 @@ ValueObject::GetSummaryAsCString () ExecutionContext exe_ctx; this->GetExecutionContextScope()->CalculateExecutionContext(exe_ctx); SymbolContext sc = exe_ctx.frame->GetSymbolContext(eSymbolContextEverything); - if (Debugger::FormatPrompt(m_last_summary_format->m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, this)) + + if (m_last_summary_format->m_show_members_oneliner) { - m_summary_str.swap(s.GetString()); - return m_summary_str.c_str(); + const uint32_t num_children = GetNumChildren(); + if (num_children) + { + + s.PutChar('('); + + for (uint32_t idx=0; idx<num_children; ++idx) + { + ValueObjectSP child_sp(GetChildAtIndex(idx, true)); + if (child_sp.get()) + { + if (idx) + s.PutCString(", "); + s.PutCString(child_sp.get()->GetName().AsCString()); + s.PutChar('='); + s.PutCString(child_sp.get()->GetValueAsCString()); + } + } + + s.PutChar(')'); + + m_summary_str.swap(s.GetString()); + return m_summary_str.c_str(); + } + else + return "()"; + + } + else + { + if (Debugger::FormatPrompt(m_last_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(); + } + else + return NULL; } - return NULL; } clang_type_t clang_type = GetClangType(); @@ -655,12 +694,13 @@ ValueObject::GetSummaryAsCString () const char * ValueObject::GetObjectDescription () { - if (!m_object_desc_str.empty()) - return m_object_desc_str.c_str(); - + if (!UpdateValueIfNeeded ()) return NULL; - + + if (!m_object_desc_str.empty()) + return m_object_desc_str.c_str(); + ExecutionContextScope *exe_scope = GetExecutionContextScope(); if (exe_scope == NULL) return NULL; @@ -782,6 +822,37 @@ ValueObject::GetValueAsCString () return m_value_str.c_str(); } +const char * +ValueObject::GetPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display, + lldb::Format custom_format) +{ + if(custom_format != lldb::eFormatInvalid) + SetFormat(custom_format); + + const char * return_value; + + switch(val_obj_display) + { + case eDisplayValue: + return_value = GetValueAsCString(); + break; + case eDisplaySummary: + return_value = GetSummaryAsCString(); + break; + case eDisplayLanguageSpecific: + return_value = GetObjectDescription(); + break; + } + + + // try to use the value if the user's choice failed + if(!return_value && val_obj_display != eDisplayValue) + return_value = GetValueAsCString(); + + return return_value; + +} + addr_t ValueObject::GetAddressOf (AddressType &address_type, bool scalar_is_load_address) { @@ -1049,6 +1120,8 @@ ValueObject::GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create) { AddSyntheticChild(index_const_str, synthetic_child); synthetic_child_sp = synthetic_child->GetSP(); + synthetic_child_sp->SetName(index_str); + synthetic_child_sp->m_is_array_item_for_pointer = true; } } } @@ -1165,6 +1238,12 @@ ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExp if (parent) parent->GetExpressionPath (s, qualify_cxx_base_classes, epformat); + + // if we are a deref_of_parent just because we are synthetic array + // members made up to allow ptr[%d] syntax to work in variable + // printing, then add our name ([%d]) to the expression path + if(m_is_array_item_for_pointer && epformat == eHonorPointers) + s.PutCString(m_name.AsCString()); if (!IsBaseClass()) { @@ -1312,8 +1391,17 @@ ValueObject::DumpValueObject if (val_cstr && (!entry || entry->DoesPrintValue() || !sum_cstr)) s.Printf(" %s", valobj->GetValueAsCString()); - if (sum_cstr) - s.Printf(" %s", sum_cstr); + if(sum_cstr) + { + // for some reason, using %@ (ObjC description) in a summary string, makes + // us believe we need to reset ourselves, thus invalidating the content of + // sum_cstr. Thus, IF we had a valid sum_cstr before, but it is now empty + // let us recalculate it! + if (sum_cstr[0] == '\0') + s.Printf(" %s", valobj->GetSummaryAsCString()); + else + s.Printf(" %s", sum_cstr); + } if (use_objc) { @@ -1323,7 +1411,7 @@ ValueObject::DumpValueObject else s.Printf (" [no Objective-C description available]\n"); return; - } + } } if (curr_depth < max_depth) @@ -1360,32 +1448,7 @@ ValueObject::DumpValueObject print_children = false; } - if (entry && entry->IsOneliner()) - { - const uint32_t num_children = valobj->GetNumChildren(); - if (num_children) - { - - s.PutChar('('); - - for (uint32_t idx=0; idx<num_children; ++idx) - { - ValueObjectSP child_sp(valobj->GetChildAtIndex(idx, true)); - if (child_sp.get()) - { - if (idx) - s.PutCString(", "); - s.PutCString(child_sp.get()->GetName().AsCString()); - s.PutChar('='); - s.PutCString(child_sp.get()->GetValueAsCString()); - } - } - - s.PutChar(')'); - s.EOL(); - } - } - else if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr)) + if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr)) { const uint32_t num_children = valobj->GetNumChildren(); if (num_children) diff --git a/lldb/source/Core/ValueObjectDynamicValue.cpp b/lldb/source/Core/ValueObjectDynamicValue.cpp index 0a90b85bfdd..e0d94f7e22a 100644 --- a/lldb/source/Core/ValueObjectDynamicValue.cpp +++ b/lldb/source/Core/ValueObjectDynamicValue.cpp @@ -80,7 +80,7 @@ ValueObjectDynamicValue::CalculateNumChildren() clang::ASTContext * ValueObjectDynamicValue::GetClangAST () { - const bool success = UpdateValueIfNeeded(); + const bool success = UpdateValueIfNeeded(false); if (success && m_type_sp) return m_type_sp->GetClangAST(); else diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp index 76cee5af5db..241adca4ac3 100644 --- a/lldb/source/Interpreter/CommandObject.cpp +++ b/lldb/source/Interpreter/CommandObject.cpp @@ -627,6 +627,37 @@ BreakpointIDRangeHelpTextCallback () return "A 'breakpoint id list' is a manner of specifying multiple breakpoints. This can be done through several mechanisms. The easiest way is to just enter a space-separated list of breakpoint ids. To specify all the breakpoint locations under a major breakpoint, you can use the major breakpoint number followed by '.*', eg. '5.*' means all the locations under breakpoint 5. You can also indicate a range of breakpoints by using <start-bp-id> - <end-bp-id>. The start-bp-id and end-bp-id for a range can be any valid breakpoint ids. It is not legal, however, to specify a range using specific locations that cross major breakpoint numbers. I.e. 3.2 - 3.7 is legal; 2 - 5 is legal; but 3.2 - 4.4 is not legal."; } +static const char * +FormatHelpTextCallback () +{ + StreamString sstr; + sstr << "One of the format names (or one-character names) that can be used to show a variable's value:\n"; + for (Format f = eFormatDefault; f < kNumFormats; f = Format(f+1)) + { + char format_char = FormatManager::GetFormatAsFormatChar(f); + if (format_char) + sstr.Printf("'%c' or ", format_char); + + sstr.Printf ("\"%s\" ; ", FormatManager::GetFormatAsCString(f)); + } + + sstr.Flush(); + + std::string data = sstr.GetString(); + + char* help = new char[data.length()+1]; + + data.copy(help, data.length()); + + return help; +} + +static const char * +FormatStringHelpTextCallback() +{ + return "Ask me tomorrow"; +} + const char * CommandObject::GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type) { @@ -662,7 +693,8 @@ CommandObject::g_arguments_data[] = { eArgTypeExpression, "expr", CommandCompletions::eNoCompletion, NULL, "Help text goes here." }, { eArgTypeExprFormat, "expression-format", CommandCompletions::eNoCompletion, NULL, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]" }, { eArgTypeFilename, "filename", CommandCompletions::eDiskFileCompletion, NULL, "The name of a file (can include path)." }, - { eArgTypeFormat, "format", CommandCompletions::eNoCompletion, NULL, "Help text goes here." }, + { eArgTypeFormat, "format", CommandCompletions::eNoCompletion, FormatHelpTextCallback, NULL }, + { eArgTypeFormatString, "format-string", CommandCompletions::eNoCompletion, FormatStringHelpTextCallback, NULL }, { eArgTypeFrameIndex, "frame-index", CommandCompletions::eNoCompletion, NULL, "Index into a thread's list of frames." }, { eArgTypeFullName, "fullname", CommandCompletions::eNoCompletion, NULL, "Help text goes here." }, { eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, NULL, "The name of a function." }, diff --git a/lldb/source/Symbol/ClangASTContext.cpp b/lldb/source/Symbol/ClangASTContext.cpp index 9da3c703ac8..dd297191b48 100644 --- a/lldb/source/Symbol/ClangASTContext.cpp +++ b/lldb/source/Symbol/ClangASTContext.cpp @@ -117,21 +117,26 @@ GetCompleteQualType (clang::ASTContext *ast, clang::QualType qual_type) clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); // We currently can't complete objective C types through the newly added ASTContext // because it only supports TagDecl objects right now... - bool is_forward_decl = class_interface_decl->isForwardDecl(); - if (is_forward_decl && class_interface_decl->hasExternalLexicalStorage()) + if(class_interface_decl) { - if (ast) + bool is_forward_decl = class_interface_decl->isForwardDecl(); + if (is_forward_decl && class_interface_decl->hasExternalLexicalStorage()) { - ExternalASTSource *external_ast_source = ast->getExternalSource(); - if (external_ast_source) + if (ast) { - external_ast_source->CompleteType (class_interface_decl); - is_forward_decl = class_interface_decl->isForwardDecl(); + ExternalASTSource *external_ast_source = ast->getExternalSource(); + if (external_ast_source) + { + external_ast_source->CompleteType (class_interface_decl); + is_forward_decl = class_interface_decl->isForwardDecl(); + } } + return is_forward_decl == false; } - return is_forward_decl == false; + return true; } - return true; + else + return false; } } break; diff --git a/lldb/test/functionalities/data-formatter/Makefile b/lldb/test/functionalities/data-formatter/Makefile deleted file mode 100644 index 8a7102e347a..00000000000 --- a/lldb/test/functionalities/data-formatter/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -LEVEL = ../../make - -CXX_SOURCES := main.cpp - -include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/TestDataFormatter.py b/lldb/test/functionalities/data-formatter/TestDataFormatter.py deleted file mode 100644 index 48947f427d4..00000000000 --- a/lldb/test/functionalities/data-formatter/TestDataFormatter.py +++ /dev/null @@ -1,120 +0,0 @@ -""" -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") - - @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']) - - self.expect("frame variable", - substrs = ['(Speed) SPILookHex = 5.55' # Speed by default is 5.55. - ]); - - # 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 format add -c yes x Speed BitField") - self.runCmd("type format add -c no c RealNumber") - self.runCmd("type format add -c no x Type2") - self.runCmd("type format add -c yes c Type1") - - # The type format list should show our custom formats. - self.expect("type format list", - substrs = ['RealNumber', - 'Speed', - 'BitField', - 'Type1', - 'Type2']) - - self.expect("frame variable", - patterns = ['\(Speed\) SPILookHex = 0x[0-9a-f]+' # Speed should look hex-ish now. - ]); - - # Now let's delete the 'Speed' custom format. - self.runCmd("type format delete Speed") - - # The type format list should not show 'Speed' at this point. - self.expect("type format list", matching=False, - substrs = ['Speed']) - - # Delete type format for 'Speed', we should expect an error message. - self.expect("type format delete Speed", error=True, - substrs = ['no custom format for Speed']) - - # For some reason the type system is calling this "struct" - self.runCmd("type summary add -o yes \"\" Point") - - self.expect("frame variable iAmSomewhere", - substrs = ['x=4', - 'y=6']) - - self.expect("type summary list", - substrs = ['Point', - 'one-line']) - - self.runCmd("type summary add \"y=${var.y%x}\" Point") - - self.expect("frame variable iAmSomewhere", - substrs = ['y=0x']) - - self.runCmd("type summary add \"hello\" Point -h yes") - - self.expect("type summary list", - substrs = ['Point', - 'show children']) - - self.expect("frame variable iAmSomewhere", - substrs = ['hello', - 'x = 4', - '}']) - - -if __name__ == '__main__': - import atexit - lldb.SBDebugger.Initialize() - atexit.register(lambda: lldb.SBDebugger.Terminate()) - unittest2.main() diff --git a/lldb/test/functionalities/data-formatter/main.cpp b/lldb/test/functionalities/data-formatter/main.cpp deleted file mode 100644 index 94e2993ed90..00000000000 --- a/lldb/test/functionalities/data-formatter/main.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//===-- main.cpp ------------------------------------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> - -typedef float RealNumber; // should show as char -typedef RealNumber Temperature; // should show as float -typedef RealNumber Speed; // should show as hex - -typedef int Counter; // should show as int -typedef int BitField; // should show as hex - -typedef BitField SignalMask; // should show as hex -typedef BitField Modifiers; // should show as hex - -typedef Counter Accumulator; // should show as int - -typedef int Type1; // should show as char -typedef Type1 Type2; // should show as hex -typedef Type2 Type3; // should show as char -typedef Type3 Type4; // should show as char - -typedef int ChildType; // should show as int -typedef int AnotherChildType; // should show as int - -struct Point { - int x; - int y; - Point(int X = 3, int Y = 2) : x(X), y(Y) {} -}; - -int main (int argc, const char * argv[]) -{ - - int iAmInt = 1; - const float& IAmFloat = float(2.45); - - RealNumber RNILookChar = 3.14; - Temperature TMILookFloat = 4.97; - Speed SPILookHex = 5.55; - - Counter CTILookInt = 6; - BitField BFILookHex = 7; - SignalMask SMILookHex = 8; - Modifiers MFILookHex = 9; - - Accumulator* ACILookInt = new Accumulator(10); - - const Type1& T1ILookChar = 11; - Type2 T2ILookHex = 12; - Type3 T3ILookChar = 13; - Type4 T4ILookChar = 14; - - AnotherChildType AHILookInt = 15; - - Speed* SPPtrILookHex = new Speed(16); - - Point iAmSomewhere(4,6); - - return 0; // Set break point at this line. -} - |