diff options
-rw-r--r-- | lldb/include/lldb/Core/Debugger.h | 55 | ||||
-rw-r--r-- | lldb/include/lldb/Core/FormatManager.h | 303 | ||||
-rw-r--r-- | lldb/include/lldb/Core/ValueObject.h | 17 | ||||
-rw-r--r-- | lldb/include/lldb/Symbol/ClangASTType.h | 7 | ||||
-rw-r--r-- | lldb/include/lldb/lldb-forward-rtti.h | 2 | ||||
-rw-r--r-- | lldb/include/lldb/lldb-forward.h | 3 | ||||
-rw-r--r-- | lldb/lldb.xcodeproj/project.pbxproj | 2 | ||||
-rw-r--r-- | lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme | 4 | ||||
-rw-r--r-- | lldb/source/Commands/CommandObjectType.cpp | 552 | ||||
-rw-r--r-- | lldb/source/Core/Debugger.cpp | 358 | ||||
-rw-r--r-- | lldb/source/Core/FormatManager.cpp | 58 | ||||
-rw-r--r-- | lldb/source/Core/ValueObject.cpp | 159 | ||||
-rw-r--r-- | lldb/source/Symbol/ClangASTType.cpp | 36 | ||||
-rw-r--r-- | lldb/test/functionalities/data-formatter/TestDataFormatter.py | 34 | ||||
-rw-r--r-- | lldb/test/functionalities/data-formatter/main.cpp | 8 |
15 files changed, 1377 insertions, 221 deletions
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index ae69d3e5588..021a18f766c 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -415,7 +415,8 @@ public: const ExecutionContext *exe_ctx, const Address *addr, Stream &s, - const char **end); + const char **end, + ValueObject* vobj = NULL); void @@ -473,18 +474,50 @@ private: public: - static bool - GetFormatForType (const ConstString &type, lldb::Format& format, bool& cascade); - - static void - AddFormatForType (const ConstString &type, lldb::Format format, bool cascade); + 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(); + }; - static bool - DeleteFormatForType (const ConstString &type); + class SummaryFormats + { + public: + + static bool + Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry); + + static void + Add(const ConstString &type, const SummaryFormat::SharedPointer &entry); + + static bool + Delete(const ConstString &type); + + static void + Clear(); + + static void + LoopThrough(FormatManager::SummaryCallback callback, void* callback_baton); + + static uint32_t + GetCurrentRevision(); + }; - static void - LoopThroughFormatList (FormatManager::Callback callback, - void* callback_baton); }; } // namespace lldb_private diff --git a/lldb/include/lldb/Core/FormatManager.h b/lldb/include/lldb/Core/FormatManager.h index d99887bf8cd..01914537d85 100644 --- a/lldb/include/lldb/Core/FormatManager.h +++ b/lldb/include/lldb/Core/FormatManager.h @@ -40,42 +40,288 @@ namespace std #include "lldb/Core/Communication.h" #include "lldb/Core/InputReaderStack.h" #include "lldb/Core/Listener.h" -#include "lldb/Core/Stream.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/SourceManager.h" #include "lldb/Core/UserID.h" #include "lldb/Core/UserSettingsController.h" +#include "lldb/Core/ValueObject.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Platform.h" #include "lldb/Target/TargetList.h" -namespace lldb_private { +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" +namespace lldb_private { -class FormatManager +class IFormatChangeListener { public: + virtual void + Changed() = 0; + + virtual + ~IFormatChangeListener() {} + +}; - typedef bool(*Callback)(void*, const char*, lldb::Format, bool); +struct SummaryFormat +{ + std::string m_format; + bool m_dont_show_children; + 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) : + m_format(f), + m_dont_show_children(nochildren), + m_dont_show_value(novalue), + m_show_members_oneliner(oneliner), + m_cascades(c) + { + } + + bool + DoesPrintChildren() const + { + return !m_dont_show_children; + } + + bool + DoesPrintValue() const + { + return !m_dont_show_value; + } + + bool + IsOneliner() const + { + return m_show_members_oneliner; + } + + typedef lldb::SharedPtr<SummaryFormat>::Type SharedPointer; + +}; - FormatManager() : - m_format_map(FormatMap()), - m_format_map_mutex(Mutex::eMutexTypeRecursive) +struct ValueFormat +{ + lldb::Format m_format; + bool m_cascades; + ValueFormat (lldb::Format f = lldb::eFormatInvalid, bool c = false) : + m_format (f), + m_cascades (c) + { + } + + typedef lldb::SharedPtr<ValueFormat>::Type SharedPointer; + + ~ValueFormat() { } + +}; + +template<typename MapType, typename CallbackType> +class FormatNavigator +{ +public: + typedef typename MapType::iterator MapIterator; + typedef typename MapType::key_type MapKeyType; + typedef typename MapType::mapped_type MapValueType; + + FormatNavigator(IFormatChangeListener* lst = NULL) : + m_map_mutex(Mutex::eMutexTypeRecursive), + m_map(MapType()), + listener(lst) + { + } + bool - GetFormatForType (const ConstString &type, lldb::Format& format, bool& cascade); - + 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 - AddFormatForType (const ConstString &type, lldb::Format format, bool cascade); - + Add(const MapKeyType &type, const MapValueType& entry) + { + Mutex::Locker(m_map_mutex); + m_map[type] = MapValueType(entry); + if(listener) + listener->Changed(); + } + bool - DeleteFormatForType (const ConstString &type); - + Delete(const MapKeyType& type) + { + Mutex::Locker(m_map_mutex); + MapIterator iter = m_map.find(type); + if (iter == m_map.end()) + return false; + m_map.erase(type); + if(listener) + listener->Changed(); + return true; + } + + void + Clear() + { + Mutex::Locker(m_map_mutex); + m_map.clear(); + if(listener) + listener->Changed(); + } + void - LoopThroughFormatList (Callback cback, void* param); + LoopThrough(CallbackType callback, void* param) + { + if (callback) + { + Mutex::Locker(m_map_mutex); + 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))) + break; + } + } + } + + ~FormatNavigator() + { + } + +private: + + Mutex m_map_mutex; + MapType m_map; + IFormatChangeListener* listener; + + DISALLOW_COPY_AND_ASSIGN(FormatNavigator); + + bool + Get(const MapKeyType &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; + } + + bool Get(ValueObject& vobj, + const clang::QualType& q_type, + MapValueType& entry) + { + if (q_type.isNull()) + return false; + clang::QualType type = q_type.getUnqualifiedType(); + type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict(); + ConstString name(type.getAsString().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); + // for C++ classes, navigate up the hierarchy + if (typePtr->isRecordType()) + { + clang::CXXRecordDecl* record = typePtr->getAsCXXRecordDecl(); + 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; + } + if (record->hasDefinition()) + { + clang::CXXRecordDecl::base_class_iterator pos,end; + if( record->getNumBases() > 0) + { + end = record->bases_end(); + for (pos = record->bases_begin(); pos != end; pos++) + { + if((Get(vobj, pos->getType(), entry)) && entry->m_cascades) + return true; // if it does not cascade, just move on to other base classes which might + } + } + if (record->getNumVBases() > 0) + { + end = record->vbases_end(); + for (pos = record->vbases_begin(); pos != end; pos++) + { + if((Get(vobj, pos->getType(), entry)) && entry->m_cascades) + return true; + } + } + } + } + } + // 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; + } + +}; + +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 FormatNavigator<ValueMap, ValueCallback> ValueNavigator; + typedef FormatNavigator<SummaryMap, SummaryCallback> SummaryNavigator; + + ValueNavigator m_value_nav; + SummaryNavigator m_summary_nav; + + uint32_t m_last_revision; + +public: + + FormatManager() : + m_value_nav(this), + m_summary_nav(this), + m_last_revision(0) + { + } + + ValueNavigator& Value() { return m_value_nav; } + SummaryNavigator& Summary() { return m_summary_nav; } + static bool GetFormatFromCString (const char *format_cstr, bool partial_match_ok, @@ -86,24 +332,23 @@ public: static const char * GetFormatAsCString (lldb::Format format); - -private: - struct Entry + + void + Changed() { - lldb::Format format; - bool cascades; - Entry (lldb::Format f = lldb::eFormatInvalid, bool c = false) : - format (f), - cascades (c) - { - } - }; + __sync_add_and_fetch(&m_last_revision, +1); + } + + uint32_t + GetCurrentRevision() const + { + return m_last_revision; + } - typedef std::map<const char*, Entry> FormatMap; - typedef FormatMap::iterator FormatIterator; + ~FormatManager() + { + } - FormatMap m_format_map; - Mutex m_format_map_mutex; }; } // namespace lldb_private diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 82ed45cf675..5242c1fb258 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -64,6 +64,12 @@ namespace lldb_private { class ValueObject : public UserID { public: + + enum GetExpressionPathFormat + { + eDereferencePointers = 1, + eHonorPointers, + }; class EvaluationPoint { @@ -262,8 +268,8 @@ public: GetBaseClassPath (Stream &s); virtual void - GetExpressionPath (Stream &s, bool qualify_cxx_base_classes); - + GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat = eDereferencePointers); + virtual bool IsInScope () { @@ -347,6 +353,9 @@ public: bool UpdateValueIfNeeded (); + + void + UpdateFormatsIfNeeded(); DataExtractor & GetDataExtractor (); @@ -508,7 +517,9 @@ protected: ValueObject *m_deref_valobj; lldb::Format m_format; - lldb::Format m_last_format; + uint32_t m_last_format_mgr_revision; + lldb::SummaryFormatSP m_last_summary_format; + lldb::ValueFormatSP m_last_value_format; bool m_value_is_valid:1, m_value_did_change:1, m_children_count_valid:1, diff --git a/lldb/include/lldb/Symbol/ClangASTType.h b/lldb/include/lldb/Symbol/ClangASTType.h index e68a9dca873..cc75f6106cb 100644 --- a/lldb/include/lldb/Symbol/ClangASTType.h +++ b/lldb/include/lldb/Symbol/ClangASTType.h @@ -182,6 +182,13 @@ public: static lldb::Format GetFormat (lldb::clang_type_t opaque_clang_qual_type); + + uint32_t + GetTypeByteSize(); + + static uint32_t + GetTypeByteSize(clang::ASTContext *ast_context, + lldb::clang_type_t opaque_clang_qual_type); bool GetValueAsScalar (const DataExtractor &data, diff --git a/lldb/include/lldb/lldb-forward-rtti.h b/lldb/include/lldb/lldb-forward-rtti.h index abfe843e1f8..766407753b7 100644 --- a/lldb/include/lldb/lldb-forward-rtti.h +++ b/lldb/include/lldb/lldb-forward-rtti.h @@ -60,6 +60,7 @@ namespace lldb { typedef SharedPtr<lldb_private::StopInfo>::Type StopInfoSP; typedef SharedPtr<lldb_private::StoppointLocation>::Type StoppointLocationSP; typedef SharedPtr<lldb_private::Stream>::Type StreamSP; + typedef SharedPtr<lldb_private::SummaryFormat>::Type SummaryFormatSP; typedef SharedPtr<lldb_private::SymbolFile>::Type SymbolFileSP; typedef SharedPtr<lldb_private::SymbolContextSpecifier>::Type SymbolContextSpecifierSP; typedef SharedPtr<lldb_private::Target>::Type TargetSP; @@ -72,6 +73,7 @@ namespace lldb { typedef SharedPtr<lldb_private::UnwindPlan>::Type UnwindPlanSP; typedef SharedPtr<lldb_private::ValueObject>::Type ValueObjectSP; typedef SharedPtr<lldb_private::Value>::Type ValueSP; + typedef SharedPtr<lldb_private::ValueFormat>::Type ValueFormatSP; typedef SharedPtr<lldb_private::ValueList>::Type ValueListSP; typedef SharedPtr<lldb_private::Variable>::Type VariableSP; typedef SharedPtr<lldb_private::VariableList>::Type VariableListSP; diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index d05359126b5..4b3a97fc4d1 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -76,6 +76,7 @@ class ExecutionContextScope; class FileSpec; class FileSpecList; class Flags; +class FormatManager; class FuncUnwinders; class Function; class FunctionInfo; @@ -130,6 +131,7 @@ class Stream; class StreamFile; class StreamString; class StringList; +class SummaryFormat; class Symbol; class SymbolContext; class SymbolContextList; @@ -164,6 +166,7 @@ class UnwindTable; class UserSettingsController; class VMRange; class Value; +class ValueFormat; class ValueList; class ValueObject; class ValueObjectList; diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index 7abe7956546..e7fb8352217 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -924,7 +924,7 @@ 26BC7E9610F1B85900F91463 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Timer.cpp; path = source/Core/Timer.cpp; sourceTree = "<group>"; }; 26BC7E9810F1B85900F91463 /* UserID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UserID.cpp; path = source/Core/UserID.cpp; sourceTree = "<group>"; }; 26BC7E9910F1B85900F91463 /* Value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Value.cpp; path = source/Core/Value.cpp; sourceTree = "<group>"; }; - 26BC7E9A10F1B85900F91463 /* ValueObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObject.cpp; path = source/Core/ValueObject.cpp; sourceTree = "<group>"; }; + 26BC7E9A10F1B85900F91463 /* ValueObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; name = ValueObject.cpp; path = source/Core/ValueObject.cpp; sourceTree = "<group>"; }; 26BC7E9B10F1B85900F91463 /* ValueObjectChild.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectChild.cpp; path = source/Core/ValueObjectChild.cpp; sourceTree = "<group>"; }; 26BC7E9C10F1B85900F91463 /* ValueObjectList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectList.cpp; path = source/Core/ValueObjectList.cpp; sourceTree = "<group>"; }; 26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectVariable.cpp; path = source/Core/ValueObjectVariable.cpp; sourceTree = "<group>"; }; diff --git a/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme b/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme index c5d00df5d84..afb96900b7d 100644 --- a/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme +++ b/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <Scheme - version = "1.3"> + version = "1.8"> <BuildAction parallelizeBuildables = "NO" buildImplicitDependencies = "YES"> @@ -77,7 +77,7 @@ launchStyle = "0" useCustomWorkingDirectory = "NO" customWorkingDirectory = "/Volumes/work/gclayton/Documents/devb/attach" - buildConfiguration = "Release" + buildConfiguration = "Debug" ignoresPersistentStateOnLaunch = "YES" enablesOpenGLESFrameCapture = "YES"> <BuildableProductRunnable> diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 32172f21025..7d3b83b8cba 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -25,10 +25,10 @@ using namespace lldb; using namespace lldb_private; //------------------------------------------------------------------------- -// CommandObjectTypeAdd +// CommandObjectTypeFormatAdd //------------------------------------------------------------------------- -class CommandObjectTypeAdd : public CommandObject +class CommandObjectTypeFormatAdd : public CommandObject { private: @@ -97,7 +97,7 @@ private: } public: - CommandObjectTypeAdd (CommandInterpreter &interpreter) : + CommandObjectTypeFormatAdd (CommandInterpreter &interpreter) : CommandObject (interpreter, "type format add", "Add a new formatting style for a type.", @@ -121,7 +121,7 @@ public: m_arguments.push_back (type_arg); } - ~CommandObjectTypeAdd () + ~CommandObjectTypeFormatAdd () { } @@ -138,9 +138,22 @@ public: } const char* format_cstr = command.GetArgumentAtIndex(0); + + if (!format_cstr || !format_cstr[0]) + { + result.AppendError("empty format strings not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + lldb::Format format; - Error error = Args::StringToFormat(format_cstr, format, NULL); + Error error; + error = Args::StringToFormat(format_cstr, format, NULL); + ValueFormat::SharedPointer entry; + + entry.reset(new ValueFormat(format,m_options.m_cascade)); + if (error.Fail()) { result.AppendError(error.AsCString()); @@ -153,7 +166,14 @@ public: for(int i = 1; i < argc; i++) { const char* typeA = command.GetArgumentAtIndex(i); ConstString typeCS(typeA); - Debugger::AddFormatForType(typeCS, format, m_options.m_cascade); + if (typeCS) + Debugger::ValueFormats::Add(typeCS, entry); + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } } result.SetStatus(eReturnStatusSuccessFinishNoResult); @@ -163,7 +183,7 @@ public: }; OptionDefinition -CommandObjectTypeAdd::CommandOptions::g_option_table[] = +CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_ALL, false, "cascade", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } @@ -171,13 +191,13 @@ CommandObjectTypeAdd::CommandOptions::g_option_table[] = //------------------------------------------------------------------------- -// CommandObjectTypeDelete +// CommandObjectTypeFormatDelete //------------------------------------------------------------------------- -class CommandObjectTypeDelete : public CommandObject +class CommandObjectTypeFormatDelete : public CommandObject { public: - CommandObjectTypeDelete (CommandInterpreter &interpreter) : + CommandObjectTypeFormatDelete (CommandInterpreter &interpreter) : CommandObject (interpreter, "type format delete", "Delete an existing formatting style for a type.", @@ -185,17 +205,17 @@ public: { CommandArgumentEntry type_arg; CommandArgumentData type_style_arg; - + type_style_arg.arg_type = eArgTypeName; type_style_arg.arg_repetition = eArgRepeatPlain; type_arg.push_back (type_style_arg); m_arguments.push_back (type_arg); - + } - ~CommandObjectTypeDelete () + ~CommandObjectTypeFormatDelete () { } @@ -214,7 +234,15 @@ public: const char* typeA = command.GetArgumentAtIndex(0); ConstString typeCS(typeA); - if (Debugger::DeleteFormatForType(typeCS)) + if(!typeCS) + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + + if (Debugger::ValueFormats::Delete(typeCS)) { result.SetStatus(eReturnStatusSuccessFinishNoResult); return result.Succeeded(); @@ -225,31 +253,60 @@ public: result.SetStatus(eReturnStatusFailed); return false; } + + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeFormatClear +//------------------------------------------------------------------------- +class CommandObjectTypeFormatClear : public CommandObject +{ +public: + CommandObjectTypeFormatClear (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type format clear", + "Delete all existing format styles.", + NULL) + { + } + + ~CommandObjectTypeFormatClear () + { + } + + bool + Execute (Args& command, CommandReturnObject &result) + { + Debugger::ValueFormats::Clear(); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); } }; //------------------------------------------------------------------------- -// CommandObjectTypeList +// CommandObjectTypeFormatList //------------------------------------------------------------------------- -bool CommandObjectTypeList_LoopCallback(void* pt2self, const char* type, lldb::Format format, bool cascade); +bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, const char* type, const ValueFormat::SharedPointer& entry); -class CommandObjectTypeList; +class CommandObjectTypeFormatList; -struct CommandObjectTypeList_LoopCallbackParam { - CommandObjectTypeList* self; +struct CommandObjectTypeFormatList_LoopCallbackParam { + CommandObjectTypeFormatList* self; CommandReturnObject* result; RegularExpression* regex; - CommandObjectTypeList_LoopCallbackParam(CommandObjectTypeList* S, CommandReturnObject* R, + CommandObjectTypeFormatList_LoopCallbackParam(CommandObjectTypeFormatList* S, CommandReturnObject* R, RegularExpression* X = NULL) : self(S), result(R), regex(X) {} }; -class CommandObjectTypeList : public CommandObject +class CommandObjectTypeFormatList : public CommandObject { public: - CommandObjectTypeList (CommandInterpreter &interpreter) : + CommandObjectTypeFormatList (CommandInterpreter &interpreter) : CommandObject (interpreter, "type format list", "Show a list of current formatting styles.", @@ -266,7 +323,7 @@ public: m_arguments.push_back (type_arg); } - ~CommandObjectTypeList () + ~CommandObjectTypeFormatList () { } @@ -275,16 +332,16 @@ public: { const size_t argc = command.GetArgumentCount(); - CommandObjectTypeList_LoopCallbackParam *param; + CommandObjectTypeFormatList_LoopCallbackParam *param; if (argc == 1) { RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0)); regex->Compile(command.GetArgumentAtIndex(0)); - param = new CommandObjectTypeList_LoopCallbackParam(this,&result,regex); + param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result,regex); } else - param = new CommandObjectTypeList_LoopCallbackParam(this,&result); - Debugger::LoopThroughFormatList(CommandObjectTypeList_LoopCallback, param); + param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result); + Debugger::ValueFormats::LoopThrough(CommandObjectTypeFormatList_LoopCallback, param); delete param; result.SetStatus(eReturnStatusSuccessFinishResult); return result.Succeeded(); @@ -294,45 +351,438 @@ private: bool LoopCallback (const char* type, - lldb::Format format, - bool cascade, + const ValueFormat::SharedPointer& entry, RegularExpression* regex, CommandReturnObject *result) { if (regex == NULL || regex->Execute(type)) { - result->GetOutputStream().Printf ("%s: %s\n", type, FormatManager::GetFormatAsCString (format)); + result->GetOutputStream().Printf ("%s: %s%s\n", type, + FormatManager::GetFormatAsCString (entry->m_format), + entry->m_cascades ? "" : " (not cascading)"); } return true; } - friend bool CommandObjectTypeList_LoopCallback(void* pt2self, const char* type, lldb::Format format, bool cascade); + friend bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, const char* type, const ValueFormat::SharedPointer& entry); }; bool -CommandObjectTypeList_LoopCallback ( +CommandObjectTypeFormatList_LoopCallback ( void* pt2self, const char* type, - lldb::Format format, - bool cascade) + const ValueFormat::SharedPointer& entry) { - CommandObjectTypeList_LoopCallbackParam* param = (CommandObjectTypeList_LoopCallbackParam*)pt2self; - return param->self->LoopCallback(type, format, cascade, param->regex, param->result); + CommandObjectTypeFormatList_LoopCallbackParam* param = (CommandObjectTypeFormatList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type, entry, param->regex, param->result); } + + + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryAdd +//------------------------------------------------------------------------- + +class CommandObjectTypeSummaryAdd : public CommandObject +{ + +private: + + class CommandOptions : public Options + { + public: + + CommandOptions (CommandInterpreter &interpreter) : + Options (interpreter) + { + } + + virtual + ~CommandOptions (){} + + virtual Error + SetOptionValue (uint32_t option_idx, const char *option_arg) + { + Error error; + char short_option = (char) m_getopt_table[option_idx].val; + bool success; + + switch (short_option) + { + case 'c': + m_cascade = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid value for cascade: %s.\n", option_arg); + break; + case 'h': + m_no_children = !Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid value for nochildren: %s.\n", option_arg); + break; + case 'v': + m_no_value = !Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid value for novalue: %s.\n", option_arg); + break; + case 'o': + m_one_liner = Args::StringToBoolean(option_arg, true, &success); + if (!success) + error.SetErrorStringWithFormat("Invalid value for oneliner: %s.\n", option_arg); + break; + default: + error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option); + break; + } + + return error; + } + + void + OptionParsingStarting () + { + m_cascade = true; + m_no_children = true; + m_no_value = false; + m_one_liner = false; + } + + const OptionDefinition* + GetDefinitions () + { + return g_option_table; + } + + // Options table: Required for subclasses of Options. + + static OptionDefinition g_option_table[]; + + // Instance variables to hold the values for command options. + + bool m_cascade; + bool m_no_children; + bool m_no_value; + bool m_one_liner; + }; + + CommandOptions m_options; + + virtual Options * + GetOptions () + { + return &m_options; + } + +public: + CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type summary add", + "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); + } + + ~CommandObjectTypeSummaryAdd () + { + } + + bool + Execute (Args& command, CommandReturnObject &result) + { + 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) + { + result.AppendErrorWithFormat ("%s takes two 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 ) + { + result.AppendError("empty summary strings not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + 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)); + + 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 = (isValidShortcut ? 0 : 1); i < argc; i++) { + const char* typeA = command.GetArgumentAtIndex(i); + ConstString typeCS(typeA); + if (typeCS) + Debugger::SummaryFormats::Add(typeCS, entry); + else + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + } + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + +}; + +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."}, + { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } +}; + + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryDelete +//------------------------------------------------------------------------- + +class CommandObjectTypeSummaryDelete : public CommandObject +{ +public: + CommandObjectTypeSummaryDelete (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type summary delete", + "Delete an existing summary style for a type.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatPlain; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + + } + + ~CommandObjectTypeSummaryDelete () + { + } + + bool + Execute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + if (argc != 1) + { + result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str()); + result.SetStatus(eReturnStatusFailed); + return false; + } + + const char* typeA = command.GetArgumentAtIndex(0); + ConstString typeCS(typeA); + + if(!typeCS) + { + result.AppendError("empty typenames not allowed"); + result.SetStatus(eReturnStatusFailed); + return false; + } + + + if (Debugger::SummaryFormats::Delete(typeCS)) + { + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } + else + { + result.AppendErrorWithFormat ("no custom summary for %s.\n", typeA); + result.SetStatus(eReturnStatusFailed); + return false; + } + + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryClear +//------------------------------------------------------------------------- + +class CommandObjectTypeSummaryClear : public CommandObject +{ +public: + CommandObjectTypeSummaryClear (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type summary clear", + "Delete all existing summary styles.", + NULL) + { + } + + ~CommandObjectTypeSummaryClear () + { + } + + bool + Execute (Args& command, CommandReturnObject &result) + { + Debugger::SummaryFormats::Clear(); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +}; + +//------------------------------------------------------------------------- +// CommandObjectTypeSummaryList +//------------------------------------------------------------------------- + +bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry); + +class CommandObjectTypeSummaryList; + +struct CommandObjectTypeSummaryList_LoopCallbackParam { + CommandObjectTypeSummaryList* self; + CommandReturnObject* result; + RegularExpression* regex; + CommandObjectTypeSummaryList_LoopCallbackParam(CommandObjectTypeSummaryList* S, CommandReturnObject* R, + RegularExpression* X = NULL) : self(S), result(R), regex(X) {} +}; + +class CommandObjectTypeSummaryList : public CommandObject +{ +public: + CommandObjectTypeSummaryList (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "type summary list", + "Show a list of current summary styles.", + NULL) + { + CommandArgumentEntry type_arg; + CommandArgumentData type_style_arg; + + type_style_arg.arg_type = eArgTypeName; + type_style_arg.arg_repetition = eArgRepeatOptional; + + type_arg.push_back (type_style_arg); + + m_arguments.push_back (type_arg); + } + + ~CommandObjectTypeSummaryList () + { + } + + bool + Execute (Args& command, CommandReturnObject &result) + { + const size_t argc = command.GetArgumentCount(); + + CommandObjectTypeSummaryList_LoopCallbackParam *param; + + 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::SummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param); + delete param; + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } + +private: + + bool + LoopCallback (const char* type, + const SummaryFormat::SharedPointer& entry, + RegularExpression* regex, + CommandReturnObject *result) + { + if (regex == NULL || regex->Execute(type)) + { + result->GetOutputStream().Printf ("%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)" : ""); + } + return true; + } + + friend bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry); + +}; + +bool +CommandObjectTypeSummaryList_LoopCallback ( + void* pt2self, + const char* type, + const SummaryFormat::SharedPointer& entry) +{ + CommandObjectTypeSummaryList_LoopCallbackParam* param = (CommandObjectTypeSummaryList_LoopCallbackParam*)pt2self; + return param->self->LoopCallback(type, entry, param->regex, param->result); +} + + + + + class CommandObjectTypeFormat : public CommandObjectMultiword { public: CommandObjectTypeFormat (CommandInterpreter &interpreter) : CommandObjectMultiword (interpreter, "type format", - "A set of commands for editing variable display options", + "A set of commands for editing variable value display options", "type format [<sub-command-options>] ") { - LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeAdd (interpreter))); - LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeDelete (interpreter))); - LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeList (interpreter))); + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFormatAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFormatClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFormatDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFormatList (interpreter))); } @@ -341,6 +791,27 @@ public: } }; +class CommandObjectTypeSummary : public CommandObjectMultiword +{ +public: + CommandObjectTypeSummary (CommandInterpreter &interpreter) : + CommandObjectMultiword (interpreter, + "type format", + "A set of commands for editing variable summary display options", + "type summary [<sub-command-options>] ") + { + LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSummaryAdd (interpreter))); + LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSummaryClear (interpreter))); + LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSummaryDelete (interpreter))); + LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSummaryList (interpreter))); + } + + + ~CommandObjectTypeSummary () + { + } +}; + //------------------------------------------------------------------------- // CommandObjectType //------------------------------------------------------------------------- @@ -352,6 +823,7 @@ CommandObjectType::CommandObjectType (CommandInterpreter &interpreter) : "type [<sub-command-options>]") { LoadSubCommand ("format", CommandObjectSP (new CommandObjectTypeFormat (interpreter))); + LoadSubCommand ("summary", CommandObjectSP (new CommandObjectTypeSummary (interpreter))); } diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index 7ab7a7728d7..fadeafdf813 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -11,6 +11,9 @@ #include <map> +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" + #include "lldb/lldb-private.h" #include "lldb/Core/ConnectionFileDescriptor.h" #include "lldb/Core/FormatManager.h" @@ -20,6 +23,7 @@ #include "lldb/Core/StreamAsynchronousIO.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" +#include "lldb/Core/ValueObject.h" #include "lldb/Host/Terminal.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Target/TargetList.h" @@ -698,13 +702,20 @@ Debugger::FormatPrompt const ExecutionContext *exe_ctx, const Address *addr, Stream &s, - const char **end + const char **end, + ValueObject* vobj ) { + ValueObject* realvobj = NULL; // makes it super-easy to parse pointers bool success = true; const char *p; for (p = format; *p != '\0'; ++p) { + if(realvobj) + { + vobj = realvobj; + realvobj = NULL; + } size_t non_special_chars = ::strcspn (p, "${}\\"); if (non_special_chars > 0) { @@ -732,7 +743,7 @@ Debugger::FormatPrompt ++p; // Skip the '{' - if (FormatPrompt (p, sc, exe_ctx, addr, sub_strm, &p)) + if (FormatPrompt (p, sc, exe_ctx, addr, sub_strm, &p, vobj)) { // The stream had all it needed s.Write(sub_strm.GetData(), sub_strm.GetSize()); @@ -777,6 +788,284 @@ Debugger::FormatPrompt bool var_success = false; switch (var_name_begin[0]) { + 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)) + { + if (ClangASTContext::IsCharType (elem_or_pointee_clang_type)) + { + StreamString sstr; + ExecutionContextScope *exe_scope = vobj->GetExecutionContextScope(); + Process *process = exe_scope->CalculateProcess(); + if(!process) break; + lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS; + AddressType cstr_address_type = eAddressTypeInvalid; + DataExtractor data; + size_t bytes_read = 0; + std::vector<char> data_buffer; + Error error; + cstr_address = vobj->GetPointerValue (cstr_address_type, true); + { + 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'; + + sstr << '"'; + + data.SetData (&data_buffer.front(), data_buffer.size(), endian::InlHostByteOrder()); + while ((bytes_read = process->ReadMemory (cstr_address, &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 (&sstr, + 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_address += k_max_buf_size; + } + sstr << '"'; + s.PutCString(sstr.GetData()); + var_success = true; + break; + } + } + else /*if (ClangASTContext::IsAggregateType (elem_or_pointee_clang_type)) or this is some other pointer type*/ + { + Error error; + realvobj = vobj; + vobj = vobj->Dereference(error).get(); + if(!vobj || error.Fail()) + break; + } + } + else + break; + } + case 'v': + { + const char* targetvalue; + bool use_summary = false; + ValueObject* target; + lldb::Format custom_format = eFormatInvalid; + int bitfield_lower = -1; + int bitfield_higher = -1; + if (!vobj) break; + // simplest case ${var}, just print vobj's value + if (::strncmp (var_name_begin, "var}", strlen("var}")) == 0) + target = vobj; + 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; + { + const char* percent_position = ::strchr(var_name_begin,'%'); // TODO: make this a constant + //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); + FormatManager::GetFormatFromCString(format_name, + true, + custom_format); // if this fails, custom_format is reset to invalid + delete format_name; + //} + } + } + else if (::strncmp(var_name_begin,"var[",strlen("var[")) == 0) + { + // this is a bitfield variable + const char *var_name_final; + target = vobj; + + { + const char* 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); + FormatManager::GetFormatFromCString(format_name, + true, + custom_format); // if this fails, custom_format is reset to invalid + delete format_name; + } + } + + { + // code here might be simpler than in the case below + const char* open_bracket_position = ::strchr(var_name_begin,'['); + if(open_bracket_position && open_bracket_position < var_name_final) + { + char* separator_position = ::strchr(open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield + char* 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 (separator_position == NULL || separator_position > var_name_end) + { + char *end = NULL; + bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0); + bitfield_higher = bitfield_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; + bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0); + bitfield_higher = ::strtoul (separator_position+1, &end, 0); + //printf("got to read low=%d high=%d\n",bitfield_lower,bitfield_higher); + } + else + break; + if(bitfield_lower > bitfield_higher) + break; + } + } + } + // this is ${var.something} or multiple .something nested + else if (::strncmp (var_name_begin, "var", strlen("var")) == 0) + { + // check for custom format string + + // we need this because we might have ${var.something%format}. in this case var_name_end + // still points to the closing }, but we must extract the variable name only up to + // before the %. var_name_final will point to that % sign position + const char* var_name_final; + + { + const char* 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); + FormatManager::GetFormatFromCString(format_name, + true, + custom_format); // if this fails, custom_format is reset to invalid + delete format_name; + } + } + + { + const char* open_bracket_position = ::strchr(var_name_begin,'['); + if(open_bracket_position && open_bracket_position < var_name_final) + { + char* separator_position = ::strchr(open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield + char* 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 (separator_position == NULL || separator_position > var_name_end) + { + char *end = NULL; + bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0); + bitfield_higher = bitfield_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; + bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0); + bitfield_higher = ::strtoul (separator_position+1, &end, 0); + //printf("got to read low=%d high=%d\n",bitfield_lower,bitfield_higher); + } + else + break; + if(bitfield_lower > bitfield_higher) + break; + //*((char*)open_bracket_position) = '\0'; + //printf("variable name is %s\n",var_name_begin); + //*((char*)open_bracket_position) = '['; + } + } + + Error error; + lldb::VariableSP var_sp; + StreamString sstring; + vobj->GetExpressionPath(sstring, true, ValueObject::eHonorPointers); + //printf("name to expand in phase 0: %s\n",sstring.GetData()); + sstring.PutRawBytes(var_name_begin+3, var_name_final-var_name_begin-3); + //printf("name to expand in phase 1: %s\n",sstring.GetData()); + std::string name = std::string(sstring.GetData()); + target = exe_ctx->frame->GetValueForVariableExpressionPath (name.c_str(), + eNoDynamicValues, + 0, + var_sp, + error).get(); + if (error.Fail()) + { + //printf("ERROR: %s\n",error.AsCString("unknown")); + 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"); + // format this as a bitfield + DataExtractor extractor = target->GetDataExtractor(); + uint32_t item_byte_size = ClangASTType::GetTypeByteSize(target->GetClangAST(), target->GetClangType()); + if(custom_format == eFormatInvalid) + custom_format = eFormatHex; + var_success = + extractor.Dump(&s, 0, custom_format, item_byte_size, 1, 1, LLDB_INVALID_ADDRESS, bitfield_higher-bitfield_lower+1, bitfield_lower) > 0; + //printf("var_success = %s\n",var_success ? "true" : "false"); + } + 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"); + if(targetvalue) + s.PutCString(targetvalue); + var_success = targetvalue; + //printf("here I come 4 : %s\n",var_success ? "good" : "bad"); + if(custom_format != eFormatInvalid) + target->SetFormat(eFormatDefault); + //printf("here I come 5\n"); + } + break; + } case 'a': if (::strncmp (var_name_begin, "addr}", strlen("addr}")) == 0) { @@ -1321,27 +1610,76 @@ GetFormatManager() { } bool -Debugger::GetFormatForType (const ConstString &type, lldb::Format& format, bool& cascade) +Debugger::ValueFormats::Get(ValueObject& vobj, ValueFormat::SharedPointer &entry) +{ + return GetFormatManager().Value().Get(vobj,entry); +} + +void +Debugger::ValueFormats::Add(const ConstString &type, const ValueFormat::SharedPointer &entry) +{ + GetFormatManager().Value().Add(type.AsCString(),entry); +} + +bool +Debugger::ValueFormats::Delete(const ConstString &type) +{ + return GetFormatManager().Value().Delete(type.AsCString()); +} + +void +Debugger::ValueFormats::Clear() +{ + GetFormatManager().Value().Clear(); +} + +void +Debugger::ValueFormats::LoopThrough(FormatManager::ValueCallback callback, void* callback_baton) +{ + GetFormatManager().Value().LoopThrough(callback, callback_baton); +} + +uint32_t +Debugger::ValueFormats::GetCurrentRevision() +{ + return GetFormatManager().GetCurrentRevision(); +} + + +bool +Debugger::SummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry) { - return GetFormatManager().GetFormatForType(type, format, cascade); + return GetFormatManager().Summary().Get(vobj,entry); } void -Debugger::AddFormatForType (const ConstString &type, lldb::Format format, bool cascade) +Debugger::SummaryFormats::Add(const ConstString &type, const SummaryFormat::SharedPointer &entry) { - GetFormatManager().AddFormatForType(type,format, cascade); + GetFormatManager().Summary().Add(type.AsCString(),entry); } bool -Debugger::DeleteFormatForType (const ConstString &type) +Debugger::SummaryFormats::Delete(const ConstString &type) +{ + return GetFormatManager().Summary().Delete(type.AsCString()); +} + +void +Debugger::SummaryFormats::Clear() { - return GetFormatManager().DeleteFormatForType(type); + GetFormatManager().Summary().Clear(); } void -Debugger::LoopThroughFormatList (FormatManager::Callback callback, void* callback_baton) +Debugger::SummaryFormats::LoopThrough(FormatManager::SummaryCallback callback, void* callback_baton) +{ + GetFormatManager().Summary().LoopThrough(callback, callback_baton); +} + +uint32_t +Debugger::SummaryFormats::GetCurrentRevision() { - return GetFormatManager().LoopThroughFormatList(callback, callback_baton); + return GetFormatManager().GetCurrentRevision(); } #pragma mark Debugger::SettingsController diff --git a/lldb/source/Core/FormatManager.cpp b/lldb/source/Core/FormatManager.cpp index 72503842387..8f8a60d3b49 100644 --- a/lldb/source/Core/FormatManager.cpp +++ b/lldb/source/Core/FormatManager.cpp @@ -151,61 +151,3 @@ FormatManager::GetFormatAsCString (Format format) return g_format_infos[format].format_name; return NULL; } - -bool -FormatManager::GetFormatForType (const ConstString &type_name, lldb::Format& format, bool& cascade) -{ - Mutex::Locker locker (m_format_map_mutex); - FormatMap& fmtmap = m_format_map; - FormatMap::iterator iter = fmtmap.find(type_name.GetCString()); - if(iter == fmtmap.end()) - return false; - else { - format = iter->second.format; - cascade = iter->second.cascades; - return true; - } -} - -void -FormatManager::AddFormatForType (const ConstString &type_name, lldb::Format format, bool cascade) -{ - Entry entry(format, cascade); - Mutex::Locker locker (m_format_map_mutex); - FormatMap& fmtmap = m_format_map; - fmtmap[type_name.GetCString()] = entry; -} - -bool -FormatManager::DeleteFormatForType (const ConstString &type_name) -{ - Mutex::Locker locker (m_format_map_mutex); - FormatMap& fmtmap = m_format_map; - const char* typeCS = type_name.GetCString(); - FormatMap::iterator iter = fmtmap.find(typeCS); - if (iter != fmtmap.end()) - { - fmtmap.erase(typeCS); - return true; - } - return false; -} - -void -FormatManager::LoopThroughFormatList (Callback callback, void* param) -{ - if (callback) - { - Mutex::Locker locker (m_format_map_mutex); - FormatIterator pos, end = m_format_map.end(); - for (pos = m_format_map.begin(); pos != end; ++pos) - { - const char* type = pos->first; - lldb::Format format = pos->second.format; - bool cascade = pos->second.cascades; - if (!callback(param, type, format, cascade)) - break; - } - } -} - diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index c4bfc3c126c..5177600b0fe 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -19,6 +19,7 @@ // Project includes #include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/Debugger.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/ValueObjectChild.h" #include "lldb/Core/ValueObjectConstResult.h" @@ -71,7 +72,10 @@ ValueObject::ValueObject (ValueObject &parent) : m_children_count_valid (false), m_old_value_valid (false), m_pointers_point_to_load_addrs (false), - m_is_deref_of_parent (false) + m_is_deref_of_parent (false), + m_last_format_mgr_revision(0), + m_last_summary_format(), + m_last_value_format() { m_manager->ManageObject(this); } @@ -103,7 +107,10 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) : m_children_count_valid (false), m_old_value_valid (false), m_pointers_point_to_load_addrs (false), - m_is_deref_of_parent (false) + m_is_deref_of_parent (false), + m_last_format_mgr_revision(0), + m_last_summary_format(), + m_last_value_format() { m_manager = new ValueObjectManager(); m_manager->ManageObject (this); @@ -119,6 +126,9 @@ ValueObject::~ValueObject () bool ValueObject::UpdateValueIfNeeded () { + + UpdateFormatsIfNeeded(); + // If this is a constant value, then our success is predicated on whether // we have an error or not if (GetIsConstant()) @@ -168,6 +178,26 @@ ValueObject::UpdateValueIfNeeded () return m_error.Success(); } +void +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 (m_last_format_mgr_revision != Debugger::ValueFormats::GetCurrentRevision()) + { + if (m_last_summary_format.get()) + m_last_summary_format.reset((SummaryFormat*)NULL); + 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); + m_last_format_mgr_revision = Debugger::ValueFormats::GetCurrentRevision(); + m_value_str.clear(); + m_summary_str.clear(); + } +} + DataExtractor & ValueObject::GetDataExtractor () { @@ -454,9 +484,23 @@ const char * ValueObject::GetSummaryAsCString () { if (UpdateValueIfNeeded ()) - { + { if (m_summary_str.empty()) { + if (m_last_summary_format.get()) + { + StreamString s; + 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)) + { + m_summary_str.swap(s.GetString()); + return m_summary_str.c_str(); + } + return NULL; + } + clang_type_t clang_type = GetClangType(); // See if this is a pointer to a C string? @@ -664,28 +708,6 @@ ValueObject::GetValueAsCString () { if (UpdateValueIfNeeded()) { - /* - this is a quick fix for the case in which we display a variable, then change its format with - type format add and the old display string keeps showing until one steps through the code - */ - { - const Value::ContextType context_type = m_value.GetContextType(); - switch (context_type) - { - case Value::eContextTypeClangType: - case Value::eContextTypeLLDBType: - case Value::eContextTypeVariable: - { - Format format = GetFormat(); - if (format != m_last_format) - m_value_str.clear(); - } - break; - - default: - break; - } - } if (m_value_str.empty()) { const Value::ContextType context_type = m_value.GetContextType(); @@ -701,13 +723,18 @@ ValueObject::GetValueAsCString () { StreamString sstr; Format format = GetFormat(); - if (format == eFormatDefault) - format = ClangASTType::GetFormat(clang_type); + if (format == eFormatDefault) + { + if (m_last_value_format) + format = m_last_value_format->m_format; + else + format = ClangASTType::GetFormat(clang_type); + } if (ClangASTType::DumpTypeValue (GetClangAST(), // The clang AST clang_type, // The clang type to display &sstr, - m_last_format = format, // Format to display this type with + format, // Format to display this type with m_data, // Data to extract from 0, // Byte offset into "m_data" GetByteSize(), // Byte size of item in "m_data" @@ -1123,16 +1150,22 @@ ValueObject::GetNonBaseClassParent() } void -ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes) +ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat) { const bool is_deref_of_parent = IsDereferenceOfParent (); - - if (is_deref_of_parent) - s.PutCString("*("); - if (GetParent()) - GetParent()->GetExpressionPath (s, qualify_cxx_base_classes); + if(is_deref_of_parent && epformat == eDereferencePointers) { + // this is the original format of GetExpressionPath() producing code like *(a_ptr).memberName, which is entirely + // fine, until you put this into StackFrame::GetValueForVariableExpressionPath() which prefers to see a_ptr->memberName. + // the eHonorPointers mode is meant to produce strings in this latter format + s.PutCString("*("); + } + + ValueObject* parent = GetParent(); + if (parent) + parent->GetExpressionPath (s, qualify_cxx_base_classes, epformat); + if (!IsBaseClass()) { if (!is_deref_of_parent) @@ -1145,14 +1178,21 @@ ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes) { const uint32_t non_base_class_parent_type_info = ClangASTContext::GetTypeInfo (non_base_class_parent_clang_type, NULL, NULL); - if (non_base_class_parent_type_info & ClangASTContext::eTypeIsPointer) + if(parent && parent->IsDereferenceOfParent() && epformat == eHonorPointers) { s.PutCString("->"); } - else if ((non_base_class_parent_type_info & ClangASTContext::eTypeHasChildren) && - !(non_base_class_parent_type_info & ClangASTContext::eTypeIsArray)) - { - s.PutChar('.'); + else + { + if (non_base_class_parent_type_info & ClangASTContext::eTypeIsPointer) + { + s.PutCString("->"); + } + else if ((non_base_class_parent_type_info & ClangASTContext::eTypeHasChildren) && + !(non_base_class_parent_type_info & ClangASTContext::eTypeIsArray)) + { + s.PutChar('.'); + } } } } @@ -1170,8 +1210,9 @@ ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes) } } - if (is_deref_of_parent) + if (is_deref_of_parent && epformat == eDereferencePointers) { s.PutChar(')'); + } } void @@ -1245,6 +1286,8 @@ ValueObject::DumpValueObject } const char *val_cstr = NULL; + const char *sum_cstr = NULL; + SummaryFormat* entry = valobj->m_last_summary_format.get(); if (err_cstr == NULL) { @@ -1261,10 +1304,13 @@ ValueObject::DumpValueObject const bool is_ref = type_flags.Test (ClangASTContext::eTypeIsReference); if (print_valobj) { - const char *sum_cstr = valobj->GetSummaryAsCString(); + + sum_cstr = valobj->GetSummaryAsCString(); - if (val_cstr) - s.Printf(" %s", val_cstr); + // We must calculate this value in realtime because entry might alter this variable's value + // (e.g. by saying ${var%fmt}) and render precached values useless + if (val_cstr && (!entry || entry->DoesPrintValue() || !sum_cstr)) + s.Printf(" %s", valobj->GetValueAsCString()); if (sum_cstr) s.Printf(" %s", sum_cstr); @@ -1314,7 +1360,32 @@ ValueObject::DumpValueObject print_children = false; } - if (print_children) + 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)) { const uint32_t num_children = valobj->GetNumChildren(); if (num_children) diff --git a/lldb/source/Symbol/ClangASTType.cpp b/lldb/source/Symbol/ClangASTType.cpp index 9b862d191c4..53011f00fb4 100644 --- a/lldb/source/Symbol/ClangASTType.cpp +++ b/lldb/source/Symbol/ClangASTType.cpp @@ -247,25 +247,8 @@ ClangASTType::GetFormat () lldb::Format ClangASTType::GetFormat (clang_type_t clang_type) { - // first of all, check for a valid format for this type itself clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type)); - lldb::Format format; - bool cascade; - if(Debugger::GetFormatForType(GetClangTypeName(qual_type), format, cascade)) - return format; // return it if found - - // here, I know this type does not have a direct format. two things can happen: - // 1) this is a typedef - I expand this to its parent type and look there - // 2) this is not a typedef - I use the default formatting options - const clang::TypedefType *typedef_type = qual_type->getAs<clang::TypedefType>(); - while (typedef_type) { - qual_type = typedef_type->getDecl()->getUnderlyingType(); - std::string name = qual_type.getAsString(); - if(Debugger::GetFormatForType(GetClangTypeName(qual_type), format, cascade) && cascade) // if I have a cascading format... - return format; // ...use it - typedef_type = qual_type->getAs<clang::TypedefType>(); // try to expand another level - } - + switch (qual_type->getTypeClass()) { case clang::Type::FunctionNoProto: @@ -1347,6 +1330,23 @@ ClangASTType::ReadFromMemory data); } +uint32_t +ClangASTType::GetTypeByteSize() +{ + return GetTypeByteSize(m_ast, + m_type); +} + +uint32_t +ClangASTType::GetTypeByteSize( + clang::ASTContext *ast_context, + lldb::clang_type_t opaque_clang_qual_type) +{ + clang::QualType qual_type(clang::QualType::getFromOpaquePtr(opaque_clang_qual_type)); + + return (ast_context->getTypeSize (qual_type) + 7) / 8; +} + bool ClangASTType::ReadFromMemory diff --git a/lldb/test/functionalities/data-formatter/TestDataFormatter.py b/lldb/test/functionalities/data-formatter/TestDataFormatter.py index b88c6fc8af2..48947f427d4 100644 --- a/lldb/test/functionalities/data-formatter/TestDataFormatter.py +++ b/lldb/test/functionalities/data-formatter/TestDataFormatter.py @@ -51,11 +51,8 @@ class DataFormatterTestCase(TestBase): # 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 delete Speed', check=False) - self.runCmd('type format delete Bitfield', check=False) - self.runCmd('type format delete RealNumber', check=False) - self.runCmd('type format delete Type2', check=False) - self.runCmd('type format delete Type1', check=False) + 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) @@ -87,6 +84,33 @@ class DataFormatterTestCase(TestBase): # 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__': diff --git a/lldb/test/functionalities/data-formatter/main.cpp b/lldb/test/functionalities/data-formatter/main.cpp index 5413bbd291e..94e2993ed90 100644 --- a/lldb/test/functionalities/data-formatter/main.cpp +++ b/lldb/test/functionalities/data-formatter/main.cpp @@ -31,6 +31,12 @@ 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[]) { @@ -57,6 +63,8 @@ int main (int argc, const char * argv[]) Speed* SPPtrILookHex = new Speed(16); + Point iAmSomewhere(4,6); + return 0; // Set break point at this line. } |