diff --git a/src/libs/qtcreatorcdbext/containers.cpp b/src/libs/qtcreatorcdbext/containers.cpp index efa4f47fb70..b3d5088cc51 100644 --- a/src/libs/qtcreatorcdbext/containers.cpp +++ b/src/libs/qtcreatorcdbext/containers.cpp @@ -86,7 +86,7 @@ static inline std::string fixInnerType(std::string type, // Resolve types unless they are POD or pointers to POD (that is, qualify 'Foo' and 'Foo*') const bool needResolve = kt == KT_Unknown || kt == KT_PointerType || !(kt & KT_POD_Type); const std::string fixed = needResolve ? - SymbolGroupValue::resolveType(stripped, container.context(), container.node()->symbolGroup()) : + SymbolGroupValue::resolveType(stripped, container.context(), container.module()) : stripped; if (SymbolGroupValue::verbose) { DebugPrint dp; @@ -294,7 +294,9 @@ static inline std::string pointedToSymbolName(ULONG64 address, const std::string template AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc, - const std::string &innerType, int count) + const std::string &module, + const std::string &innerType, + int count) { AbstractSymbolGroupNodePtrVector rc; if (!count) @@ -303,7 +305,7 @@ AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc add rc.reserve(count); for (int i = 0; i < count; i++) { const std::string name = pointedToSymbolName(addressFunc(), innerType); - if (SymbolGroupNode *child = sg->addSymbol(name, std::string(), &errorMessage)) { + if (SymbolGroupNode *child = sg->addSymbol(module, name, std::string(), &errorMessage)) { rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, child)); } else { if (SymbolGroupValue::verbose) @@ -335,12 +337,13 @@ private: const ULONG m_delta; }; -static inline AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, ULONG64 address, - const std::string &innerType, int count) +static inline AbstractSymbolGroupNodePtrVector + arrayChildList(SymbolGroup *sg, ULONG64 address, const std::string &module, + const std::string &innerType, int count) { if (const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str())) return arrayChildList(sg, AddressSequence(address, innerTypeSize), - innerType, count); + module, innerType, count); return AbstractSymbolGroupNodePtrVector(); } @@ -361,7 +364,7 @@ static inline AbstractSymbolGroupNodePtrVector const std::string innerType = fixInnerType(SymbolGroupValue::stripPointerType(firstType), vec); if (SymbolGroupValue::verbose) DebugPrint() << n->name() << " inner type: '" << innerType << "' from '" << firstType << '\''; - return arrayChildList(n->symbolGroup(), address, innerType, count); + return arrayChildList(n->symbolGroup(), address, n->module(), innerType, count); } } } @@ -373,6 +376,7 @@ template AbstractSymbolGroupNodePtrVector stdDequeChildrenHelper(SymbolGroup *sg, const AddressType *blockArray, ULONG64 blockArraySize, + const std::string &module, const std::string &innerType, ULONG64 innerTypeSize, ULONG64 startOffset, ULONG64 dequeSize, int count) { @@ -389,7 +393,7 @@ AbstractSymbolGroupNodePtrVector block -= blockArraySize; const ULONG64 blockOffset = offset % dequeSize; const ULONG64 address = blockArray[block] + innerTypeSize * blockOffset; - if (SymbolGroupNode *n = sg->addSymbol(pointedToSymbolName(address, innerType), std::string(), &errorMessage)) { + if (SymbolGroupNode *n = sg->addSymbol(module, pointedToSymbolName(address, innerType), std::string(), &errorMessage)) { rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, n)); } else { return AbstractSymbolGroupNodePtrVector(); @@ -428,10 +432,10 @@ static inline AbstractSymbolGroupNodePtrVector const AbstractSymbolGroupNodePtrVector rc = SymbolGroupValue::pointerSize() == 8 ? stdDequeChildrenHelper(deque.node()->symbolGroup(), reinterpret_cast(mapArray), mapSize, - innerType, innerTypeSize, startOffset, dequeSize, count) : + deque.module(), innerType, innerTypeSize, startOffset, dequeSize, count) : stdDequeChildrenHelper(deque.node()->symbolGroup(), reinterpret_cast(mapArray), mapSize, - innerType, innerTypeSize, startOffset, dequeSize, count); + deque.module(), innerType, innerTypeSize, startOffset, dequeSize, count); delete [] mapArray; return rc; } @@ -660,7 +664,7 @@ static inline AbstractSymbolGroupNodePtrVector if (const SymbolGroupValue firstElementV = vec["p"]["array"][unsigned(0)]) { if (const ULONG64 arrayAddress = firstElementV.address()) { const std::string fixedInnerType = fixInnerType(firstElementV.type(), vec); - return arrayChildList(n->symbolGroup(), arrayAddress, fixedInnerType, count); + return arrayChildList(n->symbolGroup(), arrayAddress, n->module(), fixedInnerType, count); } } } @@ -721,7 +725,7 @@ static inline AbstractSymbolGroupNodePtrVector if (SymbolGroupValue::isPointerType(innerType)) // Quick check: Any pointer is T[] return arrayChildList(v.node()->symbolGroup(), AddressSequence(arrayAddress, pointerSize), - innerType, count); + v.module(), innerType, count); // Check condition for large||static. bool isLargeOrStatic = innerTypeSize > pointerSize; if (!isLargeOrStatic && !SymbolGroupValue::isPointerType(innerType)) { @@ -736,8 +740,11 @@ static inline AbstractSymbolGroupNodePtrVector if (void *data = readPointerArray(arrayAddress, count, v.context())) { // Generate sequence of addresses from pointer array 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); + arrayChildList(v.node()->symbolGroup(), + AddressArraySequence(reinterpret_cast(data)), + v.module(), innerType, count) : + arrayChildList(v.node()->symbolGroup(), AddressArraySequence(reinterpret_cast(data)), + v.module(), innerType, count); delete [] data; return rc; } @@ -745,7 +752,7 @@ static inline AbstractSymbolGroupNodePtrVector } return arrayChildList(v.node()->symbolGroup(), AddressSequence(arrayAddress, pointerSize), - innerType, count); + v.module(), innerType, count); } // Return the list of buckets of a 'QHash<>' as 'QHashData::Node *' values from @@ -755,6 +762,7 @@ SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string &hashNodeT const AddressType *pointerArray, int numBuckets, AddressType ePtr, + const std::string &module, const SymbolGroupValueContext &ctx) { SymbolGroupValueVector rc; @@ -766,7 +774,7 @@ SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string &hashNodeT for (const AddressType *p = pointerArray; p < end; p++) { if (*p != ePtr) { const std::string name = pointedToSymbolName(*p, hashNodeType); - if (SymbolGroupNode *child = sg->addSymbol(name, std::string(), &errorMessage)) { + if (SymbolGroupNode *child = sg->addSymbol(module, name, std::string(), &errorMessage)) { rc.push_back(SymbolGroupValue(child, ctx)); } else { return std::vector(); @@ -790,7 +798,7 @@ static inline std::string qHashNodeType(const SymbolGroupValue &v, // 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(); + const std::string currentModule = v.module(); return QtInfo::prependModuleAndNameSpace(qHashType, currentModule, qtInfo.nameSpace); } @@ -820,10 +828,10 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v, const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ? hashBuckets(v.node()->symbolGroup(), dummyNodeType, reinterpret_cast(bucketPointers), numBuckets, - ePtr, v.context()) : + ePtr, v.module(), v.context()) : hashBuckets(v.node()->symbolGroup(), dummyNodeType, reinterpret_cast(bucketPointers), numBuckets, - ULONG32(ePtr), v.context()); + ULONG32(ePtr), v.module(), v.context()); delete [] bucketPointers ; // Generate the list 'QHashData::Node *' by iterating over the linked list of // nodes starting at each bucket. Using the 'QHashData::Node *' instead of @@ -1001,8 +1009,8 @@ static inline AbstractSymbolGroupNodePtrVector ',' <addSymbol(keyExp, std::string(), &errorMessage); - SymbolGroupNode *valueNode = sg->addSymbol(valueExp, std::string(), &errorMessage); + SymbolGroupNode *keyNode = sg->addSymbol(v.module(), keyExp, std::string(), &errorMessage); + SymbolGroupNode *valueNode = sg->addSymbol(v.module(), valueExp, std::string(), &errorMessage); if (!keyNode || !valueNode) return AbstractSymbolGroupNodePtrVector(); rc.push_back(MapNodeSymbolGroupNode::create(int(i), keyAddress, diff --git a/src/libs/qtcreatorcdbext/extensioncontext.cpp b/src/libs/qtcreatorcdbext/extensioncontext.cpp index 08f3beabb51..0b97395afe2 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.cpp +++ b/src/libs/qtcreatorcdbext/extensioncontext.cpp @@ -189,6 +189,7 @@ void ExtensionContext::notifyState(ULONG Notify) case DEBUG_NOTIFY_SESSION_INACTIVE: report('E', 0, 0, "session_inactive", "%u", ex); discardSymbolGroup(); + discardWatchesSymbolGroup(); // We lost the debuggee, at this point restore output. if (ex & DEBUG_STATUS_NO_DEBUGGEE) unhookCallbacks(); @@ -196,11 +197,11 @@ void ExtensionContext::notifyState(ULONG Notify) } } -SymbolGroup *ExtensionContext::symbolGroup(CIDebugSymbols *symbols, ULONG threadId, int frame, std::string *errorMessage) +LocalsSymbolGroup *ExtensionContext::symbolGroup(CIDebugSymbols *symbols, ULONG threadId, int frame, std::string *errorMessage) { if (m_symbolGroup.get() && m_symbolGroup->frame() == frame && m_symbolGroup->threadId() == threadId) return m_symbolGroup.get(); - SymbolGroup *newSg = SymbolGroup::create(m_control.data(), symbols, threadId, frame, errorMessage); + LocalsSymbolGroup *newSg = LocalsSymbolGroup::create(m_control.data(), symbols, threadId, frame, errorMessage); if (!newSg) return 0; m_symbolGroup.reset(newSg); @@ -214,12 +215,36 @@ int ExtensionContext::symbolGroupFrame() const return -1; } +WatchesSymbolGroup *ExtensionContext::watchesSymbolGroup() const +{ + if (m_watchesSymbolGroup.get()) + return m_watchesSymbolGroup.get(); + return 0; +} + +WatchesSymbolGroup *ExtensionContext::watchesSymbolGroup(CIDebugSymbols *symbols, std::string *errorMessage) +{ + if (m_watchesSymbolGroup.get()) + return m_watchesSymbolGroup.get(); + WatchesSymbolGroup *newSg = WatchesSymbolGroup::create(symbols, errorMessage); + if (!newSg) + return 0; + m_watchesSymbolGroup.reset(newSg); + return newSg; +} + void ExtensionContext::discardSymbolGroup() { if (m_symbolGroup.get()) m_symbolGroup.reset(); } +void ExtensionContext::discardWatchesSymbolGroup() +{ + if (m_watchesSymbolGroup.get()) + m_watchesSymbolGroup.reset(); +} + bool ExtensionContext::report(char code, int token, int remainingChunks, const char *serviceName, PCSTR Format, ...) { if (!isInitialized()) diff --git a/src/libs/qtcreatorcdbext/extensioncontext.h b/src/libs/qtcreatorcdbext/extensioncontext.h index 0cae1c7e806..8ff9807cb73 100644 --- a/src/libs/qtcreatorcdbext/extensioncontext.h +++ b/src/libs/qtcreatorcdbext/extensioncontext.h @@ -40,7 +40,8 @@ #include #include -class SymbolGroup; +class LocalsSymbolGroup; +class WatchesSymbolGroup; // Global singleton with context. // Caches a symbolgroup per frame and thread as long as the session is accessible. @@ -86,9 +87,13 @@ public: void notifyIdle(); // Return symbol group for frame (cached as long as frame/thread do not change). - SymbolGroup *symbolGroup(CIDebugSymbols *symbols, ULONG threadId, int frame, std::string *errorMessage); + LocalsSymbolGroup *symbolGroup(CIDebugSymbols *symbols, ULONG threadId, int frame, std::string *errorMessage); int symbolGroupFrame() const; + WatchesSymbolGroup *watchesSymbolGroup(CIDebugSymbols *symbols, std::string *errorMessage); + WatchesSymbolGroup *watchesSymbolGroup() const; // Do not create. + void discardWatchesSymbolGroup(); + // Set a stop reason to be reported with the next idle notification (exception). void setStopReason(const StopReasonMap &, const std::string &reason = std::string()); @@ -97,7 +102,8 @@ private: void discardSymbolGroup(); IInterfacePointer m_control; - std::auto_ptr m_symbolGroup; + std::auto_ptr m_symbolGroup; + std::auto_ptr m_watchesSymbolGroup; CIDebugClient *m_hookedClient; IDebugEventCallbacks *m_oldEventCallback; diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def index e0e94f5f4e5..e8c47d447e8 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbext.def +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbext.def @@ -4,6 +4,7 @@ DebugExtensionUninitialize DebugExtensionNotify pid expandlocals +watches locals dumplocal typecast @@ -18,4 +19,5 @@ memory shutdownex test stack +addwatch KnownStructOutput diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 7c94e135376..8088081bec3 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -86,6 +86,7 @@ enum Command { CmdPid, CmdExpandlocals, CmdLocals, + CmdWatches, CmdDumplocal, CmdTypecast, CmdAddsymbol, @@ -98,6 +99,7 @@ enum Command { CmdMemory, CmdStack, CmdShutdownex, + CmdAddWatch, CmdTest }; @@ -111,7 +113,8 @@ static const CommandDescription commandDescriptions[] = { "-c complex dumpers"}, {"locals", "Prints local variables of symbol group in GDBMI or debug format", - "[-t token] [-v] [T formats] [-I formats] [-f debugfilter] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n [iname]\n" + "[-t token] [-v] [T formats] [-I formats] [-f debugfilter] [-c] [-h] [-d]\n[-e expand-list] [-u uninitialized-list]\n" + "[-W] [-w watch-iname watch-expression] [iname]\n" "-h human-readable ouput\n" "-v increase verboseness of dumping\n" "-d debug output\n" @@ -120,6 +123,18 @@ static const CommandDescription commandDescriptions[] = { "-e expand-list Comma-separated list of inames to be expanded beforehand\n" "-u uninitialized-list Comma-separated list of uninitialized inames\n" "-I formatmap map of 'hex-encoded-iname=typecode'\n" + "-T formatmap map of 'hex-encoded-type-name=typecode'\n" + "-W Synchronize watch items (-w)\n" + "-w iname expression Watch item"}, +{"watches", + "Prints watches variables of symbol group in GDBMI or debug format", + "[-t token] [-v] [T formats] [-I formats] [-f debugfilter] [-c] [-h] [-d] \n" + "-h human-readable ouput\n" + "-v increase verboseness of dumping\n" + "-d debug output\n" + "-f debug_filter\n" + "-c complex dumpers\n" + "-I formatmap map of 'hex-encoded-iname=typecode'\n" "-T formatmap map of 'hex-encoded-type-name=typecode'"}, {"dumplocal", "Dumps local variable using simple dumpers (testing command).", "[-t token] "}, @@ -139,12 +154,18 @@ static const CommandDescription commandDescriptions[] = { {"memory","Prints memory contents in Base64 encoding.","[-t token]
"}, {"stack","Prints stack in GDBMI format.","[-t token] [max-frames]"}, {"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""}, +{"addwatch","Add watch expression"," "}, {"test","Testing command","-T type"} }; typedef std::vector StringVector; typedef std::list StringList; +static inline bool isOption(const std::string &opt) +{ + return opt.size() == 2 && opt.at(0) == '-' && opt != "--"; +} + // Helper for commandTokens() below: // Simple splitting of command lines allowing for '"'-quoted tokens // 'typecast local.i "class QString *"' -> ('typecast','local.i','class QString *') @@ -294,81 +315,143 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args) return S_OK; } +// Parameters to be shared between watch/locals dump commands +struct DumpCommandParameters +{ + DumpCommandParameters() : debugOutput(0), verbose(0) {} + + // Check option off front and remove if parsed + enum ParseOptionResult { Ok, Error, OtherOption, EndOfOptions }; + ParseOptionResult parseOption(StringList *options); + + unsigned debugOutput; + std::string debugFilter; + DumpParameters dumpParameters; + unsigned verbose; +}; + +DumpCommandParameters::ParseOptionResult DumpCommandParameters::parseOption(StringList *options) +{ + // Parse all options and erase valid ones from the list + if (options->empty()) + return DumpCommandParameters::EndOfOptions; + + const std::string &opt = options->front(); + if (!isOption(opt)) + return DumpCommandParameters::EndOfOptions; + const char option = opt.at(1); + bool knownOption = true; + switch (option) { + case 'd': + debugOutput++; + break; + case 'h': + dumpParameters.dumpFlags |= DumpParameters::DumpHumanReadable; + break; + case 'c': + dumpParameters.dumpFlags |= DumpParameters::DumpComplexDumpers; + break; + case 'f': + if (options->size() < 2) + return Error; + options->pop_front(); + debugFilter = options->front(); + break; + case 'v': + verbose++; + break; + case 'T': // typeformats: 'hex'ed name = formatnumber,...' + if (options->size() < 2) + return Error; + options->pop_front(); + if (options->front().empty()) + return Error; + dumpParameters.typeFormats = DumpParameters::decodeFormatArgument(options->front()); + break; + case 'I': // individual formats: 'hex'ed name = formatnumber,...' + if (options->size() < 2) + return Error; + options->pop_front(); + dumpParameters.individualFormats = DumpParameters::decodeFormatArgument(options->front()); + break; + default: + knownOption = false; + break; + } // case option + if (knownOption) + options->pop_front(); + return knownOption ? Ok : OtherOption; +} + // Extension command 'locals': // Display local variables of symbol group in GDBMI or debug output form. // Takes an optional iname which is expanded before displaying (for updateWatchData) -static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *token, std::string *errorMessage) +static std::string commmandLocals(ExtensionCommandContext &commandExtCtx,PCSTR args, int *token, std::string *errorMessage) { + typedef WatchesSymbolGroup::InameExpressionMap InameExpressionMap; + typedef InameExpressionMap::value_type InameExpressionMapEntry; + // Parse the command - unsigned debugOutput = 0; + ExtensionContext &extCtx = ExtensionContext::instance(); + DumpCommandParameters parameters; std::string iname; - std::string debugFilter; StringList tokens = commandTokens(args, token); StringVector expandedInames; StringVector uninitializedInames; - DumpParameters parameters; - SymbolGroupValue::verbose = 0; + InameExpressionMap watcherInameExpressionMap; + bool watchSynchronization = false; // Parse away options - while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') { - const char option = tokens.front().at(1); - tokens.pop_front(); - switch (option) { - case 'd': - debugOutput++; + for (bool optionLeft = true; optionLeft && !tokens.empty(); ) { + switch (parameters.parseOption(&tokens)) { + case DumpCommandParameters::Ok: break; - case 'h': - parameters.dumpFlags |= DumpParameters::DumpHumanReadable; - break; - case 'c': - parameters.dumpFlags |= DumpParameters::DumpComplexDumpers; - break; - case 'u': - if (tokens.empty()) { - *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); - return std::string(); - } - split(tokens.front(), ',', std::back_inserter(uninitializedInames)); + case DumpCommandParameters::Error: + *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); + return std::string(); + case DumpCommandParameters::OtherOption: { + const char option = tokens.front().at(1); tokens.pop_front(); - break; - case 'f': - if (tokens.empty()) { - *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); - return std::string(); + switch (option) { + case 'u': + if (tokens.empty()) { + *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); + return std::string(); + } + split(tokens.front(), ',', std::back_inserter(uninitializedInames)); + tokens.pop_front(); + break; + case 'e': + if (tokens.empty()) { + *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); + return std::string(); + } + split(tokens.front(), ',', std::back_inserter(expandedInames)); + tokens.pop_front(); + break; + case 'w': { // Watcher iname exp + if (tokens.size() < 2) { + *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); + return std::string(); + } + const std::string iname = tokens.front(); + tokens.pop_front(); + const std::string expression = tokens.front(); + tokens.pop_front(); + watcherInameExpressionMap.insert(InameExpressionMapEntry(iname, expression)); } - debugFilter = tokens.front(); - tokens.pop_front(); break; - case 'v': - SymbolGroupValue::verbose++; + case 'W': + watchSynchronization = true; + break; + } // case option + } + break; + case DumpCommandParameters::EndOfOptions: + optionLeft = false; break; - case 'e': - if (tokens.empty()) { - *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); - return std::string(); - } - split(tokens.front(), ',', std::back_inserter(expandedInames)); - tokens.pop_front(); - break; - case 'T': // typeformats: 'hex'ed name = formatnumber,...' - if (tokens.empty()) { - *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); - return std::string(); - } - parameters.typeFormats = DumpParameters::decodeFormatArgument(tokens.front()); - tokens.pop_front(); - break; - case 'I': // individual formats: 'hex'ed name = formatnumber,...' - if (tokens.empty()) { - *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); - return std::string(); - } - parameters.individualFormats = DumpParameters::decodeFormatArgument(tokens.front()); - tokens.pop_front(); - break; - } // case option - } // for options - + } + } // Frame and iname unsigned frame; if (tokens.empty() || !integerFromString(tokens.front(), &frame)) { @@ -380,28 +463,70 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int * if (!tokens.empty()) iname = tokens.front(); - const SymbolGroupValueContext dumpContext(exc.dataSpaces(), exc.symbols()); - SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage); + const SymbolGroupValueContext dumpContext(commandExtCtx.dataSpaces(), commandExtCtx.symbols()); + SymbolGroup * const symGroup = extCtx.symbolGroup(commandExtCtx.symbols(), commandExtCtx.threadId(), frame, errorMessage); if (!symGroup) return std::string(); if (!uninitializedInames.empty()) symGroup->markUninitialized(uninitializedInames); - if (!expandedInames.empty()) { - if (parameters.dumpFlags & DumpParameters::DumpComplexDumpers) { - symGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage); + SymbolGroupValue::verbose = parameters.verbose; + + // Synchronize watches if desired. + WatchesSymbolGroup *watchesSymbolGroup = extCtx.watchesSymbolGroup(); + if (watchSynchronization) { + if (watcherInameExpressionMap.empty()) { // No watches..kill group. + watchesSymbolGroup = 0; + extCtx.discardWatchesSymbolGroup(); + if (SymbolGroupValue::verbose) + DebugPrint() << "Discarding watchers"; } else { - symGroup->expandList(expandedInames, errorMessage); + // Force group into existence + watchesSymbolGroup = extCtx.watchesSymbolGroup(commandExtCtx.symbols(), errorMessage); + if (!watchesSymbolGroup || !watchesSymbolGroup->synchronize(watcherInameExpressionMap, errorMessage)) + return std::string(); } } - if (debugOutput) - return symGroup->debug(iname, debugFilter, debugOutput - 1); + // Pre-expand. + if (!expandedInames.empty()) { + if (parameters.dumpParameters.dumpFlags & DumpParameters::DumpComplexDumpers) { + symGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage); + if (watchesSymbolGroup) + watchesSymbolGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage); + } else { + symGroup->expandList(expandedInames, errorMessage); + if (watchesSymbolGroup) + watchesSymbolGroup->expandList(expandedInames, errorMessage); + } + } - return iname.empty() ? - symGroup->dump(dumpContext, parameters) : - symGroup->dump(iname, dumpContext, parameters, errorMessage); + // Debug output: Join the 2 symbol groups if no iname is given. + if (parameters.debugOutput) { + std::string debugRc = symGroup->debug(iname, parameters.debugFilter, parameters.debugOutput - 1); + if (iname.empty() && watchesSymbolGroup) { + debugRc.push_back('\n'); + debugRc += watchesSymbolGroup->debug(iname, parameters.debugFilter, parameters.debugOutput - 1); + } + return debugRc; + } + + // Dump all: Join the 2 symbol groups '[local.x][watch.0]'->'[local.x,watch.0]' + if (iname.empty()) { + std::string dumpRc = symGroup->dump(dumpContext, parameters.dumpParameters); + if (!dumpRc.empty() && watchesSymbolGroup) { + std::string watchesDump = watchesSymbolGroup->dump(dumpContext, parameters.dumpParameters); + if (!watchesDump.empty()) { + dumpRc.erase(dumpRc.size() - 1, 1); // Strip ']' + watchesDump[0] = ','; // '[' -> '.' + dumpRc += watchesDump; + } + } + return dumpRc; + } + + return symGroup->dump(iname, dumpContext, parameters.dumpParameters, errorMessage); } extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args) @@ -410,6 +535,62 @@ extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args) std::string errorMessage; int token; const std::string output = commmandLocals(exc, args, &token, &errorMessage); + SymbolGroupValue::verbose = 0; + if (output.empty()) { + ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str()); + } else { + ExtensionContext::instance().reportLong('R', token, "locals", output); + } + return S_OK; +} + +// Extension command 'locals': +// Display local variables of symbol group in GDBMI or debug output form. +// Takes an optional iname which is expanded before displaying (for updateWatchData) + +static std::string commmandWatches(ExtensionCommandContext &exc, PCSTR args, int *token, std::string *errorMessage) +{ + // Parse the command + DumpCommandParameters parameters; + StringList tokens = commandTokens(args, token); + // Parse away options + for (bool optionLeft = true; optionLeft && !tokens.empty(); ) { + switch (parameters.parseOption(&tokens)) { + case DumpCommandParameters::Ok: + break; + case DumpCommandParameters::Error: + case DumpCommandParameters::OtherOption: + *errorMessage = singleLineUsage(commandDescriptions[CmdWatches]); + return std::string(); + case DumpCommandParameters::EndOfOptions: + optionLeft = false; + break; + } + } + const std::string iname = tokens.empty() ? std::string() : tokens.front(); + if (iname.empty() && !parameters.debugOutput) { + *errorMessage = singleLineUsage(commandDescriptions[CmdWatches]); + return std::string(); + } + + const SymbolGroupValueContext dumpContext(exc.dataSpaces(), exc.symbols()); + SymbolGroup * const symGroup = ExtensionContext::instance().watchesSymbolGroup(exc.symbols(), errorMessage); + if (!symGroup) + return std::string(); + SymbolGroupValue::verbose = parameters.verbose; + + if (parameters.debugOutput) + return symGroup->debug(iname, parameters.debugFilter, parameters.debugOutput - 1); + return symGroup->dump(iname, dumpContext, parameters.dumpParameters, errorMessage); +} + +extern "C" HRESULT CALLBACK watches(CIDebugClient *client, PCSTR args) +{ + ExtensionCommandContext exc(client); + std::string errorMessage = "e"; + int token = 0; + const std::string output = commmandWatches(exc, args, &token, &errorMessage); + SymbolGroupValue::verbose = 0; if (output.empty()) { ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str()); } else { @@ -520,7 +701,7 @@ extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args) } else { errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]); } - if (symGroup != 0 && symGroup->addSymbol(name, iname, &errorMessage)) { + if (symGroup != 0 && symGroup->addSymbol(std::string(), name, iname, &errorMessage)) { ExtensionContext::instance().report('R', token, 0, "addsymbol", "OK"); } else { ExtensionContext::instance().report('N', token, 0, "addsymbol", errorMessage.c_str()); @@ -528,6 +709,40 @@ extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args) return S_OK; } +extern "C" HRESULT CALLBACK addwatch(CIDebugClient *client, PCSTR argsIn) +{ + ExtensionCommandContext exc(client); + + std::string errorMessage; + std::string watchExpression; + std::string iname; + + int token = 0; + bool success = false; + do { + StringList tokens = commandTokens(argsIn, &token); + if (tokens.size() != 2) { + errorMessage = singleLineUsage(commandDescriptions[CmdAddWatch]); + break; + } + iname = tokens.front(); + tokens.pop_front(); + watchExpression = tokens.front(); + + WatchesSymbolGroup *watchesSymGroup = ExtensionContext::instance().watchesSymbolGroup(exc.symbols(), &errorMessage); + if (!watchesSymGroup) + break; + success = watchesSymGroup->addWatch(iname, watchExpression, &errorMessage); + } while (false); + + if (success) { + ExtensionContext::instance().report('R', token, 0, "addwatch", "Ok"); + } else { + ExtensionContext::instance().report('N', token, 0, "addwatch", errorMessage.c_str()); + } + return S_OK; +} + // Extension command 'assign': // Assign locals by iname: 'assign locals.x=5' @@ -766,7 +981,6 @@ extern "C" HRESULT CALLBACK test(CIDebugClient *client, PCSTR argsIn) return S_OK; } - // Hook for dumping Known Structs. Not currently used. // Shows up in 'dv' as well as IDebugSymbolGroup::GetValueText. diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index 1a194e6ff3b..e6fb0a8e7b2 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -32,6 +32,7 @@ **************************************************************************/ #include "symbolgroup.h" +#include "symbolgroupvalue.h" #include "stringutils.h" #include "gdbmihelpers.h" @@ -44,27 +45,16 @@ typedef std::vector::size_type VectorIndexType; typedef std::vector StringVector; enum { debug = 0 }; -const char rootNameC[] = "local"; // ------------- SymbolGroup SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg, const SymbolParameterVector &vec, - ULONG threadId, - unsigned frame, - const std::string &function) : + const std::string &rootModule, + const char *rootName) : m_symbolGroup(sg), - m_threadId(threadId), - m_frame(frame), - m_root(0), - m_function(function) + m_root(0) { - 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); - } + m_root = SymbolGroupNode::create(this, rootModule, rootName, vec); } SymbolGroup::~SymbolGroup() @@ -132,67 +122,26 @@ bool SymbolGroup::getSymbolParameters(CIDebugSymbolGroup *symbolGroup, return true; } -SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugSymbols, - ULONG threadId, unsigned frame, - std::string *errorMessage) +bool SymbolGroup::removeSymbol(AbstractSymbolGroupNode *an, std::string *errorMessage) { - errorMessage->clear(); - - ULONG obtainedFrameCount = 0; - const ULONG frameCount = frame + 1; - - DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[frameCount]; - IDebugSymbolGroup2 *idebugSymbols = 0; - bool success = false; - SymbolParameterVector parameters; - std::string func; - - // Obtain symbol group at stack frame. - do { - HRESULT hr = control->GetStackTrace(0, 0, 0, frames, frameCount, &obtainedFrameCount); - if (FAILED(hr)) { - *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr); - break; + bool ok = false; + // Remove a real SymbolGroupNode: Call its special remove method to correct indexes. + if (SymbolGroupNode *n = an->asSymbolGroupNode()) { + ok = n->removeSelf(root(), errorMessage); + } else { + // Normal removal, assuming parent is a base (needed for removing + // error nodes by the Watch group). + if (BaseSymbolGroupNode *bParent = dynamic_cast(an->parent())) { + bParent->removeChildAt(bParent->indexOf(an)); + ok = true; } - if (obtainedFrameCount < frameCount ) { - std::ostringstream str; - str << "Unable to obtain frame " << frame << " (" << obtainedFrameCount << ")."; - *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); - break; - } - hr = debugSymbols->SetScope(0, frames + frame, NULL, 0); - if (FAILED(hr)) { - *errorMessage = msgDebugEngineComFailed("SetScope", hr); - break; - } - // refresh with current frame - hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, idebugSymbols, &idebugSymbols); - if (FAILED(hr)) { - *errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr); - break; - } - if (!SymbolGroup::getSymbolParameters(idebugSymbols, ¶meters, errorMessage)) - break; - - success = true; - } while (false); - delete [] frames; - if (!success) { - if (idebugSymbols) - idebugSymbols->Release(); - return 0; } - return new SymbolGroup(idebugSymbols, parameters, threadId, frame, func); + if (!ok && errorMessage->empty()) + *errorMessage = "SymbolGroup::removeSymbol: Unimplemented removal operation for " + an->name(); + + if (!ok && SymbolGroupValue::verbose) + DebugPrint() << "SymbolGroup::removeSymbol failed: " << *errorMessage; + return ok; } static inline std::string msgNotFound(const std::string &nodeName) @@ -246,7 +195,7 @@ std::string SymbolGroup::dump(const std::string &iname, node->clearFlags(SymbolGroupNode::ExpandedByDumper); } else { if (node->canExpand() && !node->expand(errorMessage)) - return false; + return std::string(); } // After expansion, run the complex dumpers if (p.dumpFlags & DumpParameters::DumpComplexDumpers) @@ -312,22 +261,32 @@ struct InamePathEntryLessThan : public std::binary_function InamePathEntrySet; -static inline InamePathEntrySet expandEntrySet(const std::vector &nodes) +static inline InamePathEntrySet expandEntrySet(const std::vector &nodes, const std::string &root) { InamePathEntrySet pathEntries; + const std::string::size_type rootSize = root.size(); 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(SymbolGroupNodeVisitor::iNamePathSeparator, pos); - if (dotPos == std::string::npos) - dotPos = iname.size(); - pathEntries.insert(InamePathEntry(level, iname.substr(0, dotPos))); - pos = dotPos + 1; + const std::string &iname = nodes.at(i); // Silently skip items of another group + if (iname.size() >= rootSize && iname.compare(0, rootSize, root) == 0) { + 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(SymbolGroupNodeVisitor::iNamePathSeparator, pos); + if (dotPos == std::string::npos) + dotPos = iname.size(); + pathEntries.insert(InamePathEntry(level, iname.substr(0, dotPos))); + pos = dotPos + 1; + } } } + if (SymbolGroupValue::verbose) { + DebugPrint dp; + dp << "expandEntrySet "; + const InamePathEntrySet::const_iterator cend = pathEntries.end(); + for (InamePathEntrySet::const_iterator it = pathEntries.begin(); it != cend; ++it) + dp << it->second << ' '; + } return pathEntries; } @@ -339,7 +298,7 @@ unsigned SymbolGroup::expandList(const std::vector &nodes, std::str if (nodes.empty()) return 0; // Create a set with a key . Also required for 1 node (see above). - const InamePathEntrySet pathEntries = expandEntrySet(nodes); + const InamePathEntrySet pathEntries = expandEntrySet(nodes, root()->iName()); // Now expand going by level. unsigned succeeded = 0; std::string nodeError; @@ -367,7 +326,7 @@ unsigned SymbolGroup::expandListRunComplexDumpers(const std::vector if (nodes.empty()) return 0; // Create a set with a key . Also required for 1 node (see above). - const InamePathEntrySet pathEntries = expandEntrySet(nodes); + const InamePathEntrySet pathEntries = expandEntrySet(nodes, root()->iName()); // Now expand going by level. unsigned succeeded = 0; std::string nodeError; @@ -440,9 +399,9 @@ bool SymbolGroup::typeCast(const std::string &iname, const std::string &desiredT return node->typeCast(desiredType, errorMessage); } -SymbolGroupNode *SymbolGroup::addSymbol(const std::string &name, const std::string &iname, std::string *errorMessage) +SymbolGroupNode *SymbolGroup::addSymbol(const std::string &module, const std::string &name, const std::string &iname, std::string *errorMessage) { - return m_root->addSymbolByName(name, iname, errorMessage); + return m_root->addSymbolByName(module, name, iname, errorMessage); } // Mark uninitialized (top level only) @@ -554,3 +513,198 @@ AbstractSymbolGroupNode *SymbolGroup::find(const std::string &iname) const DebugPrint() << "SymbolGroup::find " << iname << ' ' << rc; return rc; } + +// --------- LocalsSymbolGroup +LocalsSymbolGroup::LocalsSymbolGroup(CIDebugSymbolGroup *sg, + const SymbolParameterVector &vec, + ULONG threadId, unsigned frame, + const std::string &function) : + SymbolGroup(sg, vec, SymbolGroupValue::moduleOfType(function), "local"), + m_threadId(threadId), + m_frame(frame), + m_function(function) +{ + // "module!function" -> "function" + if (const std::string::size_type moduleLengh = module().size()) + m_function.erase(0, moduleLengh + 1); +} + +LocalsSymbolGroup *LocalsSymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugSymbols, + ULONG threadId, unsigned frame, + std::string *errorMessage) +{ + errorMessage->clear(); + + ULONG obtainedFrameCount = 0; + const ULONG frameCount = frame + 1; + + DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[frameCount]; + IDebugSymbolGroup2 *idebugSymbols = 0; + bool success = false; + SymbolParameterVector parameters; + std::string func; + + // Obtain symbol group at stack frame. + do { + HRESULT hr = control->GetStackTrace(0, 0, 0, frames, frameCount, &obtainedFrameCount); + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("GetStackTrace", hr); + break; + } + if (obtainedFrameCount < frameCount ) { + std::ostringstream str; + str << "Unable to obtain frame " << frame << " (" << obtainedFrameCount << ")."; + *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); + break; + } + hr = debugSymbols->SetScope(0, frames + frame, NULL, 0); + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("SetScope", hr); + break; + } + // refresh with current frame + hr = debugSymbols->GetScopeSymbolGroup2(DEBUG_SCOPE_GROUP_LOCALS, idebugSymbols, &idebugSymbols); + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("GetScopeSymbolGroup2", hr); + break; + } + if (!SymbolGroup::getSymbolParameters(idebugSymbols, ¶meters, errorMessage)) + break; + + success = true; + } while (false); + delete [] frames; + if (!success) { + if (idebugSymbols) + idebugSymbols->Release(); + return 0; + } + return new LocalsSymbolGroup(idebugSymbols, parameters, threadId, frame, func); +} + +std::string LocalsSymbolGroup::module() const +{ + return root()->module(); +} + +// ----------- WatchSymbolGroup + +const char *WatchesSymbolGroup::watchInamePrefix = "watch"; + +WatchesSymbolGroup::WatchesSymbolGroup(CIDebugSymbolGroup *sg) : + SymbolGroup(sg, SymbolParameterVector(), std::string(), WatchesSymbolGroup::watchInamePrefix) +{ +} + +bool WatchesSymbolGroup::addWatch(std::string iname, const std::string &expression, std::string *errorMessage) +{ + // "watch.0" -> "0" + const size_t prefLen = std::strlen(WatchesSymbolGroup::watchInamePrefix); + if (!iname.compare(0, prefLen, WatchesSymbolGroup::watchInamePrefix)) + iname.erase(0, prefLen + 1); + // Already in? + if (root()->childByIName(iname.c_str())) + return true; + // TODO: Figure out module + SymbolGroupNode *node = addSymbol(std::string(), expression, iname, errorMessage); + if (!node) + return false; + node->addFlags(SymbolGroupNode::WatchNode); + return true; +} + +// Compile map of current state root-iname->root-expression +WatchesSymbolGroup::InameExpressionMap + WatchesSymbolGroup::currentInameExpressionMap() const +{ + InameExpressionMap rc; + if (unsigned size = unsigned(root()->children().size())) + for (unsigned i = 0; i < size; i++) { + const AbstractSymbolGroupNode *n = root()->childAt(i); + rc.insert(InameExpressionMap::value_type(n->iName(), n->name())); + } + return rc; +} + +// Synchronize watches passing on a map of '0' -> '*(int *)(0xA0)' +bool WatchesSymbolGroup::synchronize(const InameExpressionMap &newInameExpMap, std::string *errorMessage) +{ + typedef std::set StringSet; + typedef InameExpressionMap::const_iterator InameExpressionMapConstIt; + InameExpressionMap oldInameExpMap = currentInameExpressionMap(); + const bool changed = oldInameExpMap != newInameExpMap; + if (SymbolGroupValue::verbose) + DebugPrint() << "WatchesSymbolGroup::synchronize oldsize=" << oldInameExpMap.size() + << " newsize=" << newInameExpMap.size() << " items, changed=" << changed; + if (!changed) // Quick check: All ok + return true; + // Check both maps against each other and determine elements to be deleted/added. + StringSet deletionSet; + InameExpressionMap addMap; + InameExpressionMapConstIt ocend = oldInameExpMap.end(); + InameExpressionMapConstIt ncend = newInameExpMap.end(); + // Check for new items or ones whose expression changed. + for (InameExpressionMapConstIt nit = newInameExpMap.begin(); nit != ncend; ++nit) { + const InameExpressionMapConstIt oit = oldInameExpMap.find(nit->first); + if (oit == ocend || oit->second != nit->second) + addMap.insert(InameExpressionMap::value_type(nit->first, nit->second)); + } + for (InameExpressionMapConstIt oit = oldInameExpMap.begin(); oit != ocend; ++oit) { + const InameExpressionMapConstIt nit = newInameExpMap.find(oit->first); + if (nit == ncend || nit->second != oit->second) + deletionSet.insert(oit->first); + } + if (SymbolGroupValue::verbose) + DebugPrint() << "add=" << addMap.size() << " delete=" << deletionSet.size(); + // Do insertion/deletion + if (!deletionSet.empty()) { + const StringSet::const_iterator dcend = deletionSet.end(); + for (StringSet::const_iterator it = deletionSet.begin(); it != dcend; ++it) { + AbstractSymbolGroupNode *n = root()->childByIName(it->c_str()); + if (!n) + return false; + if (SymbolGroupValue::verbose) + DebugPrint() << " Deleting " << n->name(); + if (!removeSymbol(n, errorMessage)) + return false; + } + } + // Insertion: We cannot possible fail here since this will trigger an + // endless loop of the watch model. Insert a dummy item. + if (!addMap.empty()) { + const InameExpressionMapConstIt acend = addMap.end(); + for (InameExpressionMapConstIt it = addMap.begin(); it != acend; ++it) { + const bool success = addWatch(it->first, it->second, errorMessage); + if (SymbolGroupValue::verbose) + DebugPrint() << " Adding " << it->first << ',' << it->second << ",success=" << success; + if (!success) + root()->addChild(new ErrorSymbolGroupNode(it->second, it->first)); + } + } + return true; +} + +// Create scopeless group. +WatchesSymbolGroup *WatchesSymbolGroup::create(CIDebugSymbols *symbols, + std::string *errorMessage) +{ + errorMessage->clear(); + + IDebugSymbolGroup2 *idebugSymbols = 0; + const HRESULT hr = symbols->CreateSymbolGroup2(&idebugSymbols); + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("CreateSymbolGroup", hr); + return 0; + } + return new WatchesSymbolGroup(idebugSymbols); +} diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h index 19c8fd763aa..fa598c1cdb9 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.h +++ b/src/libs/qtcreatorcdbext/symbolgroup.h @@ -37,6 +37,8 @@ #include "common.h" #include "symbolgroupnode.h" +#include + /* A symbol group storing a tree of expanded symbols rooted on * a fake "locals" root element. * Provides a find() method based on inames ("locals.this.i1.data") and @@ -53,20 +55,16 @@ private: SymbolGroup(const SymbolGroup &); SymbolGroup &operator=(const SymbolGroup &); +protected: explicit SymbolGroup(CIDebugSymbolGroup *, const SymbolParameterVector &vec, - ULONG threadId, unsigned frame, - const std::string &function); + const std::string &rootModule, + const char *rootName); public: typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector; - static SymbolGroup *create(CIDebugControl *control, - CIDebugSymbols *, - ULONG threadId, - unsigned frame, - std::string *errorMessage); - ~SymbolGroup(); + virtual ~SymbolGroup(); // Dump all std::string dump(const SymbolGroupValueContext &ctx, @@ -78,10 +76,6 @@ public: 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; } AbstractSymbolGroupNode *find(const std::string &iname) const; @@ -103,7 +97,8 @@ public: // Cast an (unexpanded) node bool typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage); // Add a symbol by name expression - SymbolGroupNode *addSymbol(const std::string &name, // Expression like 'myarray[1]' + SymbolGroupNode *addSymbol(const std::string &module, + const std::string &name, // Expression like 'myarray[1]' const std::string &iname, // Desired iname, defaults to name std::string *errorMessage); @@ -122,18 +117,65 @@ public: SymbolParameterVector *vec, std::string *errorMessage); -private: - inline AbstractSymbolGroupNode *findI(const std::string &iname) const; +protected: static bool getSymbolParameters(CIDebugSymbolGroup *m_symbolGroup, SymbolParameterVector *vec, std::string *errorMessage); + bool removeSymbol(AbstractSymbolGroupNode *n, std::string *errorMessage); + +private: + inline AbstractSymbolGroupNode *findI(const std::string &iname) const; CIDebugSymbolGroup * const m_symbolGroup; + SymbolGroupNode *m_root; +}; + +/* Symbol group representing the Locals view. It is firmly associated + * with stack frame, function (module) and thread. */ + +class LocalsSymbolGroup : public SymbolGroup { +protected: + explicit LocalsSymbolGroup(CIDebugSymbolGroup *, + const SymbolParameterVector &vec, + ULONG threadId, unsigned frame, + const std::string &function); +public: + unsigned frame() const { return m_frame; } + std::string function() const { return m_function; } + std::string module() const; + ULONG threadId() const { return m_threadId; } + + static LocalsSymbolGroup *create(CIDebugControl *control, + CIDebugSymbols *, + ULONG threadId, + unsigned frame, + std::string *errorMessage); + +private: const unsigned m_frame; const ULONG m_threadId; - SymbolGroupNode *m_root; std::string m_function; - std::string m_module; +}; + +// Watch symbol group. Contains watches as added by Qt Creator as iname='watch.0', +// name=''. The IDebugSymbolGroup is created without scope. +class WatchesSymbolGroup : public SymbolGroup { +public: + typedef std::map InameExpressionMap; + + static const char *watchInamePrefix; + + // Add a symbol as 'watch.0' or '0' with expression + bool addWatch(std::string iname, const std::string &expression, std::string *errorMessage); + // Synchronize watches passing on a map of '0' -> '*(int *)(0xA0)' + bool synchronize(const InameExpressionMap &m, std::string *errorMessage); + + static WatchesSymbolGroup *create(CIDebugSymbols *, std::string *errorMessage); + +private: + explicit WatchesSymbolGroup(CIDebugSymbolGroup *); + InameExpressionMap currentInameExpressionMap() const; + inline SymbolGroupNode *rootChildByIname(const std::string &iname) const; }; #endif // SYMBOLGROUP_H diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp index 0653444bec2..d1fb2ca394f 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.cpp @@ -73,6 +73,8 @@ static inline void debugNodeFlags(std::ostream &str, unsigned f) str << " Obscured"; if (f & SymbolGroupNode::ComplexDumperOk) str << " ComplexDumperOk"; + if (f & SymbolGroupNode::WatchNode) + str << " WatchNode"; str << ' '; } @@ -135,6 +137,12 @@ AbstractSymbolGroupNode *AbstractSymbolGroupNode::childByIName(const char *n) co return 0; } +unsigned AbstractSymbolGroupNode::indexOf(const AbstractSymbolGroupNode *n) const +{ + const AbstractSymbolGroupNodePtrVector::const_iterator it = std::find(children().begin(), children().end(), n); + return it != children().end() ? unsigned(it - children().begin()) : unsigned(-1); +} + bool AbstractSymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, const std::string &parentIname, unsigned child, unsigned depth) @@ -208,6 +216,15 @@ BaseSymbolGroupNode::~BaseSymbolGroupNode() removeChildren(); } +void BaseSymbolGroupNode::removeChildAt(unsigned n) +{ + if (VectorIndexType(n) >= m_children.size()) + return; + const AbstractSymbolGroupNodePtrVector::iterator it = m_children.begin() + n; + delete *it; + m_children.erase(it, it + 1); +} + void BaseSymbolGroupNode::removeChildren() { if (!m_children.empty()) { @@ -421,15 +438,37 @@ bool DumpParameters::recode(const std::string &type, return true; } +// --------- ErrorSymbolGroupNode +ErrorSymbolGroupNode::ErrorSymbolGroupNode(const std::string &name, const std::string &iname) : + BaseSymbolGroupNode(name, iname) +{ +} + +int ErrorSymbolGroupNode::dump(std::ostream &str, const std::string &fullIname, + const DumpParameters &, const SymbolGroupValueContext &) +{ + dumpBasicData(str, name(), fullIname, "", std::string()); + str << ",valueencoded=\"0\",value=\"\",valueenabled=\"false\",valueeditable=\"false\""; + return 0; +} + +void ErrorSymbolGroupNode::debug(std::ostream &os, const std::string &visitingFullIname, + unsigned , unsigned depth) const +{ + indentStream(os, 2 * depth); + os << "ErrorSymbolGroupNode '" << name() << "','" << iName() << "', '" << visitingFullIname << "'\n"; +} + // ------- SymbolGroupNode SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup, ULONG index, + const std::string &module, const std::string &name, const std::string &iname) : BaseSymbolGroupNode(name, iname), m_symbolGroup(symbolGroup), - m_index(index), m_dumperType(-1), m_dumperContainerSize(-1), m_dumperSpecialInfo(0) + m_module(module), m_index(index), m_dumperType(-1), m_dumperContainerSize(-1), m_dumperSpecialInfo(0) { memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS)); m_parameters.ParentSymbol = DEBUG_ANY_ID; @@ -442,6 +481,13 @@ const SymbolGroupNode *SymbolGroupNode::symbolGroupNodeParent() const return 0; } +SymbolGroupNode *SymbolGroupNode::symbolGroupNodeParent() +{ + if (AbstractSymbolGroupNode *p = parent()) + return p->asSymbolGroupNode(); + return 0; +} + bool SymbolGroupNode::isArrayElement() const { if (const SymbolGroupNode *p = symbolGroupNodeParent()) @@ -453,15 +499,20 @@ bool SymbolGroupNode::isArrayElement() const // 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) +bool SymbolGroupNode::notifyIndexesMoved(ULONG index, bool inserted, ULONG offset) { typedef AbstractSymbolGroupNodePtrVector::const_reverse_iterator ReverseIt; + + if (SymbolGroupValue::verbose > 2) + DebugPrint() << "notifyIndexesMoved: #" << this->index() << " '" << name() + << "' index=" << index << " insert=" + << inserted << " offset=" << offset; // 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)) + if (!c->notifyIndexesMoved(index, inserted, offset)) return false; } @@ -469,9 +520,18 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount) 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; + if (inserted) { + m_index += offset; + } else { + m_index -= offset; + } + if (m_parameters.ParentSymbol != DEBUG_ANY_ID && m_parameters.ParentSymbol >= index) { + if (inserted) { + m_parameters.ParentSymbol += offset; + } else { + m_parameters.ParentSymbol -= offset; + } + } return true; } @@ -596,6 +656,7 @@ void SymbolGroupNode::parseParameters(VectorIndexType index, const VectorIndexType symbolGroupIndex = pos + parameterOffset; SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup, ULONG(symbolGroupIndex), + m_module, names.at(nameIndex), inames.at(nameIndex)); child->parseParameters(symbolGroupIndex, parameterOffset, vec); @@ -607,9 +668,10 @@ void SymbolGroupNode::parseParameters(VectorIndexType index, m_parameters.SubElements = ULONG(children().size()); } -SymbolGroupNode *SymbolGroupNode::create(SymbolGroup *sg, const std::string &name, const SymbolGroup::SymbolParameterVector &vec) +SymbolGroupNode *SymbolGroupNode::create(SymbolGroup *sg, const std::string &module, + const std::string &name, const SymbolGroup::SymbolParameterVector &vec) { - SymbolGroupNode *rc = new SymbolGroupNode(sg, DEBUG_ANY_ID, name, name); + SymbolGroupNode *rc = new SymbolGroupNode(sg, DEBUG_ANY_ID, module, name, name); rc->parseParameters(DEBUG_ANY_ID, 0, vec); return rc; } @@ -833,6 +895,24 @@ int SymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullInam return dumpNode(str, name(), visitingFullIname, p, ctx); } +// Return a watch expression basically as "*(type *)(address)" +static inline std::string watchExpression(ULONG64 address, + const std::string &type, + int /* kType */, + const std::string &module) +{ + std::ostringstream str; + str << "*("; + // Try to make watch expressions faster by at least qualifying templates with + // the local module. We cannot do expensive type lookup here. + // We could insert a placeholder here for non-POD types indicating + // that a type lookup should be done when inserting watches? + if (!module.empty() && type.find('>') != std::string::npos) + str << module << '!'; + str << SymbolGroupValue::pointerType(SymbolGroupValue::stripClassPrefixes(type)) << ')' << std::hex << std::showbase << address; + return str.str(); +} + int SymbolGroupNode::dumpNode(std::ostream &str, const std::string &aName, const std::string &aFullIName, @@ -840,11 +920,12 @@ int SymbolGroupNode::dumpNode(std::ostream &str, const SymbolGroupValueContext &ctx) { const std::string t = type(); - SymbolGroupNode::dumpBasicData(str, aName, aFullIName, t, aFullIName); + const ULONG64 addr = address(); + SymbolGroupNode::dumpBasicData(str, aName, aFullIName, t, + watchExpression(addr, t, m_dumperType, m_module)); - if (const ULONG64 addr = address()) - str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec - << '"'; + if (addr) + str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec << '"'; const bool uninitialized = flags() & Uninitialized; bool valueEditable = !uninitialized; @@ -896,7 +977,7 @@ void SymbolGroupNode::debug(std::ostream &str, str << "AbsIname=" << fullIname << '"'; if (fullIname != visitingFullIname) str << ",VisitIname=\"" <indexOf(this); + if (parentPosition == unsigned(-1)) { + *errorMessage = "SymbolGroupNode::removeSelf: Internal error 2."; + return false; + } + // Determine the index for the following symbols to be subtracted by finding the next + // 'real' SymbolGroupNode child (right or up the tree-right) and taking its index + const ULONG oldIndex = index(); + const ULONG nextSymbolIdx = nextSymbolIndex(); + const ULONG offset = nextSymbolIdx > oldIndex ? nextSymbolIdx - oldIndex : 0; + if (SymbolGroupValue::verbose) + DebugPrint() << "SymbolGroupNode::removeSelf #" << index() << " '" << name() + << "' at pos=" << parentPosition<< " (" << sParent->children().size() + << ") of parent '" << sParent->name() + << "' nextIndex=" << nextSymbolIdx << " offset=" << offset << *errorMessage; + // Remove from symbol group + const HRESULT hr = symbolGroup()->debugSymbolGroup()->RemoveSymbolByIndex(oldIndex); + if (FAILED(hr)) { + *errorMessage = msgDebugEngineComFailed("RemoveSymbolByIndex", hr); + return false; + } + // Move the following symbol indexes: We no longer exist below here. + sParent ->removeChildAt(parentPosition); + if (offset) + root->notifyIndexesMoved(oldIndex + 1, false, offset); + return true; +} + static inline std::string msgCannotAddSymbol(const std::string &name, const std::string &why) { std::ostringstream str; @@ -1028,7 +1162,8 @@ static inline std::string msgCannotAddSymbol(const std::string &name, const std: } // For root nodes, only: Add a new symbol by name -SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name, +SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &module, + const std::string &name, const std::string &iname, std::string *errorMessage) { @@ -1059,7 +1194,7 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name, return 0; } SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index, - name, iname.empty() ? name : iname); + module, name, iname.empty() ? name : iname); node->parseParameters(0, 0, parameters); node->addFlags(AdditionalSymbol); addChild(node); @@ -1210,8 +1345,10 @@ SymbolGroupNodeVisitor::VisitResult const std::string &fullIname, unsigned /* child */, unsigned depth) { - // Show container children only, no additional symbol below root. - if (node->testFlags(SymbolGroupNode::Obscured|SymbolGroupNode::AdditionalSymbol)) + // Show container children only, no additional symbol below root (unless it is a watch node). + if (node->testFlags(SymbolGroupNode::Obscured)) + return VisitSkipChildren; + if (node->testFlags(SymbolGroupNode::AdditionalSymbol) && !node->testFlags(SymbolGroupNode::WatchNode)) return VisitSkipChildren; // Recurse to children only if expanded by explicit watchmodel request // and initialized. diff --git a/src/libs/qtcreatorcdbext/symbolgroupnode.h b/src/libs/qtcreatorcdbext/symbolgroupnode.h index f7b2adf96db..284b8c8c2d8 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupnode.h +++ b/src/libs/qtcreatorcdbext/symbolgroupnode.h @@ -103,8 +103,10 @@ public: unsigned indexByIName(const char *) const; // (unsigned(-1) on failure AbstractSymbolGroupNode *childByIName(const char *) const; + unsigned indexOf(const AbstractSymbolGroupNode *) const; const AbstractSymbolGroupNode *parent() const { return m_parent; } + AbstractSymbolGroupNode *parent() { return m_parent; } unsigned flags() const { return m_flags; } bool testFlags(unsigned f) const { return (m_flags & f) != 0; } @@ -151,20 +153,32 @@ class BaseSymbolGroupNode : public AbstractSymbolGroupNode { public: virtual const AbstractSymbolGroupNodePtrVector &children() const { return m_children; } + void addChild(AbstractSymbolGroupNode *c); // for watches + void removeChildAt(unsigned); 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(); }; +// Dummy fake node to satisfy the watch model for failed additions. Reports error. +class ErrorSymbolGroupNode : public BaseSymbolGroupNode +{ +public: + explicit ErrorSymbolGroupNode(const std::string &name, const std::string &iname); + + virtual int dump(std::ostream &str, const std::string &fullIname, + const DumpParameters &p, const SymbolGroupValueContext &ctx); + virtual void debug(std::ostream &os, const std::string &visitingFullIname, + unsigned verbosity, unsigned depth) const; +}; + /* SymbolGroupNode: 'Real' node within a symbol group, identified by its index * in IDebugSymbolGroup. * Provides accessors for fixed-up symbol group value and a dumping facility @@ -187,6 +201,7 @@ class SymbolGroupNode : public BaseSymbolGroupNode { explicit SymbolGroupNode(SymbolGroup *symbolGroup, ULONG index, + const std::string &module, const std::string &name, const std::string &iname); @@ -201,7 +216,8 @@ public: ExpandedByDumper = 0x10, AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible Obscured = 0x40, // Symbol is obscured by (for example) fake container children - ComplexDumperOk = 0x80 + ComplexDumperOk = 0x80, + WatchNode = 0x100 }; typedef std::vector SymbolParameterVector; @@ -210,9 +226,10 @@ public: SymbolParameterVector::size_type parameterOffset, const SymbolParameterVector &vec); - static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec); + static SymbolGroupNode *create(SymbolGroup *sg, const std::string &module, 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]' + SymbolGroupNode *addSymbolByName(const std::string &module, + const std::string &name, // Expression like 'myarray[1]' const std::string &iname, // Desired iname, defaults to name std::string *errorMessage); @@ -239,6 +256,7 @@ public: int dumperContainerSize() { return m_dumperContainerSize; } // Valid after dumper run unsigned size() const; // Size of value ULONG64 address() const; + std::string module() const { return m_module; } bool expand(std::string *errorMessage); bool expandRunComplexDumpers(const SymbolGroupValueContext &ctx, std::string *errorMessage); @@ -254,15 +272,21 @@ public: virtual SymbolGroupNode *asSymbolGroupNode() { return this; } virtual const SymbolGroupNode *asSymbolGroupNode() const { return this; } + // Remove self off parent and return the indexes to be shifted or unsigned(-1). + bool removeSelf(SymbolGroupNode *root, std::string *errorMessage); + private: const SymbolGroupNode *symbolGroupNodeParent() const; + SymbolGroupNode *symbolGroupNodeParent(); bool isArrayElement() const; - // Notify about expansion of a node, shift indexes - bool notifyExpanded(ULONG index, ULONG insertedCount); + // Notify about expansion/collapsing of a node, shift indexes + bool notifyIndexesMoved(ULONG index, bool inserted, ULONG offset); bool runSimpleDumpers(const SymbolGroupValueContext &ctx); std::wstring simpleDumpValue(const SymbolGroupValueContext &ctx); + ULONG nextSymbolIndex() const; SymbolGroup *const m_symbolGroup; + const std::string m_module; ULONG m_index; DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct. std::wstring m_dumperValue; diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index 5178efbe05d..ec5fd2b54e1 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -198,6 +198,11 @@ ULONG64 SymbolGroupValue::address() const return 0; } +std::string SymbolGroupValue::module() const +{ + return isValid() ? m_node->module() : std::string(); +} + // Temporary iname static inline std::string additionalSymbolIname(const SymbolGroup *g) { @@ -236,7 +241,7 @@ SymbolGroupValue SymbolGroupValue::typeCastedValue(ULONG64 address, const char * if (nonPointer) str << " *"; str << ")(" << std::showbase << std::hex << address << ')'; - if (SymbolGroupNode *node = sg->addSymbol(str.str(), + if (SymbolGroupNode *node = sg->addSymbol(module(), str.str(), additionalSymbolIname(sg), &m_errorMessage)) return SymbolGroupValue(node, m_context); @@ -277,6 +282,16 @@ unsigned SymbolGroupValue::isPointerType(const std::string &t) return 0; } +// add pointer type 'Foo' -> 'Foo *', 'Foo *' -> 'Foo **' +std::string SymbolGroupValue::pointerType(const std::string &type) +{ + std::string rc = type; + if (!endsWith(type, '*')) + rc.push_back(' '); + rc.push_back('*'); + return rc; +} + unsigned SymbolGroupValue::pointerSize() { static const unsigned ps = SymbolGroupValue::sizeOf("char *"); @@ -356,6 +371,12 @@ std::string SymbolGroupValue::stripModuleFromType(const std::string &type) return exclPos != std::string::npos ? type.substr(exclPos + 1, type.size() - exclPos - 1) : type; } +std::string SymbolGroupValue::moduleOfType(const std::string &type) +{ + const std::string::size_type exclPos = type.find('!'); + return exclPos != std::string::npos ? type.substr(0, exclPos) : std::string(); +} + /* 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. @@ -511,7 +532,7 @@ std::list // Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString') std::string SymbolGroupValue::resolveType(const std::string &typeIn, const SymbolGroupValueContext &ctx, - const SymbolGroup *current /* = 0 */) + const std::string ¤tModule /* = "" */) { enum { BufSize = 512 }; @@ -523,8 +544,8 @@ std::string SymbolGroupValue::resolveType(const std::string &typeIn, // Use the module of the current symbol group for templates. // This is because resolving some template types (std::list<> has been // observed to result in 'QtGui4d!std::list', which subseqently fails. - if (current && stripped.find('<') != std::string::npos) { - std::string trc = current->module(); + if (!currentModule.empty() && stripped.find('<') != std::string::npos) { + std::string trc = currentModule; trc.push_back('!'); trc += stripped; return trc; @@ -1345,7 +1366,7 @@ static inline bool dumpQWidget(const SymbolGroupValue &v, std::wostream &str, vo str << '(' << qwPrivateType << "*)(" << std::showbase << std::hex << v.address() << ')'; const std::string name = str.str(); SymbolGroupNode *qwPrivateNode - = v.node()->symbolGroup()->addSymbol(name, std::string(), &errorMessage); + = v.node()->symbolGroup()->addSymbol(v.module(), name, std::string(), &errorMessage); qwPrivate = SymbolGroupValue(qwPrivateNode, v.context()); } const SymbolGroupValue oName = qwPrivate[unsigned(0)]["objectName"]; // QWidgetPrivate inherits QObjectPrivate @@ -1414,8 +1435,8 @@ static inline std::string const QtInfo &qtInfo, const SymbolGroupValue &contextHelper) { - const std::string module = contextHelper.node()->symbolGroup()->module(); - std::string rc = QtInfo::prependModuleAndNameSpace(containerType, module, qtInfo.nameSpace); + std::string rc = QtInfo::prependModuleAndNameSpace(containerType, contextHelper.module(), + qtInfo.nameSpace); rc.push_back('<'); rc += QtInfo::prependModuleAndNameSpace(innerType1, std::string(), qtInfo.nameSpace); if (!innerType2.empty()) { diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.h b/src/libs/qtcreatorcdbext/symbolgroupvalue.h index 0a955982a97..689a5123404 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.h +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.h @@ -97,6 +97,8 @@ public: double floatValue(double defaultValue = -999) const; ULONG64 pointerValue(ULONG64 defaultValue = 0) const; ULONG64 address() const; + std::string module() const; + // Return allocated array of data pointed to unsigned char *pointerData(unsigned length) const; // Return data pointed to as wchar_t/std::wstring (UTF16) @@ -114,15 +116,18 @@ public: static std::string addPointerType(const std::string &); static std::string stripArrayType(const std::string &); static std::string stripModuleFromType(const std::string &type); + static std::string moduleOfType(const std::string &type); // pointer type, return number of characters to strip static unsigned isPointerType(const std::string &); + // add pointer type 'Foo' -> 'Foo *', 'Foo *' -> 'Foo **' + static std::string pointerType(const std::string &type); // Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString') // Some operations on types (like adding symbols may fail non-deterministically // or be slow when the module specification is omitted). // If a current SymbolGroup is passed on, its module will be used for templates. static std::string resolveType(const std::string &type, const SymbolGroupValueContext &ctx, - const SymbolGroup *current = 0); + const std::string ¤tModule = std::string()); static std::list resolveSymbol(const char *pattern, const SymbolGroupValueContext &c, diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 67dbd67d62c..96440d4e7df 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -85,6 +85,7 @@ Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*) enum { debug = 0 }; enum { debugLocals = 0 }; +enum { debugWatches = 0 }; enum { debugBreakpoints = 0 }; #if 0 @@ -703,16 +704,35 @@ void CdbEngine::detachDebugger() postCommand(".detach", 0); } +static inline bool isWatchIName(const QByteArray &iname) +{ + return iname.startsWith("watch"); +} + void CdbEngine::updateWatchData(const WatchData &dataIn, const WatchUpdateFlags & flags) { - if (debug) - qDebug("CdbEngine::updateWatchData() %dms %s incr=%d: %s", - elapsedLogTime(), stateName(state()), + if (debug || debugLocals || debugWatches) + qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s", + elapsedLogTime(), m_accessible, stateName(state()), flags.tryIncremental, qPrintable(dataIn.toString())); - if (!dataIn.hasChildren) { + if (!m_accessible) // Add watch data while running? + return; + + // New watch item? + if (isWatchIName(dataIn.iname) && dataIn.isValueNeeded()) { + QByteArray args; + ByteArrayInputStream str(args); + str << dataIn.iname << " \"" << dataIn.exp << '"'; + postExtensionCommand("addwatch", args, 0, + &CdbEngine::handleAddWatch, 0, + qVariantFromValue(dataIn)); + return; + } + + if (!dataIn.hasChildren && !dataIn.isValueNeeded()) { WatchData data = dataIn; data.setAllUnneeded(); watchHandler()->insertData(data); @@ -721,6 +741,22 @@ void CdbEngine::updateWatchData(const WatchData &dataIn, updateLocalVariable(dataIn.iname); } +void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply) +{ + WatchData item = qvariant_cast(reply->cookie); + if (debugWatches) + qDebug() << "handleAddWatch ok=" << reply->success << item.iname; + if (reply->success) { + updateLocalVariable(item.iname); + } else { + item.setError(tr("Unable to add expression")); + watchHandler()->insertData(item); + showMessage(QString::fromLatin1("Unable to add watch item '%1'/'%2': %3"). + arg(QString::fromAscii(item.iname), QString::fromAscii(item.exp), + reply->errorMessage), LogError); + } +} + void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const { if (debuggerCore()->boolSetting(VerboseLog)) @@ -737,22 +773,28 @@ void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const void CdbEngine::updateLocalVariable(const QByteArray &iname) { - const int stackFrame = stackHandler()->currentIndex(); - if (stackFrame >= 0) { - QByteArray localsArguments; - ByteArrayInputStream str(localsArguments); - addLocalsOptions(str); - str << blankSeparator << stackFrame << ' ' << iname; - postExtensionCommand("locals", localsArguments, 0, &CdbEngine::handleLocals); - } else { - qWarning("Internal error; no stack frame in updateLocalVariable"); + const bool isWatch = isWatchIName(iname); + if (debugWatches) + qDebug() << "updateLocalVariable watch=" << isWatch << iname; + QByteArray localsArguments; + ByteArrayInputStream str(localsArguments); + addLocalsOptions(str); + if (!isWatch) { + const int stackFrame = stackHandler()->currentIndex(); + if (stackFrame < 0) { + qWarning("Internal error; no stack frame in updateLocalVariable"); + return; + } + str << blankSeparator << stackFrame; } + str << blankSeparator << iname; + postExtensionCommand(isWatch ? "watches" : "locals", localsArguments, 0, &CdbEngine::handleLocals); } unsigned CdbEngine::debuggerCapabilities() const { return DisassemblerCapability | RegisterCapability | ShowMemoryCapability - |WatchpointCapability|JumpToLineCapability + |WatchpointCapability|JumpToLineCapability|AddWatcherCapability |BreakOnThrowAndCatchCapability; // Sort-of: Can break on throw(). } @@ -1052,6 +1094,8 @@ void CdbEngine::activateFrame(int index) void CdbEngine::updateLocals() { + typedef QHash WatcherHash; + const int frameIndex = stackHandler()->currentIndex(); if (frameIndex < 0) { watchHandler()->beginCycle(); @@ -1094,6 +1138,16 @@ void CdbEngine::updateLocals() } } } + // Perform watches synchronization + str << blankSeparator << "-W"; + const WatcherHash watcherHash = WatchHandler::watcherNames(); + if (!watcherHash.isEmpty()) { + const WatcherHash::const_iterator cend = watcherHash.constEnd(); + for (WatcherHash::const_iterator it = watcherHash.constBegin(); it != cend; ++it) { + str << blankSeparator << "-w " << it.value() << " \"" << it.key() << '"'; + } + } + // Required arguments: frame str << blankSeparator << frameIndex; watchHandler()->beginCycle(); diff --git a/src/plugins/debugger/cdb/cdbengine.h b/src/plugins/debugger/cdb/cdbengine.h index 539a6d5f810..34405dc4b0d 100644 --- a/src/plugins/debugger/cdb/cdbengine.h +++ b/src/plugins/debugger/cdb/cdbengine.h @@ -177,6 +177,7 @@ private: void handleThreads(const CdbExtensionCommandPtr &); void handlePid(const CdbExtensionCommandPtr &reply); void handleLocals(const CdbExtensionCommandPtr &reply); + void handleAddWatch(const CdbExtensionCommandPtr &reply); void handleExpandLocals(const CdbExtensionCommandPtr &reply); void handleRegisters(const CdbExtensionCommandPtr &reply); void handleModules(const CdbExtensionCommandPtr &reply);