From cc3dd43942d55683c54ffcf4fdf3a000155a2357 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Wed, 15 Dec 2010 11:01:01 +0100 Subject: [PATCH] Debugger[New CDB]: Linked lists containers/container child expansion. Introduce node hierarchy and move nodes to a separate file. Introduce reference nodes that point to additional symbols and symbols within the symbol tree (make deeply nested linked list elements visible as array elements). Properly name container elements as array elements 0..n. Fix pre-expansion of complex dumpers. --- src/libs/qtcreatorcdbext/containers.cpp | 70 +- src/libs/qtcreatorcdbext/containers.h | 9 +- .../qtcreatorcdbext/qtcreatorcdbext_build.pro | 4 +- .../qtcreatorcdbext/qtcreatorcdbextension.cpp | 65 +- src/libs/qtcreatorcdbext/symbolgroup.cpp | 1089 ++--------------- src/libs/qtcreatorcdbext/symbolgroup.h | 251 +--- src/libs/qtcreatorcdbext/symbolgroupnode.cpp | 1089 +++++++++++++++++ src/libs/qtcreatorcdbext/symbolgroupnode.h | 362 ++++++ src/libs/qtcreatorcdbext/symbolgroupvalue.cpp | 8 +- 9 files changed, 1679 insertions(+), 1268 deletions(-) create mode 100644 src/libs/qtcreatorcdbext/symbolgroupnode.cpp create mode 100644 src/libs/qtcreatorcdbext/symbolgroupnode.h diff --git a/src/libs/qtcreatorcdbext/containers.cpp b/src/libs/qtcreatorcdbext/containers.cpp index b47087f016d..aa2ec262ef9 100644 --- a/src/libs/qtcreatorcdbext/containers.cpp +++ b/src/libs/qtcreatorcdbext/containers.cpp @@ -34,7 +34,7 @@ #include -typedef SymbolGroupNode::SymbolGroupNodePtrVector SymbolGroupNodePtrVector; +typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector; // Return size of container or -1 int containerSize(KnownType kt, SymbolGroupNode *n, const SymbolGroupValueContext &ctx) @@ -150,16 +150,16 @@ int containerSize(KnownType kt, const SymbolGroupValue &v) /* Generate a list of children by invoking the functions to obtain the value * and the next link */ template -SymbolGroupNodePtrVector linkedListChildList(SymbolGroupValue headNode, - int count, - ValueFunction valueFunc, - NextFunction nextFunc) +AbstractSymbolGroupNodePtrVector linkedListChildList(SymbolGroupValue headNode, + int count, + ValueFunction valueFunc, + NextFunction nextFunc) { - SymbolGroupNodePtrVector rc; + AbstractSymbolGroupNodePtrVector rc; rc.reserve(count); for (int i =0; i < count && headNode; i++) { if (const SymbolGroupValue value = valueFunc(headNode)) { - rc.push_back(value.node()); + rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, value.node())); headNode = nextFunc(headNode); } else { break; @@ -180,33 +180,33 @@ private: }; // std::list: Dummy head node and then a linked list of "_Next", "_Myval". -static inline SymbolGroupNodePtrVector stdListChildList(SymbolGroupNode *n, int count, +static inline AbstractSymbolGroupNodePtrVector stdListChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx) { if (count) if (const SymbolGroupValue head = SymbolGroupValue(n, ctx)[unsigned(0)][unsigned(0)]["_Myhead"]["_Next"]) return linkedListChildList(head, count, MemberByName("_Myval"), MemberByName("_Next")); - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); } // QLinkedList: Dummy head node and then a linked list of "n", "t". -static inline SymbolGroupNodePtrVector qLinkedListChildList(SymbolGroupNode *n, int count, +static inline AbstractSymbolGroupNodePtrVector qLinkedListChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx) { if (count) if (const SymbolGroupValue head = SymbolGroupValue(n, ctx)["e"]["n"]) return linkedListChildList(head, count, MemberByName("t"), MemberByName("n")); - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); } /* Helper for array-type containers: * Add a series of "*(innertype *)0x (address + n * size)" fake child symbols. * for a function generating a sequence of addresses. */ template -SymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc, +AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc, const std::string &innerType, int count) { - SymbolGroupNodePtrVector rc; + AbstractSymbolGroupNodePtrVector rc; if (!count) return rc; std::string errorMessage; @@ -217,8 +217,8 @@ SymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc if (!endsWith(innerType, '*')) str << ' '; str << "*)" << std::showbase << std::hex << addressFunc(); - if (SymbolGroupNode *child = sg->addSymbol(str.str(), toString(i), &errorMessage)) { - rc.push_back(child); + if (SymbolGroupNode *child = sg->addSymbol(str.str(), std::string(), &errorMessage)) { + rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, child)); } else { break; } @@ -244,17 +244,17 @@ private: const ULONG m_delta; }; -static inline SymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, ULONG64 address, +static inline AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, ULONG64 address, const std::string &innerType, int count) { if (const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str())) return arrayChildList(sg, AddressSequence(address, innerTypeSize), innerType, count); - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); } // std::vector -static inline SymbolGroupNodePtrVector +static inline AbstractSymbolGroupNodePtrVector stdVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx) { if (count) { @@ -269,11 +269,11 @@ static inline SymbolGroupNodePtrVector return arrayChildList(n->symbolGroup(), address, SymbolGroupValue::stripPointerType(myFirst.type()), count); } - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); } // QVector -static inline SymbolGroupNodePtrVector +static inline AbstractSymbolGroupNodePtrVector qVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx) { if (count) { @@ -284,7 +284,7 @@ static inline SymbolGroupNodePtrVector if (const ULONG64 arrayAddress = firstElementV.address()) return arrayChildList(n->symbolGroup(), arrayAddress, firstElementV.type(), count); } - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); } // Helper function for arrayChildList() for use with QLists of large types that are an @@ -301,32 +301,32 @@ private: }; // QList<>. -static inline SymbolGroupNodePtrVector +static inline AbstractSymbolGroupNodePtrVector qListChildList(const SymbolGroupValue &v, int count) { // QList: d/array is declared as array of void *[]. Dereference first // element to obtain address. if (!count) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); const SymbolGroupValue dV = v["d"]; if (!dV) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); const int begin = dV["begin"].intValue(); if (begin < 0) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); const SymbolGroupValue firstElementV = dV["array"][unsigned(0)]; if (!firstElementV) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); ULONG64 arrayAddress = firstElementV.address(); if (!arrayAddress) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); const std::vector innerTypes = v.innerTypes(); if (innerTypes.size() != 1) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); const std::string &innerType = innerTypes.front(); const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str()); if (!innerTypeSize) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); /* QList<> is: * 1) An array of 'void *[]' where T values are coerced into the elements for * POD/pointer types and small, movable or primitive Qt types. That is, smaller @@ -355,10 +355,10 @@ static inline SymbolGroupNodePtrVector const HRESULT hr = v.context().dataspaces->ReadVirtual(arrayAddress + begin * pointerSize, data, allocSize, &bytesRead); if (FAILED(hr) || bytesRead != allocSize) { delete [] data; - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); } // Generate sequence of addresses from pointer array - const SymbolGroupNodePtrVector rc = pointerSize == 8 ? + const AbstractSymbolGroupNodePtrVector rc = pointerSize == 8 ? arrayChildList(v.node()->symbolGroup(), AddressArraySequence(reinterpret_cast(data)), innerType, count) : arrayChildList(v.node()->symbolGroup(), AddressArraySequence(reinterpret_cast(data)), innerType, count); delete [] data; @@ -369,11 +369,11 @@ static inline SymbolGroupNodePtrVector innerType, count); } -SymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type, - int size, const SymbolGroupValueContext &ctx) +AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type, + int size, const SymbolGroupValueContext &ctx) { if (!size) - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); if (size > 100) size = 100; switch (type) { @@ -400,5 +400,5 @@ SymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type, case KT_StdList: return stdListChildList(node, size , ctx); } - return SymbolGroupNodePtrVector(); + return AbstractSymbolGroupNodePtrVector(); } diff --git a/src/libs/qtcreatorcdbext/containers.h b/src/libs/qtcreatorcdbext/containers.h index 486500ff1ce..9ea9a81f38d 100644 --- a/src/libs/qtcreatorcdbext/containers.h +++ b/src/libs/qtcreatorcdbext/containers.h @@ -31,6 +31,7 @@ #define CONTAINERS_H struct SymbolGroupValueContext; +class AbstractSymbolGroupNode; class SymbolGroupNode; class SymbolGroupValue; @@ -44,9 +45,9 @@ int containerSize(KnownType kt, const SymbolGroupValue &v); int containerSize(KnownType kt, SymbolGroupNode *n, const SymbolGroupValueContext &ctx); /* Create a list of children of containers. */ -std::vector containerChildren(SymbolGroupNode *node, - int type, - int size, - const SymbolGroupValueContext &ctx); +std::vector containerChildren(SymbolGroupNode *node, + int type, + int size, + const SymbolGroupValueContext &ctx); #endif // CONTAINERS_H diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro b/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro index a2cf9029cd7..eab2af0e99f 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext_build.pro @@ -48,6 +48,7 @@ QT -= core SOURCES += qtcreatorcdbextension.cpp \ extensioncontext.cpp \ eventcallback.cpp \ + symbolgroupnode.cpp \ symbolgroup.cpp \ common.cpp \ stringutils.cpp \ @@ -68,4 +69,5 @@ HEADERS += extensioncontext.h \ base64.h \ symbolgroupvalue.h \ containers.h \ - knowntype.h + knowntype.h \ + symbolgroupnode.h diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index c49b2d2691d..ecfcc9e76c5 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -100,8 +100,9 @@ static const CommandDescription commandDescriptions[] = { "Prints inferior process id and hooks up output callbacks.", "[-t token]"}, {"expandlocals", "Expands local variables by iname in symbol group.", - "[-t token] \n" - "iname1-list: Comma-separated list of inames"}, + "[-t token] [-c] \n" + "iname1-list: Comma-separated list of inames\n" + "-c complex dumpers"}, {"locals", "Prints local variables of symbol group in GDBMI or debug format", "[-t token] [T formats] [-I formats] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n [iname]\n" @@ -247,21 +248,40 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args) std::string errorMessage; int token; - const StringVector tokens = commandTokens(args, &token); + StringList tokens = commandTokens(args, &token); StringVector inames; - if (tokens.size() == 2u && integerFromString(tokens.front(), &frame)) { + bool runComplexDumpers = false; + do { + if (!tokens.empty() && tokens.front() == "-c") { + runComplexDumpers = true; + tokens.pop_front(); + } + if (tokens.empty() || !integerFromString(tokens.front(), &frame)) { + errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]); + break; + } + tokens.pop_front(); + if (tokens.empty()) { + errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]); + break; + } + split(tokens.front(), ',', std::back_inserter(inames)); + } while (false); + + if (errorMessage.empty()) symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage); - split(tokens.at(1), ',', std::back_inserter(inames)); - } else { - errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]); - } - if (symGroup) { - const unsigned succeeded = symGroup->expandList(inames, &errorMessage); - ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s", - succeeded, unsigned(inames.size()), errorMessage.c_str()); - } else { + + if (!symGroup) { ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str()); + return S_OK; } + + const unsigned succeeded = runComplexDumpers ? + symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces()), &errorMessage) : + symGroup->expandList(inames, &errorMessage); + + ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s", + succeeded, unsigned(inames.size()), errorMessage.c_str()); return S_OK; } @@ -338,18 +358,25 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int * if (!tokens.empty()) iname = tokens.front(); + const SymbolGroupValueContext dumpContext(exc.dataSpaces()); SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage); if (!symGroup) return std::string(); - if (!expandedInames.empty()) - symGroup->expandList(expandedInames, errorMessage); + if (!uninitializedInames.empty()) symGroup->markUninitialized(uninitializedInames); + if (!expandedInames.empty()) { + if (parameters.dumpFlags & DumpParameters::DumpComplexDumpers) { + symGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage); + } else { + symGroup->expandList(expandedInames, errorMessage); + } + } + if (debugOutput) return symGroup->debug(iname, debugOutput - 1); - const SymbolGroupValueContext dumpContext(exc.dataSpaces()); return iname.empty() ? symGroup->dump(dumpContext, parameters) : symGroup->dump(iname, dumpContext, parameters, errorMessage); @@ -393,13 +420,13 @@ static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int if (!symGroup) return std::string(); - SymbolGroupNode *n = symGroup->find(iname); - if (!n) { + AbstractSymbolGroupNode *n = symGroup->find(iname); + if (!n || !n->asSymbolGroupNode()) { *errorMessage = "No such iname " + iname; return std::string(); } std::wstring value; - if (!dumpSimpleType(n, SymbolGroupValueContext(exc.dataSpaces()), &value)) { + if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces()), &value)) { *errorMessage = "Cannot dump " + iname; return std::string(); } diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index 427b08f56f8..c2510c7c509 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -28,221 +28,18 @@ **************************************************************************/ #include "symbolgroup.h" -#include "symbolgroupvalue.h" #include "stringutils.h" -#include "base64.h" -#include "containers.h" -#include -#include -#include -#include #include - -enum { debug = 0 }; +#include +#include typedef std::vector::size_type VectorIndexType; typedef std::vector StringVector; +enum { debug = 0 }; const char rootNameC[] = "local"; -enum { BufSize = 2048 }; - -std::ostream &operator<<(std::ostream &str, const DEBUG_SYMBOL_PARAMETERS ¶meters) -{ - str << "parent="; - if (parameters.ParentSymbol == DEBUG_ANY_ID) { - str << "DEBUG_ANY_ID"; - } else { - str << parameters.ParentSymbol ; - } - if (parameters.Flags != 0 && parameters.Flags != 1) - str << " flags=" << parameters.Flags; - // Detailed flags: - if (parameters.Flags & DEBUG_SYMBOL_EXPANDED) - str << " EXPANDED"; - if (parameters.Flags & DEBUG_SYMBOL_READ_ONLY) - str << " READONLY"; - if (parameters.Flags & DEBUG_SYMBOL_IS_ARRAY) - str << " ARRAY"; - if (parameters.Flags & DEBUG_SYMBOL_IS_FLOAT) - str << " FLOAT"; - if (parameters.Flags & DEBUG_SYMBOL_IS_ARGUMENT) - str << " ARGUMENT"; - if (parameters.Flags & DEBUG_SYMBOL_IS_LOCAL) - str << " LOCAL"; - str << " typeId=" << parameters.TypeId; - if (parameters.SubElements) - str << " subElements=" << parameters.SubElements; - return str; -} - -// --------------- DumpParameters -DumpParameters::DumpParameters() : dumpFlags(0) -{ -} - -// typeformats: decode hex-encoded name, value pairs: -// '414A=2,...' -> map of "AB:2". -DumpParameters::FormatMap DumpParameters::decodeFormatArgument(const std::string &f) -{ - FormatMap rc; - const std::string::size_type size = f.size(); - // Split 'hexname=4,' - for (std::string::size_type pos = 0; pos < size ; ) { - // Cut out key - const std::string::size_type equalsPos = f.find('=', pos); - if (equalsPos == std::string::npos) - return rc; - const std::string name = stringFromHex(f.c_str() + pos, f.c_str() + equalsPos); - // Search for number - const std::string::size_type numberPos = equalsPos + 1; - std::string::size_type nextPos = f.find(',', numberPos); - if (nextPos == std::string::npos) - nextPos = size; - int format; - if (!integerFromString(f.substr(numberPos, nextPos - numberPos), &format)) - return rc; - rc.insert(FormatMap::value_type(name, format)); - pos = nextPos + 1; - } - return rc; -} - -int DumpParameters::format(const std::string &type, const std::string &iname) const -{ - if (!individualFormats.empty()) { - const FormatMap::const_iterator iit = individualFormats.find(iname); - if (iit != individualFormats.end()) - return iit->second; - } - if (!typeFormats.empty()) { - const FormatMap::const_iterator tit = typeFormats.find(type); - if (tit != typeFormats.end()) - return tit->second; - } - return -1; -} - -enum PointerFormats // Watch data pointer format requests -{ - FormatAuto = 0, - FormatLatin1String = 1, - FormatUtf8String = 2, - FormatUtf16String = 3, - FormatUcs4String = 4 -}; - -enum DumpEncoding // WatchData encoding of GDBMI values -{ - DumpEncodingAscii = 0, - DumpEncodingBase64 = 1, - DumpEncodingBase64_Utf16 = 2, - DumpEncodingBase64_Ucs4 = 3, - DumpEncodingHex_Latin1 = 6, - DumpEncodingHex_Utf16 = 7, - DumpEncodingHex_Ucs4_LittleEndian = 8, - DumpEncodingHex_Utf8_LittleEndian = 9, - DumpEncodingHex_Ucs4_BigEndian = 10, - DumpEncodingHex_Utf16_BigEndian = 11, - DumpEncodingHex_Utf16_LittleEndian = 12 -}; - -/* Recode arrays/pointers of char*, wchar_t according to users - * sepcification. Handles char formats for 'char *', '0x834478 "hallo.."' - * and 'wchar_t *', '0x834478 "hallo.."'. - * This is done by retrieving the address and the length (in characters) - * of the CDB output, converting it to memory size, fetching the data - * from memory, zero-terminating and recoding it using the encoding - * defined in watchutils.cpp. - * As a special case, if there is no user-defined format and the - * CDB output contains '?' indicating non-printable characters, - * append a hex dump of the memory (auto-format). */ - -bool DumpParameters::recode(const std::string &type, - const std::string &iname, - const SymbolGroupValueContext &ctx, - std::wstring *value, int *encoding) const -{ - // We basically handle char formats for 'char *', '0x834478 "hallo.."' - // and 'wchar_t *', '0x834478 "hallo.."' - // Determine address and length from the pointer value output, - // read the raw memory and recode if that is possible. - if (type.empty() || type.at(type.size() - 1) != '*') - return false; - const int newFormat = format(type, iname); - if (value->compare(0, 2, L"0x")) - return false; - const std::wstring::size_type quote1 = value->find(L'"', 2); - if (quote1 == std::wstring::npos) - return false; - // The user did not specify any format, still, there are '?' - // (indicating non-printable) in what the debugger prints. In that case, - // append a hex dump to the normal output. If there are no '?'-> all happy. - if (newFormat < FormatLatin1String && value->find(L'?', quote1 + 1) == std::wstring::npos) - return false; - const std::wstring::size_type quote2 = value->find(L'"', quote1 + 1); - if (quote2 == std::wstring::npos) - return false; - std::wstring::size_type length = quote2 - quote1 - 1; - if (!length) - return false; - // Get address from value - ULONG64 address = 0; - if (!integerFromWString(value->substr(0, quote1 - 1), &address) || !address) - return false; - // Get real size if this is for example a wchar_t *. - const unsigned elementSize = SymbolGroupValue::sizeOf(SymbolGroupValue::stripPointerType(type).c_str()); - if (!elementSize) - return false; - length *= elementSize; - // Allocate real length + 8 bytes ('\0') for largest format (Ucs4). - // '\0' is not listed in the CDB output. - const std::wstring::size_type allocLength = length + 8; - unsigned char *buffer = new unsigned char[allocLength]; - std::fill(buffer, buffer + allocLength, 0); - ULONG obtained = 0; - if (FAILED(ctx.dataspaces->ReadVirtual(address, buffer, ULONG(length), &obtained))) { - delete [] buffer; - return false; - } - // Recode raw memory - switch (newFormat) { - case FormatLatin1String: - *value = dataToHexW(buffer, buffer + length + 1); // Latin1 + 0 - *encoding = DumpEncodingHex_Latin1; - break; - case FormatUtf8String: - *value = dataToHexW(buffer, buffer + length + 1); // UTF8 + 0 - *encoding = DumpEncodingHex_Utf8_LittleEndian; - break; - case FormatUtf16String: // Paranoia: make sure buffer is terminated at 2 byte borders - if (length % 2) { - length &= ~1; - buffer[length] = '\0'; - buffer[length + 1] = '\0'; - } - *value = base64EncodeToWString(buffer, length + 2); - *encoding = DumpEncodingBase64_Utf16; - break; - case FormatUcs4String: // Paranoia: make sure buffer is terminated at 4 byte borders - if (length % 4) { - length &= ~3; - std::fill(buffer + length, buffer + length + 4, 0); - } - *value = dataToHexW(buffer, buffer + length + 2); // UTF16 + 0 - *encoding = DumpEncodingHex_Ucs4_LittleEndian; - break; - default: // See above, append hex dump - value->push_back(' '); - value->append(dataToReadableHexW(buffer, buffer + length)); - *encoding = DumpEncodingAscii; - break; - } - delete [] buffer; - return true; -} - // ------------- SymbolGroup SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg, const SymbolParameterVector &vec, @@ -378,662 +175,6 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS return new SymbolGroup(idebugSymbols, parameters, threadId, frame); } -// ------- SymbolGroupNode -SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup, - ULONG index, - const std::string &name, - const std::string &iname, - SymbolGroupNode *parent) : - m_symbolGroup(symbolGroup), m_parent(parent), - m_index(index), m_referencedBy(0), - m_name(name), m_iname(iname), m_flags(0), m_dumperType(-1), - m_dumperContainerSize(-1) -{ - memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS)); - m_parameters.ParentSymbol = DEBUG_ANY_ID; -} - -void SymbolGroupNode::removeChildren() -{ - if (!m_children.empty()) { - const SymbolGroupNodePtrVectorIterator end = m_children.end(); - for (SymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it) { - SymbolGroupNode *child = *it; - if (child->parent() == this) // Do not delete references - delete child; - } - m_children.clear(); - } -} - -void SymbolGroupNode::setReferencedBy(SymbolGroupNode *n) -{ - if (m_referencedBy) - dprintf("Internal error: Node %s Clearing reference by %s", - name().c_str(), m_referencedBy->name().c_str()); - m_referencedBy = n; -} - -bool SymbolGroupNode::isArrayElement() const -{ - return m_parent && (m_parent->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY); -} - -// Notify about expansion of a node: -// Adapt our index and those of our children if we are behind it. -// Return true if a modification was required to be able to terminate the -// recursion. -bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount) -{ - typedef SymbolGroupNodePtrVector::reverse_iterator ReverseIt; - // Looping backwards over the children. If a subtree has no modifications, - // (meaning all other indexes are smaller) we can stop. - const ReverseIt rend = m_children.rend(); - for (ReverseIt it = m_children.rbegin(); it != rend; ++it) { - SymbolGroupNode *c = *it; - if (c->parent() == this) // Skip fake children that are referenced only - if (!(*it)->notifyExpanded(index, insertedCount)) - return false; - } - - // Correct our own + parent index if applicable. - if (m_index == DEBUG_ANY_ID || m_index < index) - return false; - - m_index += insertedCount; - if (m_parameters.ParentSymbol != DEBUG_ANY_ID && m_parameters.ParentSymbol >= index) - m_parameters.ParentSymbol += insertedCount; - return true; -} - -// Return full iname as 'locals.this.m_sth'. -std::string SymbolGroupNode::fullIName() const -{ - std::string rc = iName(); - for (const SymbolGroupNode *p = referencedParent(); p; p = p->referencedParent()) { - rc.insert(0, 1, '.'); - rc.insert(0, p->iName()); - } - return rc; -} - -// Fix names: fix complicated template base names -static inline void fixName(std::string *name) -{ - // Long template base classes 'std::tree_base' -> 'std::tree<>' - // for nice display - const std::string::size_type templatePos = name->find('<'); - if (templatePos != std::string::npos) { - name->erase(templatePos + 1, name->size() - templatePos - 1); - name->push_back('>'); - } -} - -// Fix inames: arrays and long, complicated template base names -static inline void fixIname(unsigned &id, std::string *iname) -{ - // Fix array iname "[0]" -> "0" for sorting to work correctly - if (!iname->empty() && iname->at(0) == '[') { - const std::string::size_type last = iname->size() - 1; - if (iname->at(last) == ']') { - iname->erase(last, 1); - iname->erase(0, 1); - return; - } - } - // Long template base classes 'std::tree_base 'tree@t1', - // usable as identifier and command line parameter - const std::string::size_type templatePos = iname->find('<'); - if (templatePos != std::string::npos) { - iname->erase(templatePos, iname->size() - templatePos); - if (iname->compare(0, 5, "std::") == 0) - iname->erase(0, 5); - iname->append("@t"); - iname->append(toString(id++)); - } -} - -// Fix up names and inames -static inline void fixNames(bool isTopLevel, StringVector *names, StringVector *inames) -{ - if (names->empty()) - return; - unsigned unnamedId = 1; - unsigned templateId = 1; - /* 1) Fix name="__formal", which occurs when someone writes "void foo(int /* x * /)..." - * 2) Fix array inames for sorting: "[6]" -> name="[6]",iname="6" - * 3) For toplevels: Fix shadowed variables in the order the debugger expects them: - \code - int x; // Occurrence (1), should be reported as name="x "/iname="x#1" - if (true) { - int x = 5; (2) // Occurrence (2), should be reported as name="x"/iname="x" - } - \endcode */ - StringVector::iterator nameIt = names->begin(); - const StringVector::iterator namesEnd = names->end(); - for (StringVector::iterator iNameIt = inames->begin(); nameIt != namesEnd ; ++nameIt, ++iNameIt) { - std::string &name = *nameIt; - std::string &iname = *iNameIt; - if (name.empty() || name == "__formal") { - const std::string number = toString(unnamedId++); - name = "'; - iname = "unnamed#" + number; - } else { - fixName(&name); - fixIname(templateId, &iname); - } - if (isTopLevel) { - if (const StringVector::size_type shadowCount = std::count(nameIt + 1, namesEnd, name)) { - const std::string number = toString(shadowCount); - name += " 1) - m_children.reserve(m_parameters.SubElements); - - const VectorIndexType size = vec.size(); - // Scan the top level elements - StringVector names; - names.reserve(size); - // Pass 1) Determine names. We need the complete set first in order to do some corrections. - const VectorIndexType startIndex = isTopLevel ? 0 : index + 1; - for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) { - if (vec.at(pos).ParentSymbol == index) { - const VectorIndexType symbolGroupIndex = pos + parameterOffset; - if (FAILED(m_symbolGroup->debugSymbolGroup()->GetSymbolName(ULONG(symbolGroupIndex), buf, BufSize, &obtainedSize))) - buf[0] = '\0'; - names.push_back(std::string(buf)); - } - } - // 2) Fix names - StringVector inames = names; - fixNames(isTopLevel, &names, &inames); - // Pass 3): Add nodes with fixed names - StringVector::size_type nameIndex = 0; - for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) { - if (vec.at(pos).ParentSymbol == index) { - const VectorIndexType symbolGroupIndex = pos + parameterOffset; - SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup, - ULONG(symbolGroupIndex), - names.at(nameIndex), - inames.at(nameIndex), this); - child->parseParameters(symbolGroupIndex, parameterOffset, vec); - m_children.push_back(child); - nameIndex++; - } - } - if (isTopLevel) - m_parameters.SubElements = ULONG(m_children.size()); -} - -SymbolGroupNode *SymbolGroupNode::create(SymbolGroup *sg, const std::string &name, const SymbolGroup::SymbolParameterVector &vec) -{ - SymbolGroupNode *rc = new SymbolGroupNode(sg, DEBUG_ANY_ID, name, name); - rc->parseParameters(DEBUG_ANY_ID, 0, vec); - return rc; -} - -// Fix some oddities in CDB values - -static inline bool isHexDigit(wchar_t c) -{ - return (c >= L'0' && c <= L'9') || (c >= L'a' && c <= L'f') || (c >= L'A' && c <= L'F'); -} - -static void fixValue(const std::string &type, std::wstring *value) -{ - // Pointers/Unsigned integers: fix '0x00000000`00000AD bla' ... to "0xAD bla" - const bool isHexNumber = value->size() > 3 && value->compare(0, 2, L"0x") == 0 && isHexDigit(value->at(2)); - if (isHexNumber) { - // Remove dumb 64bit separator - if (value->size() > 10 && value->at(10) == L'`') - value->erase(10, 1); - const std::string::size_type firstNonNullDigit = value->find_first_not_of(L"0", 2); - // No on-null digits: plain null ptr. - if (firstNonNullDigit == std::string::npos || value->at(firstNonNullDigit) == ' ') { - *value = L"0x0"; - } else { - // Strip - if (firstNonNullDigit > 2) - value->erase(2, firstNonNullDigit - 2); - } - } - - // Pointers: fix '0x00000000`00000AD class bla' ... to "0xAD", but leave - // 'const char *' values as is ('0x00000000`00000AD "hallo"). - if (!type.empty() && type.at(type.size() - 1) == L'*') { - // Strip ' Class bla" - std::wstring::size_type classPos = value->find(L" struct", 2); - if (classPos == std::string::npos) - classPos = value->find(L" class", 2); - if (classPos != std::string::npos) - value->erase(classPos, value->size() - classPos); - return; - } - - // unsigned hex ints that are not pointers: Convert to decimal as not to confuse watch model: - if (isHexNumber) { - ULONG64 uv; - std::wistringstream str(*value); - str >> std::hex >> uv; - if (!str.fail()) { - *value = toWString(uv); - return; - } - } - - // Integers: fix '0n10' -> '10' - if (value->size() >= 3 && value->compare(0, 2, L"0n") == 0 - && (isdigit(value->at(2)) || value->at(2) == L'-')) { - value->erase(0, 2); - return; - } - // Fix long class names on std containers 'class std::tree<...>' -> 'class std::tree<>' - if (value->compare(0, 6, L"class ") == 0 || value->compare(0, 7, L"struct ") == 0) { - const std::string::size_type openTemplate = value->find(L'<'); - if (openTemplate != std::string::npos) { - value->erase(openTemplate + 1, value->size() - openTemplate - 2); - return; - } - } -} - -// Check for ASCII-encode-able stuff. Plain characters + tabs at the most, no newline. -static bool isSevenBitClean(const wchar_t *buf, size_t size) -{ - const wchar_t *bufEnd = buf + size; - for (const wchar_t *bufPtr = buf; bufPtr < bufEnd; bufPtr++) { - const wchar_t c = *bufPtr; - if (c > 127 || (c < 32 && c != 9)) - return false; - } - return true; -} - -std::string SymbolGroupNode::type() const -{ - static char buf[BufSize]; - const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolTypeName(m_index, buf, BufSize, NULL); - return SUCCEEDED(hr) ? std::string(buf) : std::string(); -} - -unsigned SymbolGroupNode::size() const -{ - DEBUG_SYMBOL_ENTRY entry; - if (SUCCEEDED(m_symbolGroup->debugSymbolGroup()->GetSymbolEntryInformation(m_index, &entry))) - return entry.Size; - return 0; -} - -ULONG64 SymbolGroupNode::address() const -{ - ULONG64 address = 0; - const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolOffset(m_index, &address); - if (SUCCEEDED(hr)) - return address; - return 0; -} - -std::wstring SymbolGroupNode::symbolGroupRawValue() const -{ - // Determine size and return allocated buffer - const ULONG maxValueSize = 262144; - ULONG obtainedSize = 0; - HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, NULL, maxValueSize, &obtainedSize); - if (FAILED(hr)) - return std::wstring(); - if (obtainedSize > maxValueSize) - obtainedSize = maxValueSize; - wchar_t *buffer = new wchar_t[obtainedSize]; - hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, buffer, obtainedSize, &obtainedSize); - if (FAILED(hr)) // Whoops, should not happen - buffer[0] = 0; - const std::wstring rc(buffer); - delete [] buffer; - return rc; -} - -std::wstring SymbolGroupNode::symbolGroupFixedValue() const -{ - std::wstring value = symbolGroupRawValue(); - fixValue(type(), &value); - return value; -} - -// Complex dumpers: Get container/fake children -void SymbolGroupNode::runComplexDumpers(const SymbolGroupValueContext &ctx) -{ - if (m_dumperContainerSize <= 0 || (m_flags & ComplexDumperOk) || !(m_flags & SimpleDumperOk)) - return; - m_flags |= ComplexDumperOk; - const SymbolGroupNodePtrVector children = - containerChildren(this, m_dumperType, m_dumperContainerSize, ctx); - m_dumperContainerSize = int(children.size()); // Just in case... - if (children.empty()) - return; - - clearFlags(ExpandedByDumper); - // Mark current children as obscured. We cannot show both currently - // as this would upset the numerical sorting of the watch model - SymbolGroupNodePtrVectorConstIterator cend = m_children.end(); - for (SymbolGroupNodePtrVectorConstIterator it = m_children.begin(); it != cend; ++it) - (*it)->addFlags(Obscured); - // Add children and mark them as referenced by us. - cend = children.end(); - for (SymbolGroupNodePtrVectorConstIterator it = children.begin(); it != cend; ++it) { - SymbolGroupNode *c = *it; - c->setReferencedBy(this); - m_children.push_back(c); - } -} - -// Run dumpers, format simple in-line dumper value and retrieve fake children -std::wstring SymbolGroupNode::simpleDumpValue(const SymbolGroupValueContext &ctx, - const DumpParameters &) -{ - if (m_flags & Uninitialized) - return L""; - if (m_flags & SimpleDumperOk) - return m_dumperValue; - if ((m_flags & SimpleDumperMask) == 0) { - m_flags |= dumpSimpleType(this , ctx, &m_dumperValue, - &m_dumperType, &m_dumperContainerSize); - if (m_flags & SimpleDumperOk) - return m_dumperValue; - } - return symbolGroupFixedValue(); -} - -SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const -{ - return i < m_children.size() ? m_children.at(i) : static_cast(0); -} - -unsigned SymbolGroupNode::indexByIName(const char *n) const -{ - const VectorIndexType size = m_children.size(); - for (VectorIndexType i = 0; i < size; i++) - if ( m_children.at(i)->iName() == n ) - return unsigned(i); - return unsigned(-1); -} - -SymbolGroupNode *SymbolGroupNode::childByIName(const char *n) const -{ - const unsigned index = indexByIName(n); - if (index != unsigned(-1)) - return m_children.at(index); - return 0; -} - -static inline void indentStream(std::ostream &str, unsigned depth) -{ - for (unsigned d = 0; d < depth; d++) - str << " "; -} - -void SymbolGroupNode::dump(std::ostream &str, - const DumpParameters &p, - const SymbolGroupValueContext &ctx) -{ - const std::string iname = fullIName(); - const std::string t = type(); - - str << "iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name - << "\",type=\"" << t << '"'; - - if (const ULONG64 addr = address()) - str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec - << '"'; - - const bool uninitialized = m_flags & Uninitialized; - bool valueEditable = !uninitialized; - bool valueEnabled = !uninitialized; - - // Shall it be recoded? - std::wstring value = simpleDumpValue(ctx, p); - int encoding = 0; - if (p.recode(t, iname, ctx, &value, &encoding)) { - str << ",valueencoded=\"" << encoding - << "\",value=\"" << gdbmiWStringFormat(value) <<'"'; - } else { // As is: ASCII or base64? - if (isSevenBitClean(value.c_str(), value.size())) { - str << ",valueencoded=\"" << DumpEncodingAscii << "\",value=\"" - << gdbmiWStringFormat(value) << '"'; - } else { - str << ",valueencoded=\"" << DumpEncodingBase64_Utf16 << "\",value=\""; - base64Encode(str, reinterpret_cast(value.c_str()), value.size() * sizeof(wchar_t)); - str << '"'; - } - } - // Children: Dump all known non-obscured or subelements - unsigned childCountGuess = 0; - if (!uninitialized) { - if (m_dumperContainerSize > 0) { - childCountGuess = m_dumperContainerSize; // See Obscured handling - } else { - if (m_children.empty()) { - childCountGuess = m_parameters.SubElements; // Guess - } else { - childCountGuess = unsigned(m_children.size()); - } - } - } - // No children..suppose we are editable and enabled - if (childCountGuess != 0) - valueEditable = false; - str << ",valueenabled=\"" << (valueEnabled ? "true" : "false") << '"' - << ",valueeditable=\"" << (valueEditable ? "true" : "false") << '"' - << ",numchild=\"" << childCountGuess << '"'; -} - -bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth) -{ - // If we happen to be the root node, just skip over - const bool invisibleRoot = m_index == DEBUG_ANY_ID; - const unsigned childDepth = invisibleRoot ? 0 : depth + 1; - - const SymbolGroupNodeVisitor::VisitResult vr = - invisibleRoot ? SymbolGroupNodeVisitor::VisitContinue : - visitor.visit(this, child, depth); - switch (vr) { - case SymbolGroupNodeVisitor::VisitStop: - return true; - case SymbolGroupNodeVisitor::VisitSkipChildren: - break; - case SymbolGroupNodeVisitor::VisitContinue: { - const unsigned childCount = unsigned(m_children.size()); - for (unsigned c = 0; c < childCount; c++) - if (m_children.at(c)->accept(visitor, c, childDepth)) - return true; - if (!invisibleRoot) - visitor.childrenVisited(this, depth); - } - break; - } - return false; -} - -void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned depth) const -{ - indentStream(str, depth); - str << '"' << fullIName() << "\",index=" << m_index; - if (m_referencedBy) - str << ",referenced by \"" << m_referencedBy->fullIName() << '"'; - if (const VectorIndexType childCount = m_children.size()) - str << ", Children=" << childCount; - str << ' ' << m_parameters; - if (m_flags) { - str << " node-flags=" << m_flags; - if (m_flags & Uninitialized) - str << " UNINITIALIZED"; - if (m_flags & SimpleDumperNotApplicable) - str << " DumperNotApplicable"; - if (m_flags & SimpleDumperOk) - str << " DumperOk"; - if (m_flags & SimpleDumperFailed) - str << " DumperFailed"; - if (m_flags & ExpandedByDumper) - str << " ExpandedByDumper"; - if (m_flags & AdditionalSymbol) - str << " AdditionalSymbol"; - if (m_flags & Obscured) - str << " Obscured"; - if (m_flags & ComplexDumperOk) - str << " ComplexDumperOk"; - str << ' '; - } - if (verbosity) { - str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec - << " Type=\"" << type() << '"'; - if (m_dumperType >= 0) { - str << " ,dumperType=" << m_dumperType; - if (m_dumperType & KT_Qt_Type) - str << " qt"; - if (m_dumperType & KT_STL_Type) - str << " STL"; - if (m_dumperType & KT_ContainerType) - str << " container(" << m_dumperContainerSize << ')'; - } - if (!(m_flags & Uninitialized)) - str << " Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"'; - } - str << '\n'; -} - -static inline std::string msgCannotCast(const std::string &nodeName, - const std::string &fromType, - const std::string &toType, - const std::string &why) -{ - std::ostringstream str; - str << "Cannot cast node '" << nodeName << "' from '" << fromType - << "' to '" << toType << "': " << why; - return str.str(); -} - -// Expand! -bool SymbolGroupNode::expand(std::string *errorMessage) -{ - if (::debug > 1) - DebugPrint() << "SymbolGroupNode::expand " << m_name << ' ' << m_index; - if (isExpanded()) { - // Clear the flag indication dumper expansion on a second, explicit request - clearFlags(ExpandedByDumper); - return true; - } - if (!canExpand()) { - *errorMessage = "No subelements to expand in node: " + fullIName(); - return false; - } - if (m_flags & Uninitialized) { - *errorMessage = "Refusing to expand uninitialized node: " + fullIName(); - return false; - } - - const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE); - - if (FAILED(hr)) { - *errorMessage = msgDebugEngineComFailed("ExpandSymbol", hr); - return false; - } - SymbolGroup::SymbolParameterVector parameters; - // Retrieve parameters (including self, re-retrieve symbol parameters to get new 'expanded' flag - // and corrected SubElement count (might be estimate)) - if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(), - m_index, m_parameters.SubElements + 1, - ¶meters, errorMessage)) - return false; - // Before inserting children, correct indexes on whole group - m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements); - // Parse parameters, correct our own) and create child nodes. - parseParameters(m_index, m_index, parameters); - return true; -} - -bool SymbolGroupNode::typeCast(const std::string &desiredType, std::string *errorMessage) -{ - const std::string fromType = type(); - if (fromType == desiredType) - return true; - if (isExpanded()) { - *errorMessage = msgCannotCast(fullIName(), fromType, desiredType, "Already expanded"); - return false; - } - HRESULT hr = m_symbolGroup->debugSymbolGroup()->OutputAsType(m_index, desiredType.c_str()); - if (FAILED(hr)) { - *errorMessage = msgCannotCast(fullIName(), fromType, desiredType, msgDebugEngineComFailed("OutputAsType", hr)); - return false; - } - hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(m_index, 1, &m_parameters); - if (FAILED(hr)) { // Should never fail - *errorMessage = msgCannotCast(fullIName(), fromType, desiredType, msgDebugEngineComFailed("GetSymbolParameters", hr)); - return false; - } - return true; -} - -static inline std::string msgCannotAddSymbol(const std::string &name, const std::string &why) -{ - std::ostringstream str; - str << "Cannot add symbol '" << name << "': " << why; - return str.str(); -} - -// For root nodes, only: Add a new symbol by name -SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name, - const std::string &iname, - std::string *errorMessage) -{ - ULONG index = DEBUG_ANY_ID; // Append - HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index); - if (FAILED(hr)) { - *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr)); - return 0; - } - SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS()); - hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin())); - if (FAILED(hr)) { // Should never fail - *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("GetSymbolParameters", hr)); - return 0; - } - // Paranoia: Check for cuckoo's eggs (which should not happen) - if (parameters.front().ParentSymbol != m_index) { - *errorMessage = msgCannotAddSymbol(name, "Parent id mismatch"); - return 0; - } - SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index, - name, iname.empty() ? name : iname, - this); - node->parseParameters(0, 0, parameters); - node->addFlags(AdditionalSymbol); - m_children.push_back(node); - return node; -} - static inline std::string msgNotFound(const std::string &nodeName) { std::ostringstream str; @@ -1060,26 +201,31 @@ std::string SymbolGroup::dump(const std::string &iname, const DumpParameters &p, std::string *errorMessage) { - SymbolGroupNode *const node = find(iname); - if (node == 0) { + AbstractSymbolGroupNode *const aNode = find(iname); + if (!aNode) { *errorMessage = msgNotFound(iname); return std::string(); } - if (node->isExpanded()) { // Mark expand request by watch model - node->clearFlags(SymbolGroupNode::ExpandedByDumper); - } else { - if (node->canExpand() && !node->expand(errorMessage)) - return false; + + // Real nodes: Expand and complex dumpers + if (SymbolGroupNode *node = aNode->asSymbolGroupNode()) { + if (node->isExpanded()) { // Mark expand request by watch model + node->clearFlags(SymbolGroupNode::ExpandedByDumper); + } else { + if (node->canExpand() && !node->expand(errorMessage)) + return false; + } + // After expansion, run the complex dumpers + if (p.dumpFlags & DumpParameters::DumpComplexDumpers) + node->runComplexDumpers(ctx); } - // After expansion, run the complex dumpers - if (p.dumpFlags & DumpParameters::DumpComplexDumpers) - node->runComplexDumpers(ctx); + std::ostringstream str; if (p.humanReadable()) str << '\n'; DumpSymbolGroupNodeVisitor visitor(str, ctx, p); str << '['; - node->accept(visitor, 0, 0); + aNode->accept(visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0); str << ']'; return str.str(); } @@ -1092,8 +238,8 @@ std::string SymbolGroup::debug(const std::string &iname, unsigned verbosity) con if (iname.empty()) { accept(visitor); } else { - if (SymbolGroupNode *const node = find(iname)) { - node->accept(visitor, 0, 0); + if (AbstractSymbolGroupNode *const node = find(iname)) { + node->accept(visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0); } else { str << msgNotFound(iname); } @@ -1123,25 +269,32 @@ struct InamePathEntryLessThan : public std::binary_function InamePathEntrySet; -// Expand a node list "locals.i1,locals.i2" -unsigned SymbolGroup::expandList(const std::vector &nodes, std::string *errorMessage) +static inline InamePathEntrySet expandEntrySet(const std::vector &nodes) { - if (nodes.empty()) - return 0; - // Create a set with a key . Also required for 1 node (see above). InamePathEntrySet pathEntries; const VectorIndexType nodeCount = nodes.size(); for (VectorIndexType i= 0; i < nodeCount; i++) { const std::string &iname = nodes.at(i); std::string::size_type pos = 0; + // Split a path 'local.foo' and insert (0,'local'), (1,'local.foo') (see above) for (unsigned level = 0; pos < iname.size(); level++) { - std::string::size_type dotPos = iname.find('.', pos); + std::string::size_type dotPos = iname.find(SymbolGroupNodeVisitor::iNamePathSeparator, pos); if (dotPos == std::string::npos) dotPos = iname.size(); pathEntries.insert(InamePathEntry(level, iname.substr(0, dotPos))); pos = dotPos + 1; } } + return pathEntries; +} + +// Expand a node list "locals.i1,locals.i2" +unsigned SymbolGroup::expandList(const std::vector &nodes, std::string *errorMessage) +{ + if (nodes.empty()) + return 0; + // Create a set with a key . Also required for 1 node (see above). + const InamePathEntrySet pathEntries = expandEntrySet(nodes); // Now expand going by level. unsigned succeeded = 0; std::string nodeError; @@ -1157,32 +310,80 @@ unsigned SymbolGroup::expandList(const std::vector &nodes, std::str return succeeded; } +unsigned SymbolGroup::expandListRunComplexDumpers(const std::vector &nodes, + const SymbolGroupValueContext &ctx, + std::string *errorMessage) +{ + if (nodes.empty()) + return 0; + // Create a set with a key . Also required for 1 node (see above). + const InamePathEntrySet pathEntries = expandEntrySet(nodes); + // Now expand going by level. + unsigned succeeded = 0; + std::string nodeError; + InamePathEntrySet::const_iterator cend = pathEntries.end(); + for (InamePathEntrySet::const_iterator it = pathEntries.begin(); it != cend; ++it) + if (expandRunComplexDumpers(it->second, ctx, &nodeError)) { + succeeded++; + } else { + if (!errorMessage->empty()) + errorMessage->append(", "); + errorMessage->append(nodeError); + } + return succeeded; +} + +// Find a node for expansion, skipping reference nodes. +static inline SymbolGroupNode * + findNodeForExpansion(const SymbolGroup *sg, + const std::string &nodeName, + std::string *errorMessage) +{ + AbstractSymbolGroupNode *aNode = sg->find(nodeName); + if (!aNode) { + *errorMessage = msgNotFound(nodeName); + return 0; + } + + SymbolGroupNode *node = aNode->resolveReference()->asSymbolGroupNode(); + if (!node) { + *errorMessage = "Node type error in expand: " + nodeName; + return 0; + } + return node; +} + bool SymbolGroup::expand(const std::string &nodeName, std::string *errorMessage) { - SymbolGroupNode *node = find(nodeName); - if (::debug) - DebugPrint() << "expand: " << nodeName << " found=" << (node != 0) << '\n'; - if (!node) { - *errorMessage = msgNotFound(nodeName); - return false; - } - if (node == m_root) // Shouldn't happen, still, all happy - return true; - return node->expand(errorMessage); + if (SymbolGroupNode *node = findNodeForExpansion(this, nodeName, errorMessage)) + return node == m_root ? true : node->expand(errorMessage); + return false; +} + +bool SymbolGroup::expandRunComplexDumpers(const std::string &nodeName, const SymbolGroupValueContext &ctx, std::string *errorMessage) +{ + if (SymbolGroupNode *node = findNodeForExpansion(this, nodeName, errorMessage)) + return node == m_root ? true : node->expandRunComplexDumpers(ctx, errorMessage); + return false; } // Cast an (unexpanded) node bool SymbolGroup::typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage) { - SymbolGroupNode *node = find(iname); - if (!node) { + AbstractSymbolGroupNode *aNode = find(iname); + if (!aNode) { *errorMessage = msgNotFound(iname); return false; } - if (node == m_root) { + if (aNode == m_root) { *errorMessage = "Cannot cast root node"; return false; } + SymbolGroupNode *node = aNode->asSymbolGroupNode(); + if (!node) { + *errorMessage = "Node type error in typeCast: " + iname; + return false; + } return node->typeCast(desiredType, errorMessage); } @@ -1198,9 +399,9 @@ void SymbolGroup::markUninitialized(const std::vector &uniniNodes) const std::vector::const_iterator unIniNodesBegin = uniniNodes.begin(); const std::vector::const_iterator unIniNodesEnd = uniniNodes.end(); - const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_root->children().end(); - for (SymbolGroupNodePtrVector::const_iterator it = m_root->children().begin(); it != childrenEnd; ++it) { - if (std::find(unIniNodesBegin, unIniNodesEnd, (*it)->fullIName()) != unIniNodesEnd) + const AbstractSymbolGroupNodePtrVector::const_iterator childrenEnd = m_root->children().end(); + for (AbstractSymbolGroupNodePtrVector::const_iterator it = m_root->children().begin(); it != childrenEnd; ++it) { + if (std::find(unIniNodesBegin, unIniNodesEnd, (*it)->absoluteFullIName()) != unIniNodesEnd) (*it)->addFlags(SymbolGroupNode::Uninitialized); } } @@ -1218,11 +419,17 @@ static inline std::string msgAssignError(const std::string &nodeName, bool SymbolGroup::assign(const std::string &nodeName, const std::string &value, std::string *errorMessage) { - SymbolGroupNode *node = find(nodeName); - if (node == 0) { + AbstractSymbolGroupNode *aNode = find(nodeName); + if (aNode == 0) { *errorMessage = msgAssignError(nodeName, value, "No such node"); return false; } + SymbolGroupNode *node = aNode->asSymbolGroupNode(); + if (node == 0) { + *errorMessage = msgAssignError(nodeName, value, "Invalid node type"); + return false; + } + const HRESULT hr = m_symbolGroup->WriteSymbol(node->index(), const_cast(value.c_str())); if (FAILED(hr)) { *errorMessage = msgAssignError(nodeName, value, msgDebugEngineComFailed("WriteSymbol", hr)); @@ -1235,15 +442,15 @@ bool SymbolGroup::accept(SymbolGroupNodeVisitor &visitor) const { if (!m_root || m_root->children().empty()) return false; - return m_root->accept(visitor, 0, 0); + return m_root->accept(visitor, std::string(), 0, 0); } // Find "locals.this.i1" and move index recursively -static SymbolGroupNode *findNodeRecursion(const std::vector &iname, - unsigned depth, - std::vector nodes) +static AbstractSymbolGroupNode *findNodeRecursion(const std::vector &iname, + unsigned depth, + std::vector nodes) { - typedef std::vector::const_iterator ConstIt; + typedef std::vector::const_iterator ConstIt; if (debug > 1) { DebugPrint() <<"findNodeRecursion: Looking for " << iname.back() << " (" << iname.size() @@ -1255,7 +462,7 @@ static SymbolGroupNode *findNodeRecursion(const std::vector &iname, // Find the child that matches the iname part at depth const ConstIt cend = nodes.end(); for (ConstIt it = nodes.begin(); it != cend; ++it) { - SymbolGroupNode *c = *it; + AbstractSymbolGroupNode *c = *it; if (c->iName() == iname.at(depth)) { if (depth == iname.size() - 1) { // Complete iname matched->happy. return c; @@ -1268,7 +475,7 @@ static SymbolGroupNode *findNodeRecursion(const std::vector &iname, return 0; } -SymbolGroupNode *SymbolGroup::findI(const std::string &iname) const +AbstractSymbolGroupNode *SymbolGroup::findI(const std::string &iname) const { if (iname.empty()) return 0; @@ -1277,7 +484,7 @@ SymbolGroupNode *SymbolGroup::findI(const std::string &iname) const return m_root; std::vector inameTokens; - split(iname, '.', std::back_inserter(inameTokens)); + split(iname, SymbolGroupNodeVisitor::iNamePathSeparator, std::back_inserter(inameTokens)); // Must begin with root if (inameTokens.front() != m_root->name()) @@ -1287,74 +494,10 @@ SymbolGroupNode *SymbolGroup::findI(const std::string &iname) const return findNodeRecursion(inameTokens, 1, m_root->children()); } -SymbolGroupNode *SymbolGroup::find(const std::string &iname) const +AbstractSymbolGroupNode *SymbolGroup::find(const std::string &iname) const { - SymbolGroupNode *rc = findI(iname); + AbstractSymbolGroupNode *rc = findI(iname); if (::debug > 1) DebugPrint() << "SymbolGroup::find " << iname << ' ' << rc; return rc; } - -// --------- DebugSymbolGroupNodeVisitor -DebugSymbolGroupNodeVisitor::DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity) : - m_os(os), m_verbosity(verbosity) -{ -} - -SymbolGroupNodeVisitor::VisitResult - DebugSymbolGroupNodeVisitor::visit(SymbolGroupNode *node, - unsigned /* child */, unsigned depth) -{ - node->debug(m_os, m_verbosity, depth); - return VisitContinue; -} - -// --------------------- DumpSymbolGroupNodeVisitor -DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os, - const SymbolGroupValueContext &context, - const DumpParameters ¶meters) : - m_os(os), m_context(context), m_parameters(parameters), - m_visitChildren(false),m_lastDepth(unsigned(-1)) -{ -} - -SymbolGroupNodeVisitor::VisitResult - DumpSymbolGroupNodeVisitor::visit(SymbolGroupNode *node, unsigned /* child */, unsigned depth) -{ - // Show container children only - if (node->flags() & SymbolGroupNode::Obscured) - return VisitSkipChildren; - // Recurse to children only if expanded by explicit watchmodel request - // and initialized. - const unsigned flags = node->flags(); - m_visitChildren = node->isExpanded() - && (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0; - - // Comma between same level children given obscured children - if (depth == m_lastDepth) { - m_os << ','; - } else { - m_lastDepth = depth; - } - if (m_parameters.humanReadable()) { - m_os << '\n'; - indentStream(m_os, depth * 2); - } - m_os << '{'; - node->dump(m_os, m_parameters, m_context); - if (m_visitChildren) { // open children array - m_os << ",children=["; - } else { // No children, close array. - m_os << '}'; - } - if (m_parameters.humanReadable()) - m_os << '\n'; - return m_visitChildren ? VisitContinue : VisitSkipChildren; -} - -void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *n, unsigned) -{ - m_os << "]}"; // Close children array and self - if (m_parameters.humanReadable()) - m_os << " /* end of '" << n->fullIName() << "' */\n"; -} diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h index 59911642532..ce042ae7f3b 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.h +++ b/src/libs/qtcreatorcdbext/symbolgroup.h @@ -31,197 +31,7 @@ #define SYMBOLGROUP_H #include "common.h" - -#include -#include -#include -#include - -std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p); - -class SymbolGroupNodeVisitor; -class SymbolGroup; -struct SymbolGroupValueContext; - -// All parameters for dumping in one struct. -struct DumpParameters -{ - typedef std::map FormatMap; // type or iname to format - enum DumpFlags - { - DumpHumanReadable = 0x1, - DumpComplexDumpers = 0x2 - }; - - DumpParameters(); - bool humanReadable() const { return dumpFlags & DumpHumanReadable; } - // Helper to decode format option arguments. - static FormatMap decodeFormatArgument(const std::string &f); - - bool recode(const std::string &type, const std::string &iname, - const SymbolGroupValueContext &ctx, - std::wstring *value, int *encoding) const; - int format(const std::string &type, const std::string &iname) const; - - unsigned dumpFlags; - FormatMap typeFormats; - FormatMap individualFormats; -}; - -/* Thin wrapper around a symbol group entry. Provides accessors for fixed-up - * symbol group value and a dumping facility consisting of: - * - 'Simple' dumping done when running the DumpVisitor. This produces one - * line of formatted output shown for the class. These values - * values should are displayed, while still allowing for expansion of the structure - * in the debugger. - * It also pre-determines some information for complex dumping (type, container). - * - 'Complex' dumping: Obscures the symbol group children by fake children, for - * example container children, run when calling SymbolGroup::dump with an iname. - * The fake children are appended to the child list (other children are just marked as - * obscured for GDBMI dumping so that SymbolGroupValue expressions still work as before). - * The dumping is mostly based on SymbolGroupValue expressions. - * in the debugger. Evaluating those dumpers might expand symbol nodes, which are - * then marked as 'ExpandedByDumper'. This stops the dump recursion to prevent - * outputting data that were not explicitly expanded by the watch handler. */ - -class SymbolGroupNode { - SymbolGroupNode(const SymbolGroupNode&); - SymbolGroupNode& operator=(const SymbolGroupNode&); - - explicit SymbolGroupNode(SymbolGroup *symbolGroup, - ULONG index, - const std::string &name, - const std::string &iname, - SymbolGroupNode *parent = 0); - -public: - enum Flags { - Uninitialized = 0x1, - SimpleDumperNotApplicable = 0x2, // No dumper available for type - SimpleDumperOk = 0x4, // Internal dumper ran, value set - SimpleDumperFailed = 0x8, // Internal dumper failed - SimpleDumperMask = SimpleDumperNotApplicable|SimpleDumperOk|SimpleDumperFailed, - ExpandedByDumper = 0x10, - AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible - Obscured = 0x40, // Symbol is obscured by (for example) fake container children - ComplexDumperOk = 0x80 - }; - - typedef std::vector SymbolParameterVector; - typedef std::vector SymbolGroupNodePtrVector; - typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator; - typedef SymbolGroupNodePtrVector::const_iterator SymbolGroupNodePtrVectorConstIterator; - - ~SymbolGroupNode() { removeChildren(); } - - // Indicate reference - void setReferencedBy(SymbolGroupNode *n); - - void removeChildren(); - void parseParameters(SymbolParameterVector::size_type index, - SymbolParameterVector::size_type parameterOffset, - const SymbolParameterVector &vec); - - static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec); - // For root nodes, only: Add a new symbol by name - SymbolGroupNode *addSymbolByName(const std::string &name, // Expression like 'myarray[1]' - const std::string &iname, // Desired iname, defaults to name - std::string *errorMessage); - - const std::string &name() const { return m_name; } - std::string fullIName() const; - const std::string &iName() const { return m_iname; } - - const SymbolGroupNodePtrVector &children() const { return m_children; } - SymbolGroupNode *childAt(unsigned) const; - - unsigned indexByIName(const char *) const; // (unsigned(-1) on failure - SymbolGroupNode *childByIName(const char *) const; - - const SymbolGroupNode *parent() const { return m_parent; } - const SymbolGroupNode *referencedParent() const { return m_referencedBy ? m_referencedBy : m_parent; } - SymbolGroup *symbolGroup() const { return m_symbolGroup; } - - // I/O: Gdbmi dump for Visitors - void dump(std::ostream &str, const DumpParameters &p, const SymbolGroupValueContext &ctx); - // I/O: debug for Visitors - void debug(std::ostream &os, unsigned verbosity, unsigned depth) const; - - std::wstring symbolGroupRawValue() const; - std::wstring symbolGroupFixedValue() const; - - std::string type() const; - int dumperType() const { return m_dumperType; } // Valid after dumper run - int dumperContainerSize() { return m_dumperContainerSize; } // Valid after dumper run - unsigned size() const; // Size of value - ULONG64 address() const; - - bool accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth); - - bool expand(std::string *errorMessage); - bool isExpanded() const { return !m_children.empty(); } - bool canExpand() const { return m_parameters.SubElements > 0; } - void runComplexDumpers(const SymbolGroupValueContext &ctx); - // Cast to a different type. Works only on unexpanded nodes - bool typeCast(const std::string &desiredType, std::string *errorMessage); - - ULONG subElements() const { return m_parameters.SubElements; } - ULONG index() const { return m_index; } - - unsigned flags() const { return m_flags; } - void setFlags(unsigned f) { m_flags = f; } - void addFlags(unsigned f) { m_flags |= f; } - void clearFlags(unsigned f) { m_flags &= ~f; } - -private: - bool isArrayElement() const; - // Notify about expansion of a node, shift indexes - bool notifyExpanded(ULONG index, ULONG insertedCount); - std::wstring simpleDumpValue(const SymbolGroupValueContext &ctx, - const DumpParameters &p); - - SymbolGroup *const m_symbolGroup; - SymbolGroupNode *m_parent; - // Indicates a fake child (container value). Used for the full iname - SymbolGroupNode *m_referencedBy; - ULONG m_index; - DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct. - SymbolGroupNodePtrVector m_children; - const std::string m_name; - const std::string m_iname; - unsigned m_flags; - std::wstring m_dumperValue; - int m_dumperType; - int m_dumperContainerSize; -}; - -/* Visitor that takes care of iterating over the nodes - * visit() is not called for the (invisible) root node, but starting with the - * root's children with depth=0. - * Return true from visit() to terminate the recursion. */ - -class SymbolGroupNodeVisitor { - SymbolGroupNodeVisitor(const SymbolGroupNodeVisitor&); - SymbolGroupNodeVisitor& operator=(const SymbolGroupNodeVisitor&); - - friend class SymbolGroupNode; -protected: - SymbolGroupNodeVisitor() {} -public: - virtual ~SymbolGroupNodeVisitor() {} - -protected: - enum VisitResult { - VisitContinue, - VisitSkipChildren, - VisitStop - }; - -private: - virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth) = 0; - // Helper for formatting output. - virtual void childrenVisited(const SymbolGroupNode * /* node */, unsigned /* depth */) {} -}; +#include "symbolgroupnode.h" // Thin wrapper around a symbol group storing a tree of expanded symbols rooted on // a fake "locals" root element. @@ -233,15 +43,15 @@ public: typedef std::vector SymbolParameterVector; private: + SymbolGroup(const SymbolGroup &); + SymbolGroup &operator=(const SymbolGroup &); + explicit SymbolGroup(CIDebugSymbolGroup *, const SymbolParameterVector &vec, ULONG threadId, unsigned frame); - SymbolGroup(const SymbolGroup &); - SymbolGroup &operator=(const SymbolGroup &); - public: - typedef SymbolGroupNode::SymbolGroupNodePtrVector SymbolGroupNodePtrVector; + typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector; static SymbolGroup *create(CIDebugControl *control, CIDebugSymbols *, @@ -262,17 +72,22 @@ public: ULONG threadId() const { return m_threadId; } SymbolGroupNode *root() { return m_root; } const SymbolGroupNode *root() const { return m_root; } - SymbolGroupNode *find(const std::string &iname) const; - - // Expand a node list "locals.i1,locals.i2", expanding all nested child nodes - // (think mkdir -p). - unsigned expandList(const std::vector &nodes, std::string *errorMessage); - // Mark uninitialized (top level only) - void markUninitialized(const std::vector &nodes); + AbstractSymbolGroupNode *find(const std::string &iname) const; // Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible // (think mkdir without -p). bool expand(const std::string &node, std::string *errorMessage); + bool expandRunComplexDumpers(const std::string &node, const SymbolGroupValueContext &ctx, std::string *errorMessage); + // Expand a node list "locals.i1,locals.i2", expanding all nested child nodes + // (think mkdir -p). + unsigned expandList(const std::vector &nodes, std::string *errorMessage); + unsigned expandListRunComplexDumpers(const std::vector &nodes, + const SymbolGroupValueContext &ctx, + std::string *errorMessage); + + // Mark uninitialized (top level only) + void markUninitialized(const std::vector &nodes); + // Cast an (unexpanded) node bool typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage); // Add a symbol by name expression @@ -296,7 +111,7 @@ public: std::string *errorMessage); private: - inline SymbolGroupNode *findI(const std::string &iname) const; + inline AbstractSymbolGroupNode *findI(const std::string &iname) const; static bool getSymbolParameters(CIDebugSymbolGroup *m_symbolGroup, SymbolParameterVector *vec, std::string *errorMessage); @@ -307,34 +122,4 @@ private: SymbolGroupNode *m_root; }; -// Debug output visitor. -class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor { -public: - explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0); - -private: - virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth); - - std::ostream &m_os; - const unsigned m_verbosity; -}; - -// Gdbmi dump output visitor. -class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor { -public: - explicit DumpSymbolGroupNodeVisitor(std::ostream &os, - const SymbolGroupValueContext &context, - const DumpParameters ¶meters = DumpParameters()); - -private: - virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth); - virtual void childrenVisited(const SymbolGroupNode * node, unsigned depth); - - std::ostream &m_os; - const SymbolGroupValueContext &m_context; - const DumpParameters &m_parameters; - bool m_visitChildren; - unsigned m_lastDepth; -}; - #endif // SYMBOLGROUP_H diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp new file mode 100644 index 00000000000..7948388de2f --- /dev/null +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp @@ -0,0 +1,1089 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "symbolgroupnode.h" +#include "symbolgroup.h" +#include "symbolgroupvalue.h" +#include "stringutils.h" +#include "base64.h" +#include "containers.h" + +#include + +enum { debug = 0 }; + +typedef std::vector::size_type VectorIndexType; +typedef std::vector StringVector; + +enum { BufSize = 2048 }; + +static inline void indentStream(std::ostream &str, unsigned depth) +{ + for (unsigned d = 0; d < depth; d++) + str << " "; +} + +// -------------- AbstractSymbolGroupNode + +AbstractSymbolGroupNode::AbstractSymbolGroupNode(const std::string &name, + const std::string &iname) : + m_name(name), m_iname(iname), m_parent(0), m_flags(0) +{ +} + +AbstractSymbolGroupNode::~AbstractSymbolGroupNode() +{ +} + +std::string AbstractSymbolGroupNode::absoluteFullIName() const +{ + std::string rc = iName(); + for (const AbstractSymbolGroupNode *p = m_parent; p; p = p->m_parent) { + rc.insert(0, 1, SymbolGroupNodeVisitor::iNamePathSeparator); + rc.insert(0, p->iName()); + } + return rc; +} + +AbstractSymbolGroupNode *AbstractSymbolGroupNode::childAt(unsigned i) const +{ + const AbstractSymbolGroupNodePtrVector &c = children(); + return i < c.size() ? c.at(i) : static_cast(0); +} + +unsigned AbstractSymbolGroupNode::indexByIName(const char *n) const +{ + const AbstractSymbolGroupNodePtrVector &c = children(); + const VectorIndexType size = c.size(); + for (VectorIndexType i = 0; i < size; i++) + if ( c.at(i)->iName() == n ) + return unsigned(i); + return unsigned(-1); +} + +AbstractSymbolGroupNode *AbstractSymbolGroupNode::childByIName(const char *n) const +{ + const unsigned index = indexByIName(n); + if (index != unsigned(-1)) + return children().at(index); + return 0; +} + +bool AbstractSymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, + const std::string &parentIname, + unsigned child, unsigned depth) +{ + // If we happen to be the root node, just skip over + const bool invisibleRoot = !m_parent; + const unsigned childDepth = invisibleRoot ? 0 : depth + 1; + + std::string fullIname = parentIname; + if (!fullIname.empty()) + fullIname.push_back(SymbolGroupNodeVisitor::iNamePathSeparator); + fullIname += m_iname; + + const SymbolGroupNodeVisitor::VisitResult vr = + invisibleRoot ? SymbolGroupNodeVisitor::VisitContinue : + visitor.visit(this, fullIname, child, depth); + switch (vr) { + case SymbolGroupNodeVisitor::VisitStop: + return true; + case SymbolGroupNodeVisitor::VisitSkipChildren: + break; + case SymbolGroupNodeVisitor::VisitContinue: { + const AbstractSymbolGroupNodePtrVector &c = children(); + const unsigned childCount = unsigned(c.size()); + for (unsigned i = 0; i < childCount; i++) + if (c.at(i)->accept(visitor, fullIname, i, childDepth)) + return true; + if (!invisibleRoot) + visitor.childrenVisited(this, depth); + } + break; + } + return false; +} + +void AbstractSymbolGroupNode::debug(std::ostream &str, const std::string &visitingFullIname, + unsigned /* verbosity */, unsigned depth) const +{ + indentStream(str, 2 * depth); + str << "AbstractSymbolGroupNode " << visitingFullIname + << " with " << children().size() << " children\n"; +} + +void AbstractSymbolGroupNode::dumpBasicData(std::ostream &str, const std::string &aName, + const std::string &aFullIname, + const std::string &type /* = "" */, + const std::string &expression /* = "" */) +{ + str << "iname=\"" << aFullIname << "\",name=\"" << aName << '"'; + if (!type.empty()) + str << ",type=\"" << type << '"'; + if (!expression.empty()) + str << ",exp=\"" << expression << '"'; +} + +void AbstractSymbolGroupNode::setParent(AbstractSymbolGroupNode *n) +{ + if (m_parent) + dprintf("Internal error: Attempt to change non-null parent of %s", m_name.c_str()); + m_parent = n; +} + +// -------- BaseSymbolGroupNode +BaseSymbolGroupNode::BaseSymbolGroupNode(const std::string &name, const std::string &iname) : + AbstractSymbolGroupNode(name, iname) +{ +} + +BaseSymbolGroupNode::~BaseSymbolGroupNode() +{ + removeChildren(); +} + +void BaseSymbolGroupNode::removeChildren() +{ + if (!m_children.empty()) { + const AbstractSymbolGroupNodePtrVectorIterator end = m_children.end(); + for (AbstractSymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it) + delete *it; + m_children.clear(); + } +} + +void BaseSymbolGroupNode::addChild(AbstractSymbolGroupNode *c) +{ + c->setParent(this); + m_children.push_back(c); +} + +// ----------- Helpers: Stream DEBUG_SYMBOL_PARAMETERS + +std::ostream &operator<<(std::ostream &str, const DEBUG_SYMBOL_PARAMETERS ¶meters) +{ + str << "parent="; + if (parameters.ParentSymbol == DEBUG_ANY_ID) { + str << "DEBUG_ANY_ID"; + } else { + str << parameters.ParentSymbol ; + } + if (parameters.Flags != 0 && parameters.Flags != 1) + str << " flags=" << parameters.Flags; + // Detailed flags: + if (parameters.Flags & DEBUG_SYMBOL_EXPANDED) + str << " EXPANDED"; + if (parameters.Flags & DEBUG_SYMBOL_READ_ONLY) + str << " READONLY"; + if (parameters.Flags & DEBUG_SYMBOL_IS_ARRAY) + str << " ARRAY"; + if (parameters.Flags & DEBUG_SYMBOL_IS_FLOAT) + str << " FLOAT"; + if (parameters.Flags & DEBUG_SYMBOL_IS_ARGUMENT) + str << " ARGUMENT"; + if (parameters.Flags & DEBUG_SYMBOL_IS_LOCAL) + str << " LOCAL"; + str << " typeId=" << parameters.TypeId; + if (parameters.SubElements) + str << " subElements=" << parameters.SubElements; + return str; +} + +// --------------- DumpParameters +DumpParameters::DumpParameters() : dumpFlags(0) +{ +} + +// typeformats: decode hex-encoded name, value pairs: +// '414A=2,...' -> map of "AB:2". +DumpParameters::FormatMap DumpParameters::decodeFormatArgument(const std::string &f) +{ + FormatMap rc; + const std::string::size_type size = f.size(); + // Split 'hexname=4,' + for (std::string::size_type pos = 0; pos < size ; ) { + // Cut out key + const std::string::size_type equalsPos = f.find('=', pos); + if (equalsPos == std::string::npos) + return rc; + const std::string name = stringFromHex(f.c_str() + pos, f.c_str() + equalsPos); + // Search for number + const std::string::size_type numberPos = equalsPos + 1; + std::string::size_type nextPos = f.find(',', numberPos); + if (nextPos == std::string::npos) + nextPos = size; + int format; + if (!integerFromString(f.substr(numberPos, nextPos - numberPos), &format)) + return rc; + rc.insert(FormatMap::value_type(name, format)); + pos = nextPos + 1; + } + return rc; +} + +int DumpParameters::format(const std::string &type, const std::string &iname) const +{ + if (!individualFormats.empty()) { + const FormatMap::const_iterator iit = individualFormats.find(iname); + if (iit != individualFormats.end()) + return iit->second; + } + if (!typeFormats.empty()) { + const FormatMap::const_iterator tit = typeFormats.find(type); + if (tit != typeFormats.end()) + return tit->second; + } + return -1; +} + +enum PointerFormats // Watch data pointer format requests +{ + FormatAuto = 0, + FormatLatin1String = 1, + FormatUtf8String = 2, + FormatUtf16String = 3, + FormatUcs4String = 4 +}; + +enum DumpEncoding // WatchData encoding of GDBMI values +{ + DumpEncodingAscii = 0, + DumpEncodingBase64 = 1, + DumpEncodingBase64_Utf16 = 2, + DumpEncodingBase64_Ucs4 = 3, + DumpEncodingHex_Latin1 = 6, + DumpEncodingHex_Utf16 = 7, + DumpEncodingHex_Ucs4_LittleEndian = 8, + DumpEncodingHex_Utf8_LittleEndian = 9, + DumpEncodingHex_Ucs4_BigEndian = 10, + DumpEncodingHex_Utf16_BigEndian = 11, + DumpEncodingHex_Utf16_LittleEndian = 12 +}; + +/* Recode arrays/pointers of char*, wchar_t according to users + * sepcification. Handles char formats for 'char *', '0x834478 "hallo.."' + * and 'wchar_t *', '0x834478 "hallo.."'. + * This is done by retrieving the address and the length (in characters) + * of the CDB output, converting it to memory size, fetching the data + * from memory, zero-terminating and recoding it using the encoding + * defined in watchutils.cpp. + * As a special case, if there is no user-defined format and the + * CDB output contains '?' indicating non-printable characters, + * append a hex dump of the memory (auto-format). */ + +bool DumpParameters::recode(const std::string &type, + const std::string &iname, + const SymbolGroupValueContext &ctx, + std::wstring *value, int *encoding) const +{ + // We basically handle char formats for 'char *', '0x834478 "hallo.."' + // and 'wchar_t *', '0x834478 "hallo.."' + // Determine address and length from the pointer value output, + // read the raw memory and recode if that is possible. + if (type.empty() || type.at(type.size() - 1) != '*') + return false; + const int newFormat = format(type, iname); + if (value->compare(0, 2, L"0x")) + return false; + const std::wstring::size_type quote1 = value->find(L'"', 2); + if (quote1 == std::wstring::npos) + return false; + // The user did not specify any format, still, there are '?' + // (indicating non-printable) in what the debugger prints. In that case, + // append a hex dump to the normal output. If there are no '?'-> all happy. + if (newFormat < FormatLatin1String && value->find(L'?', quote1 + 1) == std::wstring::npos) + return false; + const std::wstring::size_type quote2 = value->find(L'"', quote1 + 1); + if (quote2 == std::wstring::npos) + return false; + std::wstring::size_type length = quote2 - quote1 - 1; + if (!length) + return false; + // Get address from value + ULONG64 address = 0; + if (!integerFromWString(value->substr(0, quote1 - 1), &address) || !address) + return false; + // Get real size if this is for example a wchar_t *. + const unsigned elementSize = SymbolGroupValue::sizeOf(SymbolGroupValue::stripPointerType(type).c_str()); + if (!elementSize) + return false; + length *= elementSize; + // Allocate real length + 8 bytes ('\0') for largest format (Ucs4). + // '\0' is not listed in the CDB output. + const std::wstring::size_type allocLength = length + 8; + unsigned char *buffer = new unsigned char[allocLength]; + std::fill(buffer, buffer + allocLength, 0); + ULONG obtained = 0; + if (FAILED(ctx.dataspaces->ReadVirtual(address, buffer, ULONG(length), &obtained))) { + delete [] buffer; + return false; + } + // Recode raw memory + switch (newFormat) { + case FormatLatin1String: + *value = dataToHexW(buffer, buffer + length + 1); // Latin1 + 0 + *encoding = DumpEncodingHex_Latin1; + break; + case FormatUtf8String: + *value = dataToHexW(buffer, buffer + length + 1); // UTF8 + 0 + *encoding = DumpEncodingHex_Utf8_LittleEndian; + break; + case FormatUtf16String: // Paranoia: make sure buffer is terminated at 2 byte borders + if (length % 2) { + length &= ~1; + buffer[length] = '\0'; + buffer[length + 1] = '\0'; + } + *value = base64EncodeToWString(buffer, length + 2); + *encoding = DumpEncodingBase64_Utf16; + break; + case FormatUcs4String: // Paranoia: make sure buffer is terminated at 4 byte borders + if (length % 4) { + length &= ~3; + std::fill(buffer + length, buffer + length + 4, 0); + } + *value = dataToHexW(buffer, buffer + length + 2); // UTF16 + 0 + *encoding = DumpEncodingHex_Ucs4_LittleEndian; + break; + default: // See above, append hex dump + value->push_back(' '); + value->append(dataToReadableHexW(buffer, buffer + length)); + *encoding = DumpEncodingAscii; + break; + } + delete [] buffer; + return true; +} + +// ------- SymbolGroupNode + +SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup, + ULONG index, + const std::string &name, + const std::string &iname) : + BaseSymbolGroupNode(name, iname), + m_symbolGroup(symbolGroup), + m_index(index), m_dumperType(-1), m_dumperContainerSize(-1) +{ + memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS)); + m_parameters.ParentSymbol = DEBUG_ANY_ID; +} + +const SymbolGroupNode *SymbolGroupNode::symbolGroupNodeParent() const +{ + if (const AbstractSymbolGroupNode *p = parent()) + return p->asSymbolGroupNode(); + return 0; +} + +bool SymbolGroupNode::isArrayElement() const +{ + if (const SymbolGroupNode *p = symbolGroupNodeParent()) + return (p->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY) != 0; + return false; +} + +// Notify about expansion of a node: +// Adapt our index and those of our children if we are behind it. +// Return true if a modification was required to be able to terminate the +// recursion. +bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount) +{ + typedef AbstractSymbolGroupNodePtrVector::const_reverse_iterator ReverseIt; + // Looping backwards over the children. If a subtree has no modifications, + // (meaning all other indexes are smaller) we can stop. + const ReverseIt rend = children().rend(); + for (ReverseIt it = children().rbegin(); it != rend; ++it) { + if (SymbolGroupNode *c = (*it)->asSymbolGroupNode()) + if (!c->notifyExpanded(index, insertedCount)) + return false; + } + + // Correct our own + parent index if applicable. + if (m_index == DEBUG_ANY_ID || m_index < index) + return false; + + m_index += insertedCount; + if (m_parameters.ParentSymbol != DEBUG_ANY_ID && m_parameters.ParentSymbol >= index) + m_parameters.ParentSymbol += insertedCount; + return true; +} + +// Fix names: fix complicated template base names +static inline void fixName(std::string *name) +{ + // Long template base classes 'std::tree_base' -> 'std::tree<>' + // for nice display + const std::string::size_type templatePos = name->find('<'); + if (templatePos != std::string::npos) { + name->erase(templatePos + 1, name->size() - templatePos - 1); + name->push_back('>'); + } +} + +// Fix inames: arrays and long, complicated template base names +static inline void fixIname(unsigned &id, std::string *iname) +{ + // Fix array iname "[0]" -> "0" for sorting to work correctly + if (!iname->empty() && iname->at(0) == '[') { + const std::string::size_type last = iname->size() - 1; + if (iname->at(last) == ']') { + iname->erase(last, 1); + iname->erase(0, 1); + return; + } + } + // Long template base classes 'std::tree_base 'tree@t1', + // usable as identifier and command line parameter + const std::string::size_type templatePos = iname->find('<'); + if (templatePos != std::string::npos) { + iname->erase(templatePos, iname->size() - templatePos); + if (iname->compare(0, 5, "std::") == 0) + iname->erase(0, 5); + iname->append("@t"); + iname->append(toString(id++)); + } +} + +// Fix up names and inames +static inline void fixNames(bool isTopLevel, StringVector *names, StringVector *inames) +{ + if (names->empty()) + return; + unsigned unnamedId = 1; + unsigned templateId = 1; + /* 1) Fix name="__formal", which occurs when someone writes "void foo(int /* x * /)..." + * 2) Fix array inames for sorting: "[6]" -> name="[6]",iname="6" + * 3) For toplevels: Fix shadowed variables in the order the debugger expects them: + \code + int x; // Occurrence (1), should be reported as name="x "/iname="x#1" + if (true) { + int x = 5; (2) // Occurrence (2), should be reported as name="x"/iname="x" + } + \endcode */ + StringVector::iterator nameIt = names->begin(); + const StringVector::iterator namesEnd = names->end(); + for (StringVector::iterator iNameIt = inames->begin(); nameIt != namesEnd ; ++nameIt, ++iNameIt) { + std::string &name = *nameIt; + std::string &iname = *iNameIt; + if (name.empty() || name == "__formal") { + const std::string number = toString(unnamedId++); + name = "'; + iname = "unnamed#" + number; + } else { + fixName(&name); + fixIname(templateId, &iname); + } + if (isTopLevel) { + if (const StringVector::size_type shadowCount = std::count(nameIt + 1, namesEnd, name)) { + const std::string number = toString(shadowCount); + name += " 1) + reserveChildren(m_parameters.SubElements); + + const VectorIndexType size = vec.size(); + // Scan the top level elements + StringVector names; + names.reserve(size); + // Pass 1) Determine names. We need the complete set first in order to do some corrections. + const VectorIndexType startIndex = isTopLevel ? 0 : index + 1; + for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) { + if (vec.at(pos).ParentSymbol == index) { + const VectorIndexType symbolGroupIndex = pos + parameterOffset; + if (FAILED(m_symbolGroup->debugSymbolGroup()->GetSymbolName(ULONG(symbolGroupIndex), buf, BufSize, &obtainedSize))) + buf[0] = '\0'; + names.push_back(std::string(buf)); + } + } + // 2) Fix names + StringVector inames = names; + fixNames(isTopLevel, &names, &inames); + // Pass 3): Add nodes with fixed names + StringVector::size_type nameIndex = 0; + for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) { + if (vec.at(pos).ParentSymbol == index) { + const VectorIndexType symbolGroupIndex = pos + parameterOffset; + SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup, + ULONG(symbolGroupIndex), + names.at(nameIndex), + inames.at(nameIndex)); + child->parseParameters(symbolGroupIndex, parameterOffset, vec); + addChild(child); + nameIndex++; + } + } + if (isTopLevel) + m_parameters.SubElements = ULONG(children().size()); +} + +SymbolGroupNode *SymbolGroupNode::create(SymbolGroup *sg, const std::string &name, const SymbolGroup::SymbolParameterVector &vec) +{ + SymbolGroupNode *rc = new SymbolGroupNode(sg, DEBUG_ANY_ID, name, name); + rc->parseParameters(DEBUG_ANY_ID, 0, vec); + return rc; +} + +// Fix some oddities in CDB values + +static inline bool isHexDigit(wchar_t c) +{ + return (c >= L'0' && c <= L'9') || (c >= L'a' && c <= L'f') || (c >= L'A' && c <= L'F'); +} + +static void fixValue(const std::string &type, std::wstring *value) +{ + // Pointers/Unsigned integers: fix '0x00000000`00000AD bla' ... to "0xAD bla" + const bool isHexNumber = value->size() > 3 && value->compare(0, 2, L"0x") == 0 && isHexDigit(value->at(2)); + if (isHexNumber) { + // Remove dumb 64bit separator + if (value->size() > 10 && value->at(10) == L'`') + value->erase(10, 1); + const std::string::size_type firstNonNullDigit = value->find_first_not_of(L"0", 2); + // No on-null digits: plain null ptr. + if (firstNonNullDigit == std::string::npos || value->at(firstNonNullDigit) == ' ') { + *value = L"0x0"; + } else { + // Strip + if (firstNonNullDigit > 2) + value->erase(2, firstNonNullDigit - 2); + } + } + + // Pointers: fix '0x00000000`00000AD class bla' ... to "0xAD", but leave + // 'const char *' values as is ('0x00000000`00000AD "hallo"). + if (!type.empty() && type.at(type.size() - 1) == L'*') { + // Strip ' Class bla" + std::wstring::size_type classPos = value->find(L" struct", 2); + if (classPos == std::string::npos) + classPos = value->find(L" class", 2); + if (classPos != std::string::npos) + value->erase(classPos, value->size() - classPos); + return; + } + + // unsigned hex ints that are not pointers: Convert to decimal as not to confuse watch model: + if (isHexNumber) { + ULONG64 uv; + std::wistringstream str(*value); + str >> std::hex >> uv; + if (!str.fail()) { + *value = toWString(uv); + return; + } + } + + // Integers: fix '0n10' -> '10' + if (value->size() >= 3 && value->compare(0, 2, L"0n") == 0 + && (isdigit(value->at(2)) || value->at(2) == L'-')) { + value->erase(0, 2); + return; + } + // Fix long class names on std containers 'class std::tree<...>' -> 'class std::tree<>' + if (value->compare(0, 6, L"class ") == 0 || value->compare(0, 7, L"struct ") == 0) { + const std::string::size_type openTemplate = value->find(L'<'); + if (openTemplate != std::string::npos) { + value->erase(openTemplate + 1, value->size() - openTemplate - 2); + return; + } + } +} + +// Check for ASCII-encode-able stuff. Plain characters + tabs at the most, no newline. +static bool isSevenBitClean(const wchar_t *buf, size_t size) +{ + const wchar_t *bufEnd = buf + size; + for (const wchar_t *bufPtr = buf; bufPtr < bufEnd; bufPtr++) { + const wchar_t c = *bufPtr; + if (c > 127 || (c < 32 && c != 9)) + return false; + } + return true; +} + +std::string SymbolGroupNode::type() const +{ + static char buf[BufSize]; + const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolTypeName(m_index, buf, BufSize, NULL); + return SUCCEEDED(hr) ? std::string(buf) : std::string(); +} + +unsigned SymbolGroupNode::size() const +{ + DEBUG_SYMBOL_ENTRY entry; + if (SUCCEEDED(m_symbolGroup->debugSymbolGroup()->GetSymbolEntryInformation(m_index, &entry))) + return entry.Size; + return 0; +} + +ULONG64 SymbolGroupNode::address() const +{ + ULONG64 address = 0; + const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolOffset(m_index, &address); + if (SUCCEEDED(hr)) + return address; + return 0; +} + +std::wstring SymbolGroupNode::symbolGroupRawValue() const +{ + // Determine size and return allocated buffer + const ULONG maxValueSize = 262144; + ULONG obtainedSize = 0; + HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, NULL, maxValueSize, &obtainedSize); + if (FAILED(hr)) + return std::wstring(); + if (obtainedSize > maxValueSize) + obtainedSize = maxValueSize; + wchar_t *buffer = new wchar_t[obtainedSize]; + hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, buffer, obtainedSize, &obtainedSize); + if (FAILED(hr)) // Whoops, should not happen + buffer[0] = 0; + const std::wstring rc(buffer); + delete [] buffer; + return rc; +} + +std::wstring SymbolGroupNode::symbolGroupFixedValue() const +{ + std::wstring value = symbolGroupRawValue(); + fixValue(type(), &value); + return value; +} + +// Complex dumpers: Get container/fake children +void SymbolGroupNode::runComplexDumpers(const SymbolGroupValueContext &ctx) +{ + if (m_dumperContainerSize <= 0 || (testFlags(ComplexDumperOk) || !testFlags(SimpleDumperOk))) + return; + setFlags(ComplexDumperOk); + const AbstractSymbolGroupNodePtrVector ctChildren = + containerChildren(this, m_dumperType, m_dumperContainerSize, ctx); + m_dumperContainerSize = int(ctChildren.size()); // Just in case... + if (ctChildren.empty()) + return; + + clearFlags(ExpandedByDumper); + // Mark current children as obscured. We cannot show both currently + // as this would upset the numerical sorting of the watch model + AbstractSymbolGroupNodePtrVectorConstIterator cend = children().end(); + for (AbstractSymbolGroupNodePtrVectorConstIterator it = children().begin(); it != cend; ++it) + (*it)->addFlags(Obscured); + // Add children and mark them as referenced by us. + cend = ctChildren.end(); + for (AbstractSymbolGroupNodePtrVectorConstIterator it = ctChildren.begin(); it != cend; ++it) + addChild(*it); +} + +// Run dumpers, format simple in-line dumper value and retrieve fake children +std::wstring SymbolGroupNode::simpleDumpValue(const SymbolGroupValueContext &ctx) +{ + if (testFlags(Uninitialized)) + return L""; + if (testFlags(SimpleDumperOk)) + return m_dumperValue; + if ((flags() & SimpleDumperMask) == 0) { + const unsigned dumperFlags = dumpSimpleType(this , ctx, &m_dumperValue, + &m_dumperType, &m_dumperContainerSize); + setFlags(dumperFlags); + if (testFlags(SimpleDumperOk)) + return m_dumperValue; + } + return symbolGroupFixedValue(); +} + +void SymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname, + const DumpParameters &p, const SymbolGroupValueContext &ctx) +{ + dumpNode(str, name(), visitingFullIname, p, ctx); +} + +void SymbolGroupNode::dumpNode(std::ostream &str, + const std::string &aName, + const std::string &aFullIName, + const DumpParameters &dumpParameters, + const SymbolGroupValueContext &ctx) +{ + const std::string t = type(); + SymbolGroupNode::dumpBasicData(str, aName, aFullIName, t, aFullIName); + + if (const ULONG64 addr = address()) + str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec + << '"'; + + const bool uninitialized = flags() & Uninitialized; + bool valueEditable = !uninitialized; + bool valueEnabled = !uninitialized; + + // Shall it be recoded? + std::wstring value = simpleDumpValue(ctx); + int encoding = 0; + if (dumpParameters.recode(t, aFullIName, ctx, &value, &encoding)) { + str << ",valueencoded=\"" << encoding + << "\",value=\"" << gdbmiWStringFormat(value) <<'"'; + } else { // As is: ASCII or base64? + if (isSevenBitClean(value.c_str(), value.size())) { + str << ",valueencoded=\"" << DumpEncodingAscii << "\",value=\"" + << gdbmiWStringFormat(value) << '"'; + } else { + str << ",valueencoded=\"" << DumpEncodingBase64_Utf16 << "\",value=\""; + base64Encode(str, reinterpret_cast(value.c_str()), value.size() * sizeof(wchar_t)); + str << '"'; + } + } + // Children: Dump all known non-obscured or subelements + unsigned childCountGuess = 0; + if (!uninitialized) { + if (m_dumperContainerSize > 0) { + childCountGuess = m_dumperContainerSize; // See Obscured handling + } else { + if (children().empty()) { + childCountGuess = m_parameters.SubElements; // Guess + } else { + childCountGuess = unsigned(children().size()); + } + } + } + // No children..suppose we are editable and enabled + if (childCountGuess != 0) + valueEditable = false; + str << ",valueenabled=\"" << (valueEnabled ? "true" : "false") << '"' + << ",valueeditable=\"" << (valueEditable ? "true" : "false") << '"' + << ",numchild=\"" << childCountGuess << '"'; +} + +void SymbolGroupNode::debug(std::ostream &str, + const std::string &visitingFullIname, + unsigned verbosity, unsigned depth) const +{ + indentStream(str, depth); + const std::string fullIname = absoluteFullIName(); + str << "AbsIname=" << fullIname << '"'; + if (fullIname != visitingFullIname) + str << ",VisitIname=\"" <= 0) { + str << " ,dumperType=" << m_dumperType; + if (m_dumperType & KT_Qt_Type) + str << " qt"; + if (m_dumperType & KT_STL_Type) + str << " STL"; + if (m_dumperType & KT_ContainerType) + str << " container(" << m_dumperContainerSize << ')'; + } + if (!(f & Uninitialized)) + str << " Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"'; + str << '\n'; // Potentially multiline + } + str << '\n'; +} + +static inline std::string msgCannotCast(const std::string &nodeName, + const std::string &fromType, + const std::string &toType, + const std::string &why) +{ + std::ostringstream str; + str << "Cannot cast node '" << nodeName << "' from '" << fromType + << "' to '" << toType << "': " << why; + return str.str(); +} + +// Expand! +bool SymbolGroupNode::expand(std::string *errorMessage) +{ + if (::debug > 1) + DebugPrint() << "SymbolGroupNode::expand " << name() << ' ' << m_index; + if (isExpanded()) { + // Clear the flag indication dumper expansion on a second, explicit request + clearFlags(ExpandedByDumper); + return true; + } + if (!canExpand()) { + *errorMessage = "No subelements to expand in node: " + absoluteFullIName(); + return false; + } + if (flags() & Uninitialized) { + *errorMessage = "Refusing to expand uninitialized node: " + absoluteFullIName(); + return false; + } + + const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE); + + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("ExpandSymbol", hr); + return false; + } + SymbolGroup::SymbolParameterVector parameters; + // Retrieve parameters (including self, re-retrieve symbol parameters to get new 'expanded' flag + // and corrected SubElement count (might be estimate)) + if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(), + m_index, m_parameters.SubElements + 1, + ¶meters, errorMessage)) + return false; + // Before inserting children, correct indexes on whole group + m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements); + // Parse parameters, correct our own) and create child nodes. + parseParameters(m_index, m_index, parameters); + return true; +} + +bool SymbolGroupNode::expandRunComplexDumpers(const SymbolGroupValueContext &ctx, + std::string *errorMessage) +{ + if (isExpanded() || testFlags(ComplexDumperOk)) + return true; + if (!expand(errorMessage)) + return false; + simpleDumpValue(ctx); // Run simple dumpers to obtain type and run complex dumpers + if (testFlags(SimpleDumperOk)) + runComplexDumpers(ctx); + return true; +} + +bool SymbolGroupNode::typeCast(const std::string &desiredType, std::string *errorMessage) +{ + const std::string fromType = type(); + if (fromType == desiredType) + return true; + if (isExpanded()) { + *errorMessage = msgCannotCast(absoluteFullIName(), fromType, desiredType, "Already expanded"); + return false; + } + HRESULT hr = m_symbolGroup->debugSymbolGroup()->OutputAsType(m_index, desiredType.c_str()); + if (FAILED(hr)) { + *errorMessage = msgCannotCast(absoluteFullIName(), fromType, desiredType, msgDebugEngineComFailed("OutputAsType", hr)); + return false; + } + hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(m_index, 1, &m_parameters); + if (FAILED(hr)) { // Should never fail + *errorMessage = msgCannotCast(absoluteFullIName(), fromType, desiredType, msgDebugEngineComFailed("GetSymbolParameters", hr)); + return false; + } + return true; +} + +static inline std::string msgCannotAddSymbol(const std::string &name, const std::string &why) +{ + std::ostringstream str; + str << "Cannot add symbol '" << name << "': " << why; + return str.str(); +} + +// For root nodes, only: Add a new symbol by name +SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name, + const std::string &iname, + std::string *errorMessage) +{ + ULONG index = DEBUG_ANY_ID; // Append + HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index); + if (FAILED(hr)) { + *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr)); + return 0; + } + SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS()); + hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin())); + if (FAILED(hr)) { // Should never fail + *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("GetSymbolParameters", hr)); + return 0; + } + // Paranoia: Check for cuckoo's eggs (which should not happen) + if (parameters.front().ParentSymbol != m_index) { + *errorMessage = msgCannotAddSymbol(name, "Parent id mismatch"); + return 0; + } + SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index, + name, iname.empty() ? name : iname); + node->parseParameters(0, 0, parameters); + node->addFlags(AdditionalSymbol); + addChild(node); + return node; +} + +// --------- ReferenceSymbolGroupNode +ReferenceSymbolGroupNode::ReferenceSymbolGroupNode(const std::string &name, + const std::string &iname, + SymbolGroupNode *referencedNode) : + AbstractSymbolGroupNode(name, iname), m_referencedNode(referencedNode) +{ +} + +// Convenience to create a node name name='[1]', iname='1' for arrays +ReferenceSymbolGroupNode *ReferenceSymbolGroupNode::createArrayNode(int index, + SymbolGroupNode *referencedNode) +{ + const std::string iname = toString(index); + std::string name = std::string(1, '['); + name += iname; + name.push_back(']'); + return new ReferenceSymbolGroupNode(name, iname, referencedNode); +} + +void ReferenceSymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname, + const DumpParameters &p, const SymbolGroupValueContext &ctx) +{ + // Let the referenced node dump with our iname/name + m_referencedNode->dumpNode(str, name(), visitingFullIname, p, ctx); +} + +void ReferenceSymbolGroupNode::debug(std::ostream &str, const std::string &visitingFullIname, + unsigned verbosity, unsigned depth) const +{ + indentStream(str, 2 * depth); + str << "Node " << name() << '/' << visitingFullIname << " referencing\n"; + m_referencedNode->debug(str, visitingFullIname, verbosity, depth); +} + +// --------- DebugSymbolGroupNodeVisitor + +// "local.vi" -> "local" +std::string SymbolGroupNodeVisitor::parentIname(const std::string &iname) +{ + const std::string::size_type lastSep = iname.rfind(SymbolGroupNodeVisitor::iNamePathSeparator); + return lastSep == std::string::npos ? std::string() : iname.substr(0, lastSep); +} + +DebugSymbolGroupNodeVisitor::DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity) : + m_os(os), m_verbosity(verbosity) +{ +} + +SymbolGroupNodeVisitor::VisitResult + DebugSymbolGroupNodeVisitor::visit(AbstractSymbolGroupNode *node, + const std::string &aFullIname, + unsigned /* child */, unsigned depth) +{ + node->debug(m_os, aFullIname, m_verbosity, depth); + return VisitContinue; +} + +// --------------------- DumpSymbolGroupNodeVisitor +DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os, + const SymbolGroupValueContext &context, + const DumpParameters ¶meters) : + m_os(os), m_context(context), m_parameters(parameters), + m_visitChildren(false),m_lastDepth(unsigned(-1)) +{ +} + +SymbolGroupNodeVisitor::VisitResult + DumpSymbolGroupNodeVisitor::visit(AbstractSymbolGroupNode *node, + const std::string &fullIname, + unsigned /* child */, unsigned depth) +{ + // Show container children only, no additional symbol below root. + const unsigned flags = node->flags(); + if (flags & (SymbolGroupNode::Obscured|SymbolGroupNode::AdditionalSymbol)) + return VisitSkipChildren; + // Recurse to children only if expanded by explicit watchmodel request + // and initialized. + m_visitChildren = true; + // Visit children of a SymbolGroupNode only if not expanded by its dumpers + if (SymbolGroupNode *snode = node->asSymbolGroupNode()) + m_visitChildren = snode->isExpanded() + && (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0; + // Comma between same level children given obscured children + if (depth == m_lastDepth) { + m_os << ','; + } else { + m_lastDepth = depth; + } + if (m_parameters.humanReadable()) { + m_os << '\n'; + indentStream(m_os, depth * 2); + } + m_os << '{'; + node->dump(m_os, fullIname, m_parameters, m_context); + if (m_visitChildren) { // open children array + m_os << ",children=["; + } else { // No children, close array. + m_os << '}'; + } + if (m_parameters.humanReadable()) + m_os << '\n'; + return m_visitChildren ? VisitContinue : VisitSkipChildren; +} + +void DumpSymbolGroupNodeVisitor::childrenVisited(const AbstractSymbolGroupNode *n, unsigned) +{ + m_os << "]}"; // Close children array and self + if (m_parameters.humanReadable()) + m_os << " /* end of '" << n->absoluteFullIName() << "' */\n"; +} diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.h b/src/libs/qtcreatorcdbext/symbolgroupnode.h new file mode 100644 index 00000000000..8ef34ec2000 --- /dev/null +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.h @@ -0,0 +1,362 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef SYMBOLGROUPNODE_H +#define SYMBOLGROUPNODE_H + +#include "common.h" + +#include +#include +#include +#include + +std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p); + +class SymbolGroupNodeVisitor; +class SymbolGroup; +struct SymbolGroupValueContext; +class SymbolGroupNode; + +// All parameters for GDBMI dumping in one struct. +struct DumpParameters +{ + typedef std::map FormatMap; // type or iname to format + enum DumpFlags + { + DumpHumanReadable = 0x1, + DumpComplexDumpers = 0x2 + }; + + DumpParameters(); + bool humanReadable() const { return dumpFlags & DumpHumanReadable; } + // Helper to decode format option arguments. + static FormatMap decodeFormatArgument(const std::string &f); + + bool recode(const std::string &type, const std::string &iname, + const SymbolGroupValueContext &ctx, + std::wstring *value, int *encoding) const; + int format(const std::string &type, const std::string &iname) const; + + unsigned dumpFlags; + FormatMap typeFormats; + FormatMap individualFormats; +}; + +// Base class for a node of SymbolGroup, handling the list of children. +class AbstractSymbolGroupNode +{ + AbstractSymbolGroupNode(const AbstractSymbolGroupNode&); + AbstractSymbolGroupNode& operator=(const AbstractSymbolGroupNode&); + +public: + typedef std::vector AbstractSymbolGroupNodePtrVector; + typedef AbstractSymbolGroupNodePtrVector::iterator AbstractSymbolGroupNodePtrVectorIterator; + typedef AbstractSymbolGroupNodePtrVector::const_iterator AbstractSymbolGroupNodePtrVectorConstIterator; + + virtual ~AbstractSymbolGroupNode(); + + // Name to appear in watchwindow + const std::string &name() const { return m_name; } + // 'iname' used as an internal id. + const std::string &iName() const { return m_iname; } + // Full iname 'local.x.foo': WARNING: this returns the absolute path not + // taking reference nodes into account. + std::string absoluteFullIName() const; + + virtual const AbstractSymbolGroupNodePtrVector &children() const = 0; + AbstractSymbolGroupNode *childAt(unsigned) const; + + unsigned indexByIName(const char *) const; // (unsigned(-1) on failure + AbstractSymbolGroupNode *childByIName(const char *) const; + + const AbstractSymbolGroupNode *parent() const { return m_parent; } + + unsigned flags() const { return m_flags; } + bool testFlags(unsigned f) const { return (m_flags & f) != 0; } + void setFlags(unsigned f) { m_flags = f; } + void addFlags(unsigned f) { m_flags |= f; } + void clearFlags(unsigned f) { m_flags &= ~f; } + + virtual SymbolGroupNode *asSymbolGroupNode() { return 0; } + virtual const SymbolGroupNode *asSymbolGroupNode() const { return 0; } + virtual const AbstractSymbolGroupNode *resolveReference() const { return this; } + virtual AbstractSymbolGroupNode *resolveReference() { return this; } + + bool accept(SymbolGroupNodeVisitor &visitor, + const std::string &visitingParentIname, + unsigned child, unsigned depth); + + // I/O: GDBMI dump for Visitors + virtual void dump(std::ostream &str, const std::string &visitingFullIname, + const DumpParameters &p, const SymbolGroupValueContext &ctx) = 0; + // I/O: debug output for Visitors + virtual void debug(std::ostream &os, const std::string &visitingFullIname, + unsigned verbosity, unsigned depth) const; + + // For BaseSymbolGroupNode only. + void setParent(AbstractSymbolGroupNode *n); + +protected: + explicit AbstractSymbolGroupNode(const std::string &name, const std::string &iname); + + // Basic GDBMI dumping + static inline void dumpBasicData(std::ostream &str, const std::string &aName, + const std::string &aFullIname, + const std::string &type = std::string(), + const std::string &expression = std::string()); + +private: + const std::string m_name; + const std::string m_iname; + AbstractSymbolGroupNode *m_parent; + unsigned m_flags; +}; + +// Base class for a node of SymbolGroup with a flat list of children. +class BaseSymbolGroupNode : public AbstractSymbolGroupNode +{ +public: + virtual const AbstractSymbolGroupNodePtrVector &children() const { return m_children; } + +protected: + explicit BaseSymbolGroupNode(const std::string &name, const std::string &iname); + virtual ~BaseSymbolGroupNode(); + + void reserveChildren(AbstractSymbolGroupNodePtrVector::size_type s) { m_children.reserve(s); } + void addChild(AbstractSymbolGroupNode *c); + +private: + AbstractSymbolGroupNodePtrVector m_children; + + void removeChildren(); +}; + +/* SymbolGroupNode: 'Real' node within a symbol group, identified by its index. + * Provides accessors for fixed-up symbol group value and a dumping facility + * consisting of: + * - 'Simple' dumping done when running the DumpVisitor. This produces one + * line of formatted output shown for the class. These values + * values should are displayed, while still allowing for expansion of the structure + * in the debugger. + * It also pre-determines some information for complex dumping (type, container). + * - 'Complex' dumping: Obscures the symbol group children by fake children, for + * example container children, run when calling SymbolGroup::dump with an iname. + * The fake children are appended to the child list (other children are just marked as + * obscured for GDBMI dumping so that SymbolGroupValue expressions still work as before). + * The dumping is mostly based on SymbolGroupValue expressions. + * in the debugger. Evaluating those dumpers might expand symbol nodes, which are + * then marked as 'ExpandedByDumper'. This stops the dump recursion to prevent + * outputting data that were not explicitly expanded by the watch handler. */ + +class SymbolGroupNode : public BaseSymbolGroupNode +{ + explicit SymbolGroupNode(SymbolGroup *symbolGroup, + ULONG index, + const std::string &name, + const std::string &iname); + +public: + enum Flags + { + Uninitialized = 0x1, + SimpleDumperNotApplicable = 0x2, // No dumper available for type + SimpleDumperOk = 0x4, // Internal dumper ran, value set + SimpleDumperFailed = 0x8, // Internal dumper failed + SimpleDumperMask = SimpleDumperNotApplicable|SimpleDumperOk|SimpleDumperFailed, + ExpandedByDumper = 0x10, + AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible + Obscured = 0x40, // Symbol is obscured by (for example) fake container children + ComplexDumperOk = 0x80 + }; + + typedef std::vector SymbolParameterVector; + + void parseParameters(SymbolParameterVector::size_type index, + SymbolParameterVector::size_type parameterOffset, + const SymbolParameterVector &vec); + + static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec); + // For root nodes, only: Add a new symbol by name + SymbolGroupNode *addSymbolByName(const std::string &name, // Expression like 'myarray[1]' + const std::string &iname, // Desired iname, defaults to name + std::string *errorMessage); + + SymbolGroup *symbolGroup() const { return m_symbolGroup; } + + // I/O: Gdbmi dump for Visitors + virtual void dump(std::ostream &str, const std::string &fullIname, + const DumpParameters &p, const SymbolGroupValueContext &ctx); + // Dumper functionality for reference nodes. + void dumpNode(std::ostream &str, const std::string &aName, const std::string &aFullIName, + const DumpParameters &p, const SymbolGroupValueContext &ctx); + + // I/O: debug for Visitors + virtual void debug(std::ostream &os, const std::string &visitingFullIname, + unsigned verbosity, unsigned depth) const; + + std::wstring symbolGroupRawValue() const; + std::wstring symbolGroupFixedValue() const; + + std::string type() const; + int dumperType() const { return m_dumperType; } // Valid after dumper run + int dumperContainerSize() { return m_dumperContainerSize; } // Valid after dumper run + unsigned size() const; // Size of value + ULONG64 address() const; + + bool expand(std::string *errorMessage); + bool expandRunComplexDumpers(const SymbolGroupValueContext &ctx, std::string *errorMessage); + bool isExpanded() const { return !children().empty(); } + bool canExpand() const { return m_parameters.SubElements > 0; } + void runComplexDumpers(const SymbolGroupValueContext &ctx); + // Cast to a different type. Works only on unexpanded nodes + bool typeCast(const std::string &desiredType, std::string *errorMessage); + + ULONG subElements() const { return m_parameters.SubElements; } + ULONG index() const { return m_index; } + + virtual SymbolGroupNode *asSymbolGroupNode() { return this; } + virtual const SymbolGroupNode *asSymbolGroupNode() const { return this; } + +private: + const SymbolGroupNode *symbolGroupNodeParent() const; + bool isArrayElement() const; + // Notify about expansion of a node, shift indexes + bool notifyExpanded(ULONG index, ULONG insertedCount); + std::wstring simpleDumpValue(const SymbolGroupValueContext &ctx); + + SymbolGroup *const m_symbolGroup; + ULONG m_index; + DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct. + std::wstring m_dumperValue; + int m_dumperType; + int m_dumperContainerSize; +}; + +// Artificial node referencing another (real) SymbolGroupNode (added symbol or +// symbol from within a linked list structure. Forwards dumping to referenced node +// using its own name/iname. +class ReferenceSymbolGroupNode : public AbstractSymbolGroupNode +{ +public: + explicit ReferenceSymbolGroupNode(const std::string &name, + const std::string &iname, + SymbolGroupNode *referencedNode); + // Convenience to create a node name name='[1]', iname='1' for arrays + static ReferenceSymbolGroupNode *createArrayNode(int index, + SymbolGroupNode *referencedNode); + + virtual void dump(std::ostream &str, const std::string &visitingFullIname, + const DumpParameters &p, const SymbolGroupValueContext &ctx); + virtual void debug(std::ostream &os, const std::string &visitingFullIname, + unsigned verbosity, unsigned depth) const; + + virtual const AbstractSymbolGroupNodePtrVector &children() const { return m_referencedNode->children(); } + + virtual const AbstractSymbolGroupNode *resolveReference() const { return m_referencedNode; } + virtual AbstractSymbolGroupNode *resolveReference() { return m_referencedNode; } + +private: + SymbolGroupNode * const m_referencedNode; +}; + +/* Visitor that takes care of iterating over the nodes and + * building the full iname path ('local.foo.bar') that is required for + * GDBMI dumping. The full name depends on the path on which a node was reached + * for referenced nodes (a linked list element can be reached via array index + * or by expanding the whole structure). + * visit() is not called for the (invisible) root node, but starting with the + * root's children with depth=0. + * Return true from visit() to terminate the recursion. */ + +class SymbolGroupNodeVisitor { + SymbolGroupNodeVisitor(const SymbolGroupNodeVisitor&); + SymbolGroupNodeVisitor& operator=(const SymbolGroupNodeVisitor&); + + friend class AbstractSymbolGroupNode; +protected: + SymbolGroupNodeVisitor() {} +public: + virtual ~SymbolGroupNodeVisitor() {} + + // "local.vi" -> "local" + static std::string parentIname(const std::string &iname); + static const char iNamePathSeparator = '.'; + +protected: + enum VisitResult + { + VisitContinue, + VisitSkipChildren, + VisitStop + }; + +private: + virtual VisitResult visit(AbstractSymbolGroupNode *node, + const std::string &fullIname, + unsigned child, unsigned depth) = 0; + // Helper for formatting output. + virtual void childrenVisited(const AbstractSymbolGroupNode * /* node */, unsigned /* depth */) {} +}; + +// Debug output visitor. +class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor { +public: + explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0); + +private: + virtual VisitResult visit(AbstractSymbolGroupNode *node, + const std::string &fullIname, + unsigned child, unsigned depth); + + std::ostream &m_os; + const unsigned m_verbosity; +}; + +// GDBMI dump output visitor. +class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor { +public: + explicit DumpSymbolGroupNodeVisitor(std::ostream &os, + const SymbolGroupValueContext &context, + const DumpParameters ¶meters = DumpParameters()); + +private: + virtual VisitResult visit(AbstractSymbolGroupNode *node, + const std::string &fullIname, + unsigned child, unsigned depth); + virtual void childrenVisited(const AbstractSymbolGroupNode * node, unsigned depth); + + std::ostream &m_os; + const SymbolGroupValueContext &m_context; + const DumpParameters &m_parameters; + bool m_visitChildren; + unsigned m_lastDepth; +}; + +#endif // SYMBOLGROUPNODE_H diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index 5650c1311d1..0089540c54b 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -54,7 +54,8 @@ SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const { if (ensureExpanded()) if (index < m_node->children().size()) - return SymbolGroupValue(m_node->children().at(index), m_context); + if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode()) + return SymbolGroupValue(n, m_context); return SymbolGroupValue(); } @@ -78,8 +79,9 @@ bool SymbolGroupValue::ensureExpanded() const SymbolGroupValue SymbolGroupValue::operator[](const char *name) const { if (ensureExpanded()) - if (SymbolGroupNode *child = m_node->childByIName(name)) - return SymbolGroupValue(child, m_context); + if (AbstractSymbolGroupNode *child = m_node->childByIName(name)) + if (SymbolGroupNode *n = child->asSymbolGroupNode()) + return SymbolGroupValue(n, m_context); return SymbolGroupValue(); }