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(); }