From 8d3e48af33a6c95116af2c774e35f0dd576a3a57 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 21 Dec 2010 17:23:27 +0100 Subject: [PATCH] Debugger[New CDB]: First attempts at QMap<>. Qualify map and hash nodes as Module!QMapNode<>. Limit recursion depth of GDBMI dump. Introduce error reporting. Iterate QMap nodes and create artificial key/value nodes. --- src/libs/qtcreatorcdbext/containers.cpp | 140 +++++++++++++++- .../qtcreatorcdbext/qtcreatorcdbextension.cpp | 20 ++- src/libs/qtcreatorcdbext/stringutils.h | 13 ++ src/libs/qtcreatorcdbext/symbolgroup.cpp | 35 +++- src/libs/qtcreatorcdbext/symbolgroup.h | 11 +- src/libs/qtcreatorcdbext/symbolgroupnode.cpp | 57 ++++++- src/libs/qtcreatorcdbext/symbolgroupnode.h | 24 ++- src/libs/qtcreatorcdbext/symbolgroupvalue.cpp | 157 +++++++++++++++++- src/libs/qtcreatorcdbext/symbolgroupvalue.h | 31 +++- 9 files changed, 451 insertions(+), 37 deletions(-) diff --git a/src/libs/qtcreatorcdbext/containers.cpp b/src/libs/qtcreatorcdbext/containers.cpp index 51fc5103f8d..d1c90c9a763 100644 --- a/src/libs/qtcreatorcdbext/containers.cpp +++ b/src/libs/qtcreatorcdbext/containers.cpp @@ -732,16 +732,25 @@ SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string hashNodeTy return rc; } -// Return real node type of a QHash: "class QHash" -> [struct] "QHashNode"; -static inline std::string qHashNodeType(std::string qHashType) +// Return the node type of a QHash/QMap: +// "class QHash" -> [struct] "QtCored4!QHashNode"; +static inline std::string qHashNodeType(const SymbolGroupValue &v, + const char *nodeType) { - qHashType.erase(0, 6); // Strip "class "; + std::string qHashType = v.type(); const std::string::size_type pos = qHashType.find('<'); if (pos != std::string::npos) - qHashType.insert(pos, "Node"); - return qHashType; + qHashType.insert(pos, nodeType); + // A map node must be qualified with the current module and + // the Qt namespace (particularly QMapNode, QHashNodes work also for + // the unqualified case). + const QtInfo &qtInfo = QtInfo::get(v.context()); + const std::string currentModule = v.node()->symbolGroup()->module(); + return QtInfo::prependModuleAndNameSpace(qHashType, currentModule, qtInfo.nameSpace); } +enum { debugMap = 0 }; + // Return up to count nodes of type "QHashNode" of a "class QHash". SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v, VectorIndexType count) @@ -751,6 +760,8 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v, const SymbolGroupValue hashData = v["d"]; // 'e' is used as a special value to indicate empty hash buckets in the array. const ULONG64 ePtr = v["e"].pointerValue(); + if (debugMap) + DebugPrint() << v << " Count=" << count << ",ePtr=0x" << std::hex << ePtr; if (!hashData || !ePtr) return SymbolGroupValueVector(); // Retrieve the array of buckets of 'd' @@ -762,7 +773,7 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v, if (!bucketPointers) return SymbolGroupValueVector(); // Get list of buckets (starting elements of 'QHashData::Node') - const std::string dummyNodeType = "QHashData::Node"; + const std::string dummyNodeType = ntext()).prependQtCoreModule("QHashData::Node"); const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ? hashBuckets(v.node()->symbolGroup(), dummyNodeType, reinterpret_cast(bucketPointers), numBuckets, @@ -786,13 +797,18 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v, dummyNodeList.push_back(l); if (dummyNodeList.size() >= count) // Stop at maximum count notEnough = false; + if (debugMap) + DebugPrint() << '#' << (dummyNodeList.size() - 1) << "l=" << l << ",next=" << next; + l = next; } else { break; } } } // Finally convert them into real nodes 'QHashNode (potentially expensive) - const std::string nodeType = qHashNodeType(v.type()); + const std::string nodeType = qHashNodeType(v, "Node"); + if (debugMap) + DebugPrint() << "Converting into " << nodeType; SymbolGroupValueVector nodeList; nodeList.reserve(count); const SymbolGroupValueVector::const_iterator dcend = dummyNodeList.end(); @@ -809,7 +825,7 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v, // QSet<>: Contains a 'QHash' as member 'q_hash'. // Just dump the keys as an array. static inline AbstractSymbolGroupNodePtrVector - qSetChildList(const SymbolGroupValue &v, VectorIndexType count) + qSetChildList(const SymbolGroupValue &v, int count) { const SymbolGroupValue qHash = v["q_hash"]; AbstractSymbolGroupNodePtrVector rc; @@ -831,7 +847,7 @@ static inline AbstractSymbolGroupNodePtrVector // QHash<>: Add with fake map nodes. static inline AbstractSymbolGroupNodePtrVector - qHashChildList(const SymbolGroupValue &v, VectorIndexType count) + qHashChildList(const SymbolGroupValue &v, int count) { AbstractSymbolGroupNodePtrVector rc; if (!count) @@ -852,6 +868,106 @@ static inline AbstractSymbolGroupNodePtrVector return rc; } +// QMap<>: Return the list of QMapData::Node +static inline SymbolGroupValueVector qMapNodes(const SymbolGroupValue &v, VectorIndexType count) +{ + const SymbolGroupValue e = v["e"]; + const ULONG64 ePtr = e.pointerValue(); + if (debugMap) + DebugPrint() << v.type() << " E=0x" << std::hex << ePtr; + if (!ePtr) + return SymbolGroupValueVector(); + if (debugMap) + DebugPrint() << v.type() << " E=0x" << std::hex << ePtr; + SymbolGroupValueVector rc; + rc.reserve(count); + SymbolGroupValue n = e["forward"][unsigned(0)]; + for (VectorIndexType i = 0; i < count && n && n.pointerValue() != ePtr; i++) { + rc.push_back(n); + n = n["forward"][unsigned(0)]; + } + return rc; +} + +// QMap<>: Add with fake map nodes. +static inline AbstractSymbolGroupNodePtrVector + qMapChildList(const SymbolGroupValue &v, VectorIndexType count) +{ + if (debugMap) + DebugPrint() << v.type() << "," << count; + + if (!count) + return AbstractSymbolGroupNodePtrVector(); + // Get node type: 'class namespace::QMap' + // ->'QtCored4!namespace::QMapNode' + // Note: Any types QMapNode<> will not be found without modules! + const std::string mapNodeType = qHashNodeType(v, "Node"); + const std::string mapPayloadNodeType = qHashNodeType(v, "PayloadNode"); + // Calculate the offset needed (see QMap::concrete() used by the iterator). + const unsigned mapNodeSize = SymbolGroupValue::sizeOf(mapPayloadNodeType.c_str()); + const unsigned payloadNodeSize = SymbolGroupValue::sizeOf(mapPayloadNodeType.c_str()); + const unsigned pointerSize = SymbolGroupValue::pointerSize(); + if (debugMap) { + DebugPrint() << v.type() << "," << mapNodeType << ':' + << mapNodeSize << ',' << mapPayloadNodeType << ':' << payloadNodeSize + << ", pointerSize=" << pointerSize; + } + if (!payloadNodeSize || !mapNodeSize) + return AbstractSymbolGroupNodePtrVector(); + const ULONG64 payLoad = payloadNodeSize - pointerSize; + // Get the value offset. Manually determine the alignment to be able + // to retrieve key/value without having to deal with QMapNode<> (see below). + // Subtract the 2 trailing pointers of the node. + const std::vector innerTypes = v.innerTypes(); + if (innerTypes.size() != 2u) + return AbstractSymbolGroupNodePtrVector(); + const unsigned valueSize = SymbolGroupValue::sizeOf(innerTypes.at(1).c_str()); + const unsigned valueOffset = mapNodeSize - valueSize - pointerSize; + if (debugMap) + DebugPrint() << "Payload=" << payLoad << ",valueOffset=" << valueOffset << ',' + << innerTypes.front() << ',' << innerTypes.back() << ':' << valueSize; + if (!valueOffset || !valueSize) + return AbstractSymbolGroupNodePtrVector(); + // Get the children. + const SymbolGroupValueVector childNodes = qMapNodes(v, count); + if (debugMap) + DebugPrint() << "children: " << childNodes.size() << " of " << count; + // Deep expansion of the forward[0] sometimes fails. In that case, + // take what we can get. + if (childNodes.size() != count) + count = childNodes.size(); + // The correct way of doing this would be to construct additional symbols + // '*(QMapNode *)(node_address)'. However, when doing this as of + // 'CDB 6.12.0002.633' (21.12.2010) IDebugSymbolGroup::AddSymbol() + // just fails, returning DEBUG_ANY_ID without actually doing something. So, + // we circumvent the map nodes and directly create key and values at their addresses. + AbstractSymbolGroupNodePtrVector rc; + rc.reserve(count); + std::string errorMessage; + SymbolGroup *sg = v.node()->symbolGroup(); + + for (VectorIndexType i = 0; i < count ; i++) { + const ULONG64 nodePtr = childNodes.at(i).pointerValue(); + if (!nodePtr) + return AbstractSymbolGroupNodePtrVector(); + const ULONG64 keyAddress = nodePtr - payLoad; + const std::string keyExp = pointedToSymbolName(keyAddress, innerTypes.front()); + const std::string valueExp = pointedToSymbolName(keyAddress + valueOffset, innerTypes.at(1)); + if (debugMap) { + DebugPrint() << '#' << i << '/' << count << ' ' << std::hex << ",node=0x" << nodePtr << + ',' <addSymbol(keyExp, std::string(), &errorMessage); + SymbolGroupNode *valueNode = sg->addSymbol(valueExp, std::string(), &errorMessage); + if (!keyNode || !valueNode) + return AbstractSymbolGroupNodePtrVector(); + rc.push_back(MapNodeSymbolGroupNode::create(int(i), keyAddress, + mapNodeType, keyNode, valueNode)); + } + return rc; +} + AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type, int size, const SymbolGroupValueContext &ctx) { @@ -884,6 +1000,12 @@ AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int ty break; case KT_QSet: return qSetChildList(SymbolGroupValue(node, ctx), size); + case KT_QMap: + return qMapChildList(SymbolGroupValue(node, ctx), size); + case KT_QMultiMap: + if (const SymbolGroupValue qmap = SymbolGroupValue(node, ctx)[unsigned(0)]) + return qMapChildList(qmap, size); + break; case KT_QStringList: if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)]) return qListChildList(qList, size); diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index c2d33717a5d..f2d87dd4cd7 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -109,9 +109,10 @@ static const CommandDescription commandDescriptions[] = { "-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" + "[-t token] [T formats] [-I formats] [-f debugfilter] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n [iname]\n" "-h human-readable ouput\n" "-d debug output\n" + "-f debug_filter\n" "-c complex dumpers\n" "-e expand-list Comma-separated list of inames to be expanded beforehand\n" "-u uninitialized-list Comma-separated list of uninitialized inames\n" @@ -281,7 +282,7 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args) } const unsigned succeeded = runComplexDumpers ? - symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces()), &errorMessage) : + symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &errorMessage) : symGroup->expandList(inames, &errorMessage); ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s", @@ -298,6 +299,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int * // Parse the command unsigned debugOutput = 0; std::string iname; + std::string debugFilter; StringList tokens = commandTokens(args, token); StringVector expandedInames; StringVector uninitializedInames; @@ -324,6 +326,14 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int * split(tokens.front(), ',', std::back_inserter(uninitializedInames)); tokens.pop_front(); break; + case 'f': + if (tokens.empty()) { + *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); + return std::string(); + } + debugFilter = tokens.front(); + tokens.pop_front(); + break; case 'e': if (tokens.empty()) { *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); @@ -362,7 +372,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int * if (!tokens.empty()) iname = tokens.front(); - const SymbolGroupValueContext dumpContext(exc.dataSpaces()); + const SymbolGroupValueContext dumpContext(exc.dataSpaces(), exc.symbols()); SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage); if (!symGroup) return std::string(); @@ -379,7 +389,7 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int * } if (debugOutput) - return symGroup->debug(iname, debugOutput - 1); + return symGroup->debug(iname, debugFilter, debugOutput - 1); return iname.empty() ? symGroup->dump(dumpContext, parameters) : @@ -430,7 +440,7 @@ static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int return std::string(); } std::wstring value; - if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces()), &value)) { + if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces(), exc.symbols()), &value)) { *errorMessage = "Cannot dump " + iname; return std::string(); } diff --git a/src/libs/qtcreatorcdbext/stringutils.h b/src/libs/qtcreatorcdbext/stringutils.h index ad142d10e4a..4bad5b5f427 100644 --- a/src/libs/qtcreatorcdbext/stringutils.h +++ b/src/libs/qtcreatorcdbext/stringutils.h @@ -37,6 +37,7 @@ #include #include #include +#include void trimFront(std::string &s); void trimBack(std::string &s); @@ -58,6 +59,18 @@ void split(const std::string &s, char sep, Iterator it) } } +// A boolean predicate that can be used for grepping sequences +// of strings for a 'needle' substring. +class SubStringPredicate : public std::unary_function +{ +public: + explicit SubStringPredicate(const std::string &needle) : m_needle(needle) {} + bool operator()(const std::string &s) { return s.find(m_needle) != std::string::npos; } + +private: + const std::string &m_needle; +}; + // Format numbers, etc, as a string. template std::string toString(const Streamable s) diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index b8dcf3a9635..855d62e64ce 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -33,10 +33,12 @@ #include "symbolgroup.h" #include "stringutils.h" +#include "gdbmihelpers.h" #include #include #include +#include typedef std::vector::size_type VectorIndexType; typedef std::vector StringVector; @@ -48,13 +50,21 @@ const char rootNameC[] = "local"; SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg, const SymbolParameterVector &vec, ULONG threadId, - unsigned frame) : + unsigned frame, + const std::string &function) : m_symbolGroup(sg), m_threadId(threadId), m_frame(frame), - m_root(0) + m_root(0), + m_function(function) { m_root = SymbolGroupNode::create(this, rootNameC, vec); + // Split function 'Mod!foo' + const std::string::size_type exclPos = m_function.find('!'); + if (exclPos != std::string::npos) { + m_module = m_function.substr(0, exclPos); + m_function.erase(0, exclPos + 1); + } } SymbolGroup::~SymbolGroup() @@ -135,6 +145,7 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS IDebugSymbolGroup2 *idebugSymbols = 0; bool success = false; SymbolParameterVector parameters; + std::string func; // Obtain symbol group at stack frame. do { @@ -149,6 +160,11 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS *errorMessage = str.str(); break; } + StackFrame frameData; + if (!getFrame(frame, &frameData, errorMessage)) + break; + func = wStringToString(frameData.function); + hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, NULL, &idebugSymbols); if (FAILED(hr)) { *errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr); @@ -176,7 +192,7 @@ SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugS idebugSymbols->Release(); return 0; } - return new SymbolGroup(idebugSymbols, parameters, threadId, frame); + return new SymbolGroup(idebugSymbols, parameters, threadId, frame, func); } static inline std::string msgNotFound(const std::string &nodeName) @@ -246,16 +262,21 @@ std::string SymbolGroup::dump(const std::string &iname, return str.str(); } -std::string SymbolGroup::debug(const std::string &iname, unsigned verbosity) const +std::string SymbolGroup::debug(const std::string &iname, + const std::string &filter, + unsigned verbosity) const { std::ostringstream str; str << '\n'; - DebugSymbolGroupNodeVisitor visitor(str, verbosity); + std::auto_ptr + visitor(filter.empty() ? + new DebugSymbolGroupNodeVisitor(str, verbosity) : + new DebugFilterSymbolGroupNodeVisitor(str, filter, verbosity)); if (iname.empty()) { - accept(visitor); + accept(*visitor); } else { if (AbstractSymbolGroupNode *const node = find(iname)) { - node->accept(visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0); + node->accept(*visitor, SymbolGroupNodeVisitor::parentIname(iname), 0, 0); } else { str << msgNotFound(iname); } diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h index 41da057bdb0..c9c69ac2e67 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.h +++ b/src/libs/qtcreatorcdbext/symbolgroup.h @@ -55,7 +55,8 @@ private: explicit SymbolGroup(CIDebugSymbolGroup *, const SymbolParameterVector &vec, - ULONG threadId, unsigned frame); + ULONG threadId, unsigned frame, + const std::string &function); public: typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector; @@ -73,9 +74,13 @@ public: // Expand node and dump std::string dump(const std::string &iname, const SymbolGroupValueContext &ctx, const DumpParameters &p, std::string *errorMessage); - std::string debug(const std::string &iname = std::string(), unsigned verbosity = 0) const; + std::string debug(const std::string &iname = std::string(), + const std::string &filter = std::string(), + unsigned verbosity = 0) const; unsigned frame() const { return m_frame; } + std::string function() const { return m_function; } + std::string module() const { return m_module; } ULONG threadId() const { return m_threadId; } SymbolGroupNode *root() { return m_root; } const SymbolGroupNode *root() const { return m_root; } @@ -127,6 +132,8 @@ private: const unsigned m_frame; const ULONG m_threadId; SymbolGroupNode *m_root; + std::string m_function; + std::string m_module; }; #endif // SYMBOLGROUP_H diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp index 45b09bc199d..e7470eb6d62 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp @@ -37,6 +37,7 @@ #include "stringutils.h" #include "base64.h" #include "containers.h" +#include "extensioncontext.h" #include @@ -913,6 +914,15 @@ static inline std::string msgCannotCast(const std::string &nodeName, return str.str(); } +static std::string msgExpandFailed(const std::string &name, const std::string &iname, + ULONG index, const std::string &why) +{ + std::ostringstream str; + str << "Expansion of '" << name << "'/'" << iname << " (index: " << index + << ") failed: " << why; + return str.str(); +} + // Expand! bool SymbolGroupNode::expand(std::string *errorMessage) { @@ -926,18 +936,21 @@ bool SymbolGroupNode::expand(std::string *errorMessage) return true; } if (!canExpand()) { - *errorMessage = "No subelements to expand in node: " + absoluteFullIName(); + *errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, + "No subelements to expand in node."); return false; } if (flags() & Uninitialized) { - *errorMessage = "Refusing to expand uninitialized node: " + absoluteFullIName(); + *errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, + "Refusing to expand uninitialized node."); return false; } const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE); if (FAILED(hr)) { - *errorMessage = msgDebugEngineComFailed("ExpandSymbol", hr); + *errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, msgDebugEngineComFailed("ExpandSymbol", hr)); + ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str()); return false; } SymbolGroup::SymbolParameterVector parameters; @@ -945,8 +958,10 @@ bool SymbolGroupNode::expand(std::string *errorMessage) // and corrected SubElement count (might be estimate)) if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(), m_index, m_parameters.SubElements + 1, - ¶meters, errorMessage)) + ¶meters, errorMessage)) { + *errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, *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. @@ -1005,12 +1020,21 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name, HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index); if (FAILED(hr)) { *errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr)); + ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str()); + return 0; + } + if (index == DEBUG_ANY_ID) { // Occasionally happens for unknown or 'complicated' types + *errorMessage = msgCannotAddSymbol(name, "DEBUG_ANY_ID was returned as symbol index by AddSymbol."); + ExtensionContext::instance().report('X', 0, "Error", "%s", errorMessage->c_str()); 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)); + std::ostringstream str; + str << "Cannot retrieve 1 symbol parameter entry at " << index << ": " + << msgDebugEngineComFailed("GetSymbolParameters", hr); + *errorMessage = msgCannotAddSymbol(name, str.str()); return 0; } // Paranoia: Check for cuckoo's eggs (which should not happen) @@ -1137,6 +1161,24 @@ SymbolGroupNodeVisitor::VisitResult return VisitContinue; } +DebugFilterSymbolGroupNodeVisitor::DebugFilterSymbolGroupNodeVisitor(std::ostream &os, + const std::string &filter, + const unsigned verbosity) : + DebugSymbolGroupNodeVisitor(os, verbosity), m_filter(filter) +{ +} + +SymbolGroupNodeVisitor::VisitResult + DebugFilterSymbolGroupNodeVisitor::visit(AbstractSymbolGroupNode *node, + const std::string &fullIname, + unsigned child, unsigned depth) +{ + if (fullIname.find(m_filter) == std::string::npos + && node->name().find(m_filter) == std::string::npos) + return SymbolGroupNodeVisitor::VisitContinue; + return DebugSymbolGroupNodeVisitor::visit(node, fullIname, child, depth); +} + // --------------------- DumpSymbolGroupNodeVisitor DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os, const SymbolGroupValueContext &context, @@ -1158,9 +1200,10 @@ SymbolGroupNodeVisitor::VisitResult // 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 + // Visit children of a SymbolGroupNode only if not expanded by its dumpers. + // Report only one level for Qt Creator. if (SymbolGroupNode *snode = node->asSymbolGroupNode()) - m_visitChildren = snode->isExpanded() + m_visitChildren = depth < 1 && snode->isExpanded() && (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0; // Comma between same level children given obscured children if (depth == m_lastDepth) { diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.h b/src/libs/qtcreatorcdbext/symbolgroupnode.h index 8f450855926..1c019333d21 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.h +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.h @@ -352,7 +352,7 @@ protected: VisitStop }; -private: +protected: virtual VisitResult visit(AbstractSymbolGroupNode *node, const std::string &fullIname, unsigned child, unsigned depth) = 0; @@ -365,15 +365,32 @@ class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor { public: explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0); -private: +protected: virtual VisitResult visit(AbstractSymbolGroupNode *node, const std::string &fullIname, unsigned child, unsigned depth); +private: std::ostream &m_os; const unsigned m_verbosity; }; +// Debug filtering output visitor. +class DebugFilterSymbolGroupNodeVisitor : public DebugSymbolGroupNodeVisitor { +public: + explicit DebugFilterSymbolGroupNodeVisitor(std::ostream &os, + const std::string &filter, + const unsigned verbosity = 0); + +protected: + virtual VisitResult visit(AbstractSymbolGroupNode *node, + const std::string &fullIname, + unsigned child, unsigned depth); + +private: + const std::string m_filter; +}; + // GDBMI dump output visitor. class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor { public: @@ -381,12 +398,13 @@ public: const SymbolGroupValueContext &context, const DumpParameters ¶meters = DumpParameters()); -private: +protected: virtual VisitResult visit(AbstractSymbolGroupNode *node, const std::string &fullIname, unsigned child, unsigned depth); virtual void childrenVisited(const AbstractSymbolGroupNode * node, unsigned depth); +private: std::ostream &m_os; const SymbolGroupValueContext &m_context; const DumpParameters &m_parameters; diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index 3beb65fff83..93de4ee78ca 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -37,6 +37,14 @@ #include "containers.h" #include +#include + +SymbolGroupValue::SymbolGroupValue(const std::string &parentError) : + m_node(0), m_errorMessage(parentError) +{ + if (m_errorMessage.empty()) + m_errorMessage = "Invalid"; +} SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &ctx) : @@ -62,7 +70,7 @@ SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const if (index < m_node->children().size()) if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode()) return SymbolGroupValue(n, m_context); - return SymbolGroupValue(); + return SymbolGroupValue(m_errorMessage); } bool SymbolGroupValue::ensureExpanded() const @@ -88,7 +96,7 @@ SymbolGroupValue SymbolGroupValue::operator[](const char *name) const if (AbstractSymbolGroupNode *child = m_node->childByIName(name)) if (SymbolGroupNode *n = child->asSymbolGroupNode()) return SymbolGroupValue(n, m_context); - return SymbolGroupValue(); + return SymbolGroupValue(m_errorMessage); } std::string SymbolGroupValue::type() const @@ -253,6 +261,12 @@ unsigned SymbolGroupValue::pointerSize() return ps; } +unsigned SymbolGroupValue::intSize() +{ + static const unsigned is = SymbolGroupValue::sizeOf("int"); + return is; +} + std::string SymbolGroupValue::stripPointerType(const std::string &t) { return isPointerType(t) ? t.substr(0, t.size() - 2) : t; @@ -279,6 +293,145 @@ std::string SymbolGroupValue::stripArrayType(const std::string &t) return t; } +/* QtInfo helper: Determine the full name of a Qt Symbol like 'qstrdup' in 'QtCored4'. + * as 'QtCored4![namespace::]qstrdup'. In the event someone really uses a different + * library prefix or namespaced Qt, this should be found. + * The crux is here that the underlying IDebugSymbols::StartSymbolMatch() + * does not accept module wildcards (as opposed to the 'x' command where 'x QtCo*!*qstrdup' + * would be acceptable and fast). OTOH, doing a wildcard search like '*qstrdup' is + * very slow and should be done only if there is really a different namespace or lib prefix. + * Parameter 'modulePatternC' is used to do a search on the modules returned (due to + * the amiguities and artifacts that appear like 'QGuid4!qstrdup'). */ +static inline std::string resolveQtSymbol(const char *symbolC, + const char *defaultModuleNameC, + const char *modulePatternC, + const SymbolGroupValueContext &ctx) +{ + typedef std::list StringList; + typedef StringList::const_iterator StringListConstIt; + + // First try a match with the default module name 'QtCored4!qstrdup' for speed reasons + std::string defaultPattern = defaultModuleNameC; + defaultPattern.push_back('!'); + defaultPattern += symbolC; + const StringList defaultMatches = SymbolGroupValue::resolveSymbol(defaultPattern.c_str(), ctx); + const SubStringPredicate modulePattern(modulePatternC); + const StringListConstIt defaultIt = std::find_if(defaultMatches.begin(), defaultMatches.end(), modulePattern); + if (defaultIt != defaultMatches.end()) + return *defaultIt; + // Fail, now try a search with '*qstrdup' in all modules. This might return several matches + // like 'QtCored4!qstrdup', 'QGuid4!qstrdup' + const std::string wildCardPattern = std::string(1, '*') + symbolC; + const StringList allMatches = SymbolGroupValue::resolveSymbol(wildCardPattern.c_str(), ctx); + const StringListConstIt allIt = std::find_if(allMatches.begin(), allMatches.end(), modulePattern); + return allIt != allMatches.end() ? *allIt : std::string(); +} + +const QtInfo &QtInfo::get(const SymbolGroupValueContext &ctx) +{ + static const char qtCoreDefaultModule[] = "QtCored4"; + static QtInfo rc; + if (!rc.coreModule.empty()) + return rc; + + do { + // Lookup qstrdup() to hopefully get module and namespace + // Typically, this resolves to 'QtGuid4!qstrdup' and 'QtCored4!qstrdup'... + const std::string qualifiedSymbol = resolveQtSymbol("qstrdup", qtCoreDefaultModule, "Core", ctx); + if (qualifiedSymbol.empty()) { + rc.coreModule = qtCoreDefaultModule; + break; + } + // Should be 'QtCored4!qstrdup' + const std::string::size_type exclPos = qualifiedSymbol.find('!'); + if (exclPos == std::string::npos) { + rc.coreModule = qtCoreDefaultModule; + break; + } + rc.coreModule = qualifiedSymbol.substr(0, exclPos); + // Any namespace? 'QtCored4!nsp::qstrdup' + const std::string::size_type nameSpaceStart = exclPos + 1; + const std::string::size_type colonPos = qualifiedSymbol.find(':', nameSpaceStart); + if (colonPos != std::string::npos) + rc.nameSpace = qualifiedSymbol.substr(nameSpaceStart, colonPos - nameSpaceStart); + } while (false); + return rc; +} + +std::string QtInfo::prependModuleAndNameSpace(const std::string &type, + const std::string &module, + const std::string &aNameSpace) +{ + // Strip the prefixes "class ", "struct ". + std::string rc = type; + if (rc.compare(0, 6, "class ") == 0) { + rc.erase(0, 6); + } else { + if (rc.compare(0, 7, "struct ") == 0) + rc.erase(0, 7); + } + // Is the namespace 'nsp::' missing? + if (!aNameSpace.empty()) { + const bool nameSpaceMissing = rc.size() <= aNameSpace.size() + || rc.compare(0, aNameSpace.size(), aNameSpace) != 0 + || rc.at(aNameSpace.size()) != ':'; + if (nameSpaceMissing) { + rc.insert(0, "::"); + rc.insert(0, aNameSpace); + } + } + // Is the module 'Foo!' missing? + if (!module.empty()) { + const bool moduleMissing = rc.size() <= module.size() + || rc.compare(0, module.size(), module) != 0 + || rc.at(module.size()) != '!'; + if (moduleMissing) { + rc.insert(0, 1, '!'); + rc.insert(0, module); + } + } + return rc; +} + +std::list + SymbolGroupValue::resolveSymbol(const char *pattern, + const SymbolGroupValueContext &c, + std::string *errorMessage /* = 0 */) +{ + enum { bufSize = 2048 }; + std::list rc; + if (errorMessage) + errorMessage->clear(); + // Is it an incomplete symbol? + if (!pattern[0]) + return rc; + + ULONG64 handle = 0; + // E_NOINTERFACE means "no match". Apparently, it does not always + // set handle. + HRESULT hr = c.symbols->StartSymbolMatch(pattern, &handle); + if (hr == E_NOINTERFACE) { + if (handle) + c.symbols->EndSymbolMatch(handle); + return rc; + } + if (FAILED(hr)) { + if (errorMessage) + *errorMessage= msgDebugEngineComFailed("StartSymbolMatch", hr); + return rc; + } + char buf[bufSize]; + while (true) { + hr = c.symbols->GetNextSymbolMatch(handle, buf, bufSize - 1, 0, 0); + if (hr == E_NOINTERFACE) + break; + if (hr == S_OK) + rc.push_back(std::string(buf)); + } + c.symbols->EndSymbolMatch(handle); + return rc; +} + // get the inner types: "QMap" -> "int", "double" std::vector SymbolGroupValue::innerTypesOf(const std::string &t) { diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.h b/src/libs/qtcreatorcdbext/symbolgroupvalue.h index 2d0cb49897b..a99f897e003 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.h +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.h @@ -39,16 +39,18 @@ #include #include +#include class SymbolGroupNode; // Structure to pass all IDebug interfaces used for SymbolGroupValue struct SymbolGroupValueContext { - SymbolGroupValueContext(CIDebugDataSpaces *ds) : dataspaces(ds) {} - SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0) {} + SymbolGroupValueContext(CIDebugDataSpaces *ds, CIDebugSymbols *s) : dataspaces(ds), symbols(s) {} + SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0), symbols(0) {} CIDebugDataSpaces *dataspaces; + CIDebugSymbols *symbols; }; /* SymbolGroupValue: Flyweight tied to a SymbolGroupNode @@ -57,6 +59,8 @@ struct SymbolGroupValueContext class SymbolGroupValue { + explicit SymbolGroupValue(const std::string &parentError); + public: explicit SymbolGroupValue(SymbolGroupNode *node, const SymbolGroupValueContext &c); SymbolGroupValue(); @@ -98,7 +102,11 @@ public: static std::string addPointerType(const std::string &); static std::string stripArrayType(const std::string &); static bool isPointerType(const std::string &); + static std::list resolveSymbol(const char *pattern, + const SymbolGroupValueContext &c, + std::string *errorMessage = 0); static unsigned pointerSize(); + static unsigned intSize(); // get the inner types: "QMap" -> "int", "double" static std::vector innerTypesOf(const std::string &t); @@ -115,6 +123,25 @@ private: // For debugging purposes std::ostream &operator<<(std::ostream &, const SymbolGroupValue &v); +// Qt Information: Namespace and module. +struct QtInfo +{ + static const QtInfo &get(const SymbolGroupValueContext &ctx); + + // Prepend core module and Qt namespace. To be able to work with some + // 'complicated' types like QMapNode, specifying the module helps + std::string prependQtCoreModule(const std::string &type) const + { return QtInfo::prependModuleAndNameSpace(type, coreModule, nameSpace); } + // Prepend module and namespace if missing with some smartness + // ('Foo' or -> 'nsp::Foo') => 'QtCored4!nsp::Foo' + static std::string prependModuleAndNameSpace(const std::string &type, + const std::string &module, + const std::string &nameSpace); + + std::string nameSpace; + std::string coreModule; +}; + /* Helpers for detecting types reported from IDebugSymbolGroup * 1) Class prefix==true is applicable to outer types obtained from * from IDebugSymbolGroup: 'class foo' or 'struct foo'.