Debugger[New CDB]:Introduce watches infrastructure.

- Move the 'current module' into the Node
- Split symbol group hierarchy into LocalsSymbolGroup
  tied to frame/thread and a separate, scopeless
  WatchesSymbolGroup
- Add infrastructure for removing symbols from a SymbolGroup,
  doing the index bookkeeping.
- Add method to synchronize watches to  WatchesSymbolGroup
  (iname/name map).
- Introduce watches commands for adding and dumping.
- Extend locals command to get watches as well.
- Add a dummy 'ErrorSymbolGroupNode' to use in case
  insertion fails.
This commit is contained in:
Friedemann Kleint
2011-01-14 16:50:31 +01:00
parent 7968853f1a
commit 3a87af8ada
13 changed files with 944 additions and 251 deletions

View File

@@ -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*') // 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 bool needResolve = kt == KT_Unknown || kt == KT_PointerType || !(kt & KT_POD_Type);
const std::string fixed = needResolve ? const std::string fixed = needResolve ?
SymbolGroupValue::resolveType(stripped, container.context(), container.node()->symbolGroup()) : SymbolGroupValue::resolveType(stripped, container.context(), container.module()) :
stripped; stripped;
if (SymbolGroupValue::verbose) { if (SymbolGroupValue::verbose) {
DebugPrint dp; DebugPrint dp;
@@ -294,7 +294,9 @@ static inline std::string pointedToSymbolName(ULONG64 address, const std::string
template <class AddressFunc> template <class AddressFunc>
AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc, AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc,
const std::string &innerType, int count) const std::string &module,
const std::string &innerType,
int count)
{ {
AbstractSymbolGroupNodePtrVector rc; AbstractSymbolGroupNodePtrVector rc;
if (!count) if (!count)
@@ -303,7 +305,7 @@ AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc add
rc.reserve(count); rc.reserve(count);
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
const std::string name = pointedToSymbolName(addressFunc(), innerType); 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)); rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, child));
} else { } else {
if (SymbolGroupValue::verbose) if (SymbolGroupValue::verbose)
@@ -335,12 +337,13 @@ private:
const ULONG m_delta; const ULONG m_delta;
}; };
static inline AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, ULONG64 address, static inline AbstractSymbolGroupNodePtrVector
const std::string &innerType, int count) arrayChildList(SymbolGroup *sg, ULONG64 address, const std::string &module,
const std::string &innerType, int count)
{ {
if (const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str())) if (const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str()))
return arrayChildList(sg, AddressSequence(address, innerTypeSize), return arrayChildList(sg, AddressSequence(address, innerTypeSize),
innerType, count); module, innerType, count);
return AbstractSymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }
@@ -361,7 +364,7 @@ static inline AbstractSymbolGroupNodePtrVector
const std::string innerType = fixInnerType(SymbolGroupValue::stripPointerType(firstType), vec); const std::string innerType = fixInnerType(SymbolGroupValue::stripPointerType(firstType), vec);
if (SymbolGroupValue::verbose) if (SymbolGroupValue::verbose)
DebugPrint() << n->name() << " inner type: '" << innerType << "' from '" << firstType << '\''; 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<class AddressType>
AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector
stdDequeChildrenHelper(SymbolGroup *sg, stdDequeChildrenHelper(SymbolGroup *sg,
const AddressType *blockArray, ULONG64 blockArraySize, const AddressType *blockArray, ULONG64 blockArraySize,
const std::string &module,
const std::string &innerType, ULONG64 innerTypeSize, const std::string &innerType, ULONG64 innerTypeSize,
ULONG64 startOffset, ULONG64 dequeSize, int count) ULONG64 startOffset, ULONG64 dequeSize, int count)
{ {
@@ -389,7 +393,7 @@ AbstractSymbolGroupNodePtrVector
block -= blockArraySize; block -= blockArraySize;
const ULONG64 blockOffset = offset % dequeSize; const ULONG64 blockOffset = offset % dequeSize;
const ULONG64 address = blockArray[block] + innerTypeSize * blockOffset; 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)); rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, n));
} else { } else {
return AbstractSymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
@@ -428,10 +432,10 @@ static inline AbstractSymbolGroupNodePtrVector
const AbstractSymbolGroupNodePtrVector rc = SymbolGroupValue::pointerSize() == 8 ? const AbstractSymbolGroupNodePtrVector rc = SymbolGroupValue::pointerSize() == 8 ?
stdDequeChildrenHelper(deque.node()->symbolGroup(), stdDequeChildrenHelper(deque.node()->symbolGroup(),
reinterpret_cast<const ULONG64 *>(mapArray), mapSize, reinterpret_cast<const ULONG64 *>(mapArray), mapSize,
innerType, innerTypeSize, startOffset, dequeSize, count) : deque.module(), innerType, innerTypeSize, startOffset, dequeSize, count) :
stdDequeChildrenHelper(deque.node()->symbolGroup(), stdDequeChildrenHelper(deque.node()->symbolGroup(),
reinterpret_cast<const ULONG32 *>(mapArray), mapSize, reinterpret_cast<const ULONG32 *>(mapArray), mapSize,
innerType, innerTypeSize, startOffset, dequeSize, count); deque.module(), innerType, innerTypeSize, startOffset, dequeSize, count);
delete [] mapArray; delete [] mapArray;
return rc; return rc;
} }
@@ -660,7 +664,7 @@ static inline AbstractSymbolGroupNodePtrVector
if (const SymbolGroupValue firstElementV = vec["p"]["array"][unsigned(0)]) { if (const SymbolGroupValue firstElementV = vec["p"]["array"][unsigned(0)]) {
if (const ULONG64 arrayAddress = firstElementV.address()) { if (const ULONG64 arrayAddress = firstElementV.address()) {
const std::string fixedInnerType = fixInnerType(firstElementV.type(), vec); 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[] if (SymbolGroupValue::isPointerType(innerType)) // Quick check: Any pointer is T[]
return arrayChildList(v.node()->symbolGroup(), return arrayChildList(v.node()->symbolGroup(),
AddressSequence(arrayAddress, pointerSize), AddressSequence(arrayAddress, pointerSize),
innerType, count); v.module(), innerType, count);
// Check condition for large||static. // Check condition for large||static.
bool isLargeOrStatic = innerTypeSize > pointerSize; bool isLargeOrStatic = innerTypeSize > pointerSize;
if (!isLargeOrStatic && !SymbolGroupValue::isPointerType(innerType)) { if (!isLargeOrStatic && !SymbolGroupValue::isPointerType(innerType)) {
@@ -736,8 +740,11 @@ static inline AbstractSymbolGroupNodePtrVector
if (void *data = readPointerArray(arrayAddress, count, v.context())) { if (void *data = readPointerArray(arrayAddress, count, v.context())) {
// Generate sequence of addresses from pointer array // Generate sequence of addresses from pointer array
const AbstractSymbolGroupNodePtrVector rc = pointerSize == 8 ? const AbstractSymbolGroupNodePtrVector rc = pointerSize == 8 ?
arrayChildList(v.node()->symbolGroup(), AddressArraySequence<ULONG64>(reinterpret_cast<const ULONG64 *>(data)), innerType, count) : arrayChildList(v.node()->symbolGroup(),
arrayChildList(v.node()->symbolGroup(), AddressArraySequence<ULONG32>(reinterpret_cast<const ULONG32 *>(data)), innerType, count); AddressArraySequence<ULONG64>(reinterpret_cast<const ULONG64 *>(data)),
v.module(), innerType, count) :
arrayChildList(v.node()->symbolGroup(), AddressArraySequence<ULONG32>(reinterpret_cast<const ULONG32 *>(data)),
v.module(), innerType, count);
delete [] data; delete [] data;
return rc; return rc;
} }
@@ -745,7 +752,7 @@ static inline AbstractSymbolGroupNodePtrVector
} }
return arrayChildList(v.node()->symbolGroup(), return arrayChildList(v.node()->symbolGroup(),
AddressSequence(arrayAddress, pointerSize), AddressSequence(arrayAddress, pointerSize),
innerType, count); v.module(), innerType, count);
} }
// Return the list of buckets of a 'QHash<>' as 'QHashData::Node *' values from // 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, const AddressType *pointerArray,
int numBuckets, int numBuckets,
AddressType ePtr, AddressType ePtr,
const std::string &module,
const SymbolGroupValueContext &ctx) const SymbolGroupValueContext &ctx)
{ {
SymbolGroupValueVector rc; SymbolGroupValueVector rc;
@@ -766,7 +774,7 @@ SymbolGroupValueVector hashBuckets(SymbolGroup *sg, const std::string &hashNodeT
for (const AddressType *p = pointerArray; p < end; p++) { for (const AddressType *p = pointerArray; p < end; p++) {
if (*p != ePtr) { if (*p != ePtr) {
const std::string name = pointedToSymbolName(*p, hashNodeType); 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)); rc.push_back(SymbolGroupValue(child, ctx));
} else { } else {
return std::vector<SymbolGroupValue>(); return std::vector<SymbolGroupValue>();
@@ -790,7 +798,7 @@ static inline std::string qHashNodeType(const SymbolGroupValue &v,
// the Qt namespace (particularly QMapNode, QHashNodes work also for // the Qt namespace (particularly QMapNode, QHashNodes work also for
// the unqualified case). // the unqualified case).
const QtInfo &qtInfo = QtInfo::get(v.context()); 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); return QtInfo::prependModuleAndNameSpace(qHashType, currentModule, qtInfo.nameSpace);
} }
@@ -820,10 +828,10 @@ SymbolGroupValueVector qHashNodes(const SymbolGroupValue &v,
const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ? const SymbolGroupValueVector buckets = SymbolGroupValue::pointerSize() == 8 ?
hashBuckets(v.node()->symbolGroup(), dummyNodeType, hashBuckets(v.node()->symbolGroup(), dummyNodeType,
reinterpret_cast<const ULONG64 *>(bucketPointers), numBuckets, reinterpret_cast<const ULONG64 *>(bucketPointers), numBuckets,
ePtr, v.context()) : ePtr, v.module(), v.context()) :
hashBuckets(v.node()->symbolGroup(), dummyNodeType, hashBuckets(v.node()->symbolGroup(), dummyNodeType,
reinterpret_cast<const ULONG32 *>(bucketPointers), numBuckets, reinterpret_cast<const ULONG32 *>(bucketPointers), numBuckets,
ULONG32(ePtr), v.context()); ULONG32(ePtr), v.module(), v.context());
delete [] bucketPointers ; delete [] bucketPointers ;
// Generate the list 'QHashData::Node *' by iterating over the linked list of // Generate the list 'QHashData::Node *' by iterating over the linked list of
// nodes starting at each bucket. Using the 'QHashData::Node *' instead of // nodes starting at each bucket. Using the 'QHashData::Node *' instead of
@@ -1001,8 +1009,8 @@ static inline AbstractSymbolGroupNodePtrVector
',' <<keyExp << ',' << valueExp; ',' <<keyExp << ',' << valueExp;
} }
// Create the nodes // Create the nodes
SymbolGroupNode *keyNode = sg->addSymbol(keyExp, std::string(), &errorMessage); SymbolGroupNode *keyNode = sg->addSymbol(v.module(), keyExp, std::string(), &errorMessage);
SymbolGroupNode *valueNode = sg->addSymbol(valueExp, std::string(), &errorMessage); SymbolGroupNode *valueNode = sg->addSymbol(v.module(), valueExp, std::string(), &errorMessage);
if (!keyNode || !valueNode) if (!keyNode || !valueNode)
return AbstractSymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
rc.push_back(MapNodeSymbolGroupNode::create(int(i), keyAddress, rc.push_back(MapNodeSymbolGroupNode::create(int(i), keyAddress,

View File

@@ -189,6 +189,7 @@ void ExtensionContext::notifyState(ULONG Notify)
case DEBUG_NOTIFY_SESSION_INACTIVE: case DEBUG_NOTIFY_SESSION_INACTIVE:
report('E', 0, 0, "session_inactive", "%u", ex); report('E', 0, 0, "session_inactive", "%u", ex);
discardSymbolGroup(); discardSymbolGroup();
discardWatchesSymbolGroup();
// We lost the debuggee, at this point restore output. // We lost the debuggee, at this point restore output.
if (ex & DEBUG_STATUS_NO_DEBUGGEE) if (ex & DEBUG_STATUS_NO_DEBUGGEE)
unhookCallbacks(); 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) if (m_symbolGroup.get() && m_symbolGroup->frame() == frame && m_symbolGroup->threadId() == threadId)
return m_symbolGroup.get(); 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) if (!newSg)
return 0; return 0;
m_symbolGroup.reset(newSg); m_symbolGroup.reset(newSg);
@@ -214,12 +215,36 @@ int ExtensionContext::symbolGroupFrame() const
return -1; 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() void ExtensionContext::discardSymbolGroup()
{ {
if (m_symbolGroup.get()) if (m_symbolGroup.get())
m_symbolGroup.reset(); 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, ...) bool ExtensionContext::report(char code, int token, int remainingChunks, const char *serviceName, PCSTR Format, ...)
{ {
if (!isInitialized()) if (!isInitialized())

View File

@@ -40,7 +40,8 @@
#include <memory> #include <memory>
#include <map> #include <map>
class SymbolGroup; class LocalsSymbolGroup;
class WatchesSymbolGroup;
// Global singleton with context. // Global singleton with context.
// Caches a symbolgroup per frame and thread as long as the session is accessible. // Caches a symbolgroup per frame and thread as long as the session is accessible.
@@ -86,9 +87,13 @@ public:
void notifyIdle(); void notifyIdle();
// Return symbol group for frame (cached as long as frame/thread do not change). // 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; 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). // Set a stop reason to be reported with the next idle notification (exception).
void setStopReason(const StopReasonMap &, const std::string &reason = std::string()); void setStopReason(const StopReasonMap &, const std::string &reason = std::string());
@@ -97,7 +102,8 @@ private:
void discardSymbolGroup(); void discardSymbolGroup();
IInterfacePointer<CIDebugControl> m_control; IInterfacePointer<CIDebugControl> m_control;
std::auto_ptr<SymbolGroup> m_symbolGroup; std::auto_ptr<LocalsSymbolGroup> m_symbolGroup;
std::auto_ptr<WatchesSymbolGroup> m_watchesSymbolGroup;
CIDebugClient *m_hookedClient; CIDebugClient *m_hookedClient;
IDebugEventCallbacks *m_oldEventCallback; IDebugEventCallbacks *m_oldEventCallback;

View File

@@ -4,6 +4,7 @@ DebugExtensionUninitialize
DebugExtensionNotify DebugExtensionNotify
pid pid
expandlocals expandlocals
watches
locals locals
dumplocal dumplocal
typecast typecast
@@ -18,4 +19,5 @@ memory
shutdownex shutdownex
test test
stack stack
addwatch
KnownStructOutput KnownStructOutput

View File

@@ -86,6 +86,7 @@ enum Command {
CmdPid, CmdPid,
CmdExpandlocals, CmdExpandlocals,
CmdLocals, CmdLocals,
CmdWatches,
CmdDumplocal, CmdDumplocal,
CmdTypecast, CmdTypecast,
CmdAddsymbol, CmdAddsymbol,
@@ -98,6 +99,7 @@ enum Command {
CmdMemory, CmdMemory,
CmdStack, CmdStack,
CmdShutdownex, CmdShutdownex,
CmdAddWatch,
CmdTest CmdTest
}; };
@@ -111,7 +113,8 @@ static const CommandDescription commandDescriptions[] = {
"-c complex dumpers"}, "-c complex dumpers"},
{"locals", {"locals",
"Prints local variables of symbol group in GDBMI or debug format", "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<frame-number> [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] <frame-number> [iname]\n"
"-h human-readable ouput\n" "-h human-readable ouput\n"
"-v increase verboseness of dumping\n" "-v increase verboseness of dumping\n"
"-d debug output\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" "-e expand-list Comma-separated list of inames to be expanded beforehand\n"
"-u uninitialized-list Comma-separated list of uninitialized inames\n" "-u uninitialized-list Comma-separated list of uninitialized inames\n"
"-I formatmap map of 'hex-encoded-iname=typecode'\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] <iname>\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'"}, "-T formatmap map of 'hex-encoded-type-name=typecode'"},
{"dumplocal", "Dumps local variable using simple dumpers (testing command).", {"dumplocal", "Dumps local variable using simple dumpers (testing command).",
"[-t token] <frame-number> <iname>"}, "[-t token] <frame-number> <iname>"},
@@ -139,12 +154,18 @@ static const CommandDescription commandDescriptions[] = {
{"memory","Prints memory contents in Base64 encoding.","[-t token] <address> <length>"}, {"memory","Prints memory contents in Base64 encoding.","[-t token] <address> <length>"},
{"stack","Prints stack in GDBMI format.","[-t token] [max-frames]"}, {"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.",""}, {"shutdownex","Unhooks output callbacks.\nNeeds to be called explicitly only in case of remote debugging.",""},
{"addwatch","Add watch expression","<iname> <expression>"},
{"test","Testing command","-T type"} {"test","Testing command","-T type"}
}; };
typedef std::vector<std::string> StringVector; typedef std::vector<std::string> StringVector;
typedef std::list<std::string> StringList; typedef std::list<std::string> StringList;
static inline bool isOption(const std::string &opt)
{
return opt.size() == 2 && opt.at(0) == '-' && opt != "--";
}
// Helper for commandTokens() below: // Helper for commandTokens() below:
// Simple splitting of command lines allowing for '"'-quoted tokens // Simple splitting of command lines allowing for '"'-quoted tokens
// 'typecast local.i "class QString *"' -> ('typecast','local.i','class QString *') // '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; 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': // Extension command 'locals':
// Display local variables of symbol group in GDBMI or debug output form. // Display local variables of symbol group in GDBMI or debug output form.
// Takes an optional iname which is expanded before displaying (for updateWatchData) // 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 // Parse the command
unsigned debugOutput = 0; ExtensionContext &extCtx = ExtensionContext::instance();
DumpCommandParameters parameters;
std::string iname; std::string iname;
std::string debugFilter;
StringList tokens = commandTokens<StringList>(args, token); StringList tokens = commandTokens<StringList>(args, token);
StringVector expandedInames; StringVector expandedInames;
StringVector uninitializedInames; StringVector uninitializedInames;
DumpParameters parameters; InameExpressionMap watcherInameExpressionMap;
SymbolGroupValue::verbose = 0; bool watchSynchronization = false;
// Parse away options // Parse away options
while (!tokens.empty() && tokens.front().size() == 2 && tokens.front().at(0) == '-') { for (bool optionLeft = true; optionLeft && !tokens.empty(); ) {
const char option = tokens.front().at(1); switch (parameters.parseOption(&tokens)) {
tokens.pop_front(); case DumpCommandParameters::Ok:
switch (option) {
case 'd':
debugOutput++;
break; break;
case 'h': case DumpCommandParameters::Error:
parameters.dumpFlags |= DumpParameters::DumpHumanReadable; *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
break; return std::string();
case 'c': case DumpCommandParameters::OtherOption: {
parameters.dumpFlags |= DumpParameters::DumpComplexDumpers; const char option = tokens.front().at(1);
break;
case 'u':
if (tokens.empty()) {
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
return std::string();
}
split(tokens.front(), ',', std::back_inserter(uninitializedInames));
tokens.pop_front(); tokens.pop_front();
break; switch (option) {
case 'f': case 'u':
if (tokens.empty()) { if (tokens.empty()) {
*errorMessage = singleLineUsage(commandDescriptions[CmdLocals]); *errorMessage = singleLineUsage(commandDescriptions[CmdLocals]);
return std::string(); 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; break;
case 'v': case 'W':
SymbolGroupValue::verbose++; watchSynchronization = true;
break;
} // case option
}
break;
case DumpCommandParameters::EndOfOptions:
optionLeft = false;
break; 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 // Frame and iname
unsigned frame; unsigned frame;
if (tokens.empty() || !integerFromString(tokens.front(), &frame)) { if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
@@ -380,28 +463,70 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
if (!tokens.empty()) if (!tokens.empty())
iname = tokens.front(); iname = tokens.front();
const SymbolGroupValueContext dumpContext(exc.dataSpaces(), exc.symbols()); const SymbolGroupValueContext dumpContext(commandExtCtx.dataSpaces(), commandExtCtx.symbols());
SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage); SymbolGroup * const symGroup = extCtx.symbolGroup(commandExtCtx.symbols(), commandExtCtx.threadId(), frame, errorMessage);
if (!symGroup) if (!symGroup)
return std::string(); return std::string();
if (!uninitializedInames.empty()) if (!uninitializedInames.empty())
symGroup->markUninitialized(uninitializedInames); symGroup->markUninitialized(uninitializedInames);
if (!expandedInames.empty()) { SymbolGroupValue::verbose = parameters.verbose;
if (parameters.dumpFlags & DumpParameters::DumpComplexDumpers) {
symGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage); // 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 { } 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) // Pre-expand.
return symGroup->debug(iname, debugFilter, debugOutput - 1); 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() ? // Debug output: Join the 2 symbol groups if no iname is given.
symGroup->dump(dumpContext, parameters) : if (parameters.debugOutput) {
symGroup->dump(iname, dumpContext, parameters, errorMessage); 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) 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; std::string errorMessage;
int token; int token;
const std::string output = commmandLocals(exc, args, &token, &errorMessage); 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<StringList>(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()) { if (output.empty()) {
ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str()); ExtensionContext::instance().report('N', token, 0, "locals", errorMessage.c_str());
} else { } else {
@@ -520,7 +701,7 @@ extern "C" HRESULT CALLBACK addsymbol(CIDebugClient *client, PCSTR args)
} else { } else {
errorMessage = singleLineUsage(commandDescriptions[CmdAddsymbol]); 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"); ExtensionContext::instance().report('R', token, 0, "addsymbol", "OK");
} else { } else {
ExtensionContext::instance().report('N', token, 0, "addsymbol", errorMessage.c_str()); 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; 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<StringList>(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': // Extension command 'assign':
// Assign locals by iname: 'assign locals.x=5' // Assign locals by iname: 'assign locals.x=5'
@@ -766,7 +981,6 @@ extern "C" HRESULT CALLBACK test(CIDebugClient *client, PCSTR argsIn)
return S_OK; return S_OK;
} }
// Hook for dumping Known Structs. Not currently used. // Hook for dumping Known Structs. Not currently used.
// Shows up in 'dv' as well as IDebugSymbolGroup::GetValueText. // Shows up in 'dv' as well as IDebugSymbolGroup::GetValueText.

View File

@@ -32,6 +32,7 @@
**************************************************************************/ **************************************************************************/
#include "symbolgroup.h" #include "symbolgroup.h"
#include "symbolgroupvalue.h"
#include "stringutils.h" #include "stringutils.h"
#include "gdbmihelpers.h" #include "gdbmihelpers.h"
@@ -44,27 +45,16 @@ typedef std::vector<int>::size_type VectorIndexType;
typedef std::vector<std::string> StringVector; typedef std::vector<std::string> StringVector;
enum { debug = 0 }; enum { debug = 0 };
const char rootNameC[] = "local";
// ------------- SymbolGroup // ------------- SymbolGroup
SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg, SymbolGroup::SymbolGroup(IDebugSymbolGroup2 *sg,
const SymbolParameterVector &vec, const SymbolParameterVector &vec,
ULONG threadId, const std::string &rootModule,
unsigned frame, const char *rootName) :
const std::string &function) :
m_symbolGroup(sg), m_symbolGroup(sg),
m_threadId(threadId), m_root(0)
m_frame(frame),
m_root(0),
m_function(function)
{ {
m_root = SymbolGroupNode::create(this, rootNameC, vec); m_root = SymbolGroupNode::create(this, rootModule, rootName, 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() SymbolGroup::~SymbolGroup()
@@ -132,67 +122,26 @@ bool SymbolGroup::getSymbolParameters(CIDebugSymbolGroup *symbolGroup,
return true; return true;
} }
SymbolGroup *SymbolGroup::create(CIDebugControl *control, CIDebugSymbols *debugSymbols, bool SymbolGroup::removeSymbol(AbstractSymbolGroupNode *an, std::string *errorMessage)
ULONG threadId, unsigned frame,
std::string *errorMessage)
{ {
errorMessage->clear(); bool ok = false;
// Remove a real SymbolGroupNode: Call its special remove method to correct indexes.
ULONG obtainedFrameCount = 0; if (SymbolGroupNode *n = an->asSymbolGroupNode()) {
const ULONG frameCount = frame + 1; ok = n->removeSelf(root(), errorMessage);
} else {
DEBUG_STACK_FRAME *frames = new DEBUG_STACK_FRAME[frameCount]; // Normal removal, assuming parent is a base (needed for removing
IDebugSymbolGroup2 *idebugSymbols = 0; // error nodes by the Watch group).
bool success = false; if (BaseSymbolGroupNode *bParent = dynamic_cast<BaseSymbolGroupNode *>(an->parent())) {
SymbolParameterVector parameters; bParent->removeChildAt(bParent->indexOf(an));
std::string func; ok = true;
// 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, &parameters, 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) 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); node->clearFlags(SymbolGroupNode::ExpandedByDumper);
} else { } else {
if (node->canExpand() && !node->expand(errorMessage)) if (node->canExpand() && !node->expand(errorMessage))
return false; return std::string();
} }
// After expansion, run the complex dumpers // After expansion, run the complex dumpers
if (p.dumpFlags & DumpParameters::DumpComplexDumpers) if (p.dumpFlags & DumpParameters::DumpComplexDumpers)
@@ -312,22 +261,32 @@ struct InamePathEntryLessThan : public std::binary_function<InamePathEntry, Inam
typedef std::set<InamePathEntry, InamePathEntryLessThan> InamePathEntrySet; typedef std::set<InamePathEntry, InamePathEntryLessThan> InamePathEntrySet;
static inline InamePathEntrySet expandEntrySet(const std::vector<std::string> &nodes) static inline InamePathEntrySet expandEntrySet(const std::vector<std::string> &nodes, const std::string &root)
{ {
InamePathEntrySet pathEntries; InamePathEntrySet pathEntries;
const std::string::size_type rootSize = root.size();
const VectorIndexType nodeCount = nodes.size(); const VectorIndexType nodeCount = nodes.size();
for (VectorIndexType i= 0; i < nodeCount; i++) { for (VectorIndexType i= 0; i < nodeCount; i++) {
const std::string &iname = nodes.at(i); const std::string &iname = nodes.at(i); // Silently skip items of another group
std::string::size_type pos = 0; if (iname.size() >= rootSize && iname.compare(0, rootSize, root) == 0) {
// Split a path 'local.foo' and insert (0,'local'), (1,'local.foo') (see above) std::string::size_type pos = 0;
for (unsigned level = 0; pos < iname.size(); level++) { // Split a path 'local.foo' and insert (0,'local'), (1,'local.foo') (see above)
std::string::size_type dotPos = iname.find(SymbolGroupNodeVisitor::iNamePathSeparator, pos); for (unsigned level = 0; pos < iname.size(); level++) {
if (dotPos == std::string::npos) std::string::size_type dotPos = iname.find(SymbolGroupNodeVisitor::iNamePathSeparator, pos);
dotPos = iname.size(); if (dotPos == std::string::npos)
pathEntries.insert(InamePathEntry(level, iname.substr(0, dotPos))); dotPos = iname.size();
pos = dotPos + 1; 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; return pathEntries;
} }
@@ -339,7 +298,7 @@ unsigned SymbolGroup::expandList(const std::vector<std::string> &nodes, std::str
if (nodes.empty()) if (nodes.empty())
return 0; return 0;
// Create a set with a key <level, name>. Also required for 1 node (see above). // Create a set with a key <level, name>. Also required for 1 node (see above).
const InamePathEntrySet pathEntries = expandEntrySet(nodes); const InamePathEntrySet pathEntries = expandEntrySet(nodes, root()->iName());
// Now expand going by level. // Now expand going by level.
unsigned succeeded = 0; unsigned succeeded = 0;
std::string nodeError; std::string nodeError;
@@ -367,7 +326,7 @@ unsigned SymbolGroup::expandListRunComplexDumpers(const std::vector<std::string>
if (nodes.empty()) if (nodes.empty())
return 0; return 0;
// Create a set with a key <level, name>. Also required for 1 node (see above). // Create a set with a key <level, name>. Also required for 1 node (see above).
const InamePathEntrySet pathEntries = expandEntrySet(nodes); const InamePathEntrySet pathEntries = expandEntrySet(nodes, root()->iName());
// Now expand going by level. // Now expand going by level.
unsigned succeeded = 0; unsigned succeeded = 0;
std::string nodeError; std::string nodeError;
@@ -440,9 +399,9 @@ bool SymbolGroup::typeCast(const std::string &iname, const std::string &desiredT
return node->typeCast(desiredType, errorMessage); 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) // Mark uninitialized (top level only)
@@ -554,3 +513,198 @@ AbstractSymbolGroupNode *SymbolGroup::find(const std::string &iname) const
DebugPrint() << "SymbolGroup::find " << iname << ' ' << rc; DebugPrint() << "SymbolGroup::find " << iname << ' ' << rc;
return 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, &parameters, 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<std::string> 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);
}

View File

@@ -37,6 +37,8 @@
#include "common.h" #include "common.h"
#include "symbolgroupnode.h" #include "symbolgroupnode.h"
#include <map>
/* A symbol group storing a tree of expanded symbols rooted on /* A symbol group storing a tree of expanded symbols rooted on
* a fake "locals" root element. * a fake "locals" root element.
* Provides a find() method based on inames ("locals.this.i1.data") and * Provides a find() method based on inames ("locals.this.i1.data") and
@@ -53,20 +55,16 @@ private:
SymbolGroup(const SymbolGroup &); SymbolGroup(const SymbolGroup &);
SymbolGroup &operator=(const SymbolGroup &); SymbolGroup &operator=(const SymbolGroup &);
protected:
explicit SymbolGroup(CIDebugSymbolGroup *, explicit SymbolGroup(CIDebugSymbolGroup *,
const SymbolParameterVector &vec, const SymbolParameterVector &vec,
ULONG threadId, unsigned frame, const std::string &rootModule,
const std::string &function); const char *rootName);
public: public:
typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector; typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
static SymbolGroup *create(CIDebugControl *control, virtual ~SymbolGroup();
CIDebugSymbols *,
ULONG threadId,
unsigned frame,
std::string *errorMessage);
~SymbolGroup();
// Dump all // Dump all
std::string dump(const SymbolGroupValueContext &ctx, std::string dump(const SymbolGroupValueContext &ctx,
@@ -78,10 +76,6 @@ public:
const std::string &filter = std::string(), const std::string &filter = std::string(),
unsigned verbosity = 0) const; 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; } SymbolGroupNode *root() { return m_root; }
const SymbolGroupNode *root() const { return m_root; } const SymbolGroupNode *root() const { return m_root; }
AbstractSymbolGroupNode *find(const std::string &iname) const; AbstractSymbolGroupNode *find(const std::string &iname) const;
@@ -103,7 +97,8 @@ public:
// Cast an (unexpanded) node // Cast an (unexpanded) node
bool typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage); bool typeCast(const std::string &iname, const std::string &desiredType, std::string *errorMessage);
// Add a symbol by name expression // 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 const std::string &iname, // Desired iname, defaults to name
std::string *errorMessage); std::string *errorMessage);
@@ -122,18 +117,65 @@ public:
SymbolParameterVector *vec, SymbolParameterVector *vec,
std::string *errorMessage); std::string *errorMessage);
private: protected:
inline AbstractSymbolGroupNode *findI(const std::string &iname) const;
static bool getSymbolParameters(CIDebugSymbolGroup *m_symbolGroup, static bool getSymbolParameters(CIDebugSymbolGroup *m_symbolGroup,
SymbolParameterVector *vec, SymbolParameterVector *vec,
std::string *errorMessage); std::string *errorMessage);
bool removeSymbol(AbstractSymbolGroupNode *n, std::string *errorMessage);
private:
inline AbstractSymbolGroupNode *findI(const std::string &iname) const;
CIDebugSymbolGroup * const m_symbolGroup; 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 unsigned m_frame;
const ULONG m_threadId; const ULONG m_threadId;
SymbolGroupNode *m_root;
std::string m_function; std::string m_function;
std::string m_module; };
// Watch symbol group. Contains watches as added by Qt Creator as iname='watch.0',
// name='<expression>'. The IDebugSymbolGroup is created without scope.
class WatchesSymbolGroup : public SymbolGroup {
public:
typedef std::map<std::string, std::string> 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 #endif // SYMBOLGROUP_H

View File

@@ -73,6 +73,8 @@ static inline void debugNodeFlags(std::ostream &str, unsigned f)
str << " Obscured"; str << " Obscured";
if (f & SymbolGroupNode::ComplexDumperOk) if (f & SymbolGroupNode::ComplexDumperOk)
str << " ComplexDumperOk"; str << " ComplexDumperOk";
if (f & SymbolGroupNode::WatchNode)
str << " WatchNode";
str << ' '; str << ' ';
} }
@@ -135,6 +137,12 @@ AbstractSymbolGroupNode *AbstractSymbolGroupNode::childByIName(const char *n) co
return 0; 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, bool AbstractSymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor,
const std::string &parentIname, const std::string &parentIname,
unsigned child, unsigned depth) unsigned child, unsigned depth)
@@ -208,6 +216,15 @@ BaseSymbolGroupNode::~BaseSymbolGroupNode()
removeChildren(); 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() void BaseSymbolGroupNode::removeChildren()
{ {
if (!m_children.empty()) { if (!m_children.empty()) {
@@ -421,15 +438,37 @@ bool DumpParameters::recode(const std::string &type,
return true; 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, "<unknown>", std::string());
str << ",valueencoded=\"0\",value=\"<Error>\",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::SymbolGroupNode(SymbolGroup *symbolGroup, SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup,
ULONG index, ULONG index,
const std::string &module,
const std::string &name, const std::string &name,
const std::string &iname) : const std::string &iname) :
BaseSymbolGroupNode(name, iname), BaseSymbolGroupNode(name, iname),
m_symbolGroup(symbolGroup), 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)); memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS));
m_parameters.ParentSymbol = DEBUG_ANY_ID; m_parameters.ParentSymbol = DEBUG_ANY_ID;
@@ -442,6 +481,13 @@ const SymbolGroupNode *SymbolGroupNode::symbolGroupNodeParent() const
return 0; return 0;
} }
SymbolGroupNode *SymbolGroupNode::symbolGroupNodeParent()
{
if (AbstractSymbolGroupNode *p = parent())
return p->asSymbolGroupNode();
return 0;
}
bool SymbolGroupNode::isArrayElement() const bool SymbolGroupNode::isArrayElement() const
{ {
if (const SymbolGroupNode *p = symbolGroupNodeParent()) 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. // 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 // Return true if a modification was required to be able to terminate the
// recursion. // recursion.
bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount) bool SymbolGroupNode::notifyIndexesMoved(ULONG index, bool inserted, ULONG offset)
{ {
typedef AbstractSymbolGroupNodePtrVector::const_reverse_iterator ReverseIt; 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, // Looping backwards over the children. If a subtree has no modifications,
// (meaning all other indexes are smaller) we can stop. // (meaning all other indexes are smaller) we can stop.
const ReverseIt rend = children().rend(); const ReverseIt rend = children().rend();
for (ReverseIt it = children().rbegin(); it != rend; ++it) { for (ReverseIt it = children().rbegin(); it != rend; ++it) {
if (SymbolGroupNode *c = (*it)->asSymbolGroupNode()) if (SymbolGroupNode *c = (*it)->asSymbolGroupNode())
if (!c->notifyExpanded(index, insertedCount)) if (!c->notifyIndexesMoved(index, inserted, offset))
return false; return false;
} }
@@ -469,9 +520,18 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount)
if (m_index == DEBUG_ANY_ID || m_index < index) if (m_index == DEBUG_ANY_ID || m_index < index)
return false; return false;
m_index += insertedCount; if (inserted) {
if (m_parameters.ParentSymbol != DEBUG_ANY_ID && m_parameters.ParentSymbol >= index) m_index += offset;
m_parameters.ParentSymbol += insertedCount; } 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; return true;
} }
@@ -596,6 +656,7 @@ void SymbolGroupNode::parseParameters(VectorIndexType index,
const VectorIndexType symbolGroupIndex = pos + parameterOffset; const VectorIndexType symbolGroupIndex = pos + parameterOffset;
SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup, SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup,
ULONG(symbolGroupIndex), ULONG(symbolGroupIndex),
m_module,
names.at(nameIndex), names.at(nameIndex),
inames.at(nameIndex)); inames.at(nameIndex));
child->parseParameters(symbolGroupIndex, parameterOffset, vec); child->parseParameters(symbolGroupIndex, parameterOffset, vec);
@@ -607,9 +668,10 @@ void SymbolGroupNode::parseParameters(VectorIndexType index,
m_parameters.SubElements = ULONG(children().size()); 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); rc->parseParameters(DEBUG_ANY_ID, 0, vec);
return rc; return rc;
} }
@@ -833,6 +895,24 @@ int SymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullInam
return dumpNode(str, name(), visitingFullIname, p, ctx); 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, int SymbolGroupNode::dumpNode(std::ostream &str,
const std::string &aName, const std::string &aName,
const std::string &aFullIName, const std::string &aFullIName,
@@ -840,11 +920,12 @@ int SymbolGroupNode::dumpNode(std::ostream &str,
const SymbolGroupValueContext &ctx) const SymbolGroupValueContext &ctx)
{ {
const std::string t = type(); 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()) if (addr)
str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec << '"';
<< '"';
const bool uninitialized = flags() & Uninitialized; const bool uninitialized = flags() & Uninitialized;
bool valueEditable = !uninitialized; bool valueEditable = !uninitialized;
@@ -896,7 +977,7 @@ void SymbolGroupNode::debug(std::ostream &str,
str << "AbsIname=" << fullIname << '"'; str << "AbsIname=" << fullIname << '"';
if (fullIname != visitingFullIname) if (fullIname != visitingFullIname)
str << ",VisitIname=\"" <<visitingFullIname; str << ",VisitIname=\"" <<visitingFullIname;
str << "\",index=" << m_index; str << "\",module=\"" << m_module << "\",index=" << m_index;
if (const VectorIndexType childCount = children().size()) if (const VectorIndexType childCount = children().size())
str << ", Children=" << childCount; str << ", Children=" << childCount;
str << ' ' << m_parameters << DebugNodeFlags(flags()); str << ' ' << m_parameters << DebugNodeFlags(flags());
@@ -979,7 +1060,7 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
return false; return false;
} }
// Before inserting children, correct indexes on whole group // Before inserting children, correct indexes on whole group
m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements); m_symbolGroup->root()->notifyIndexesMoved(m_index + 1, true, parameters.at(0).SubElements);
// Parse parameters, correct our own) and create child nodes. // Parse parameters, correct our own) and create child nodes.
parseParameters(m_index, m_index, parameters); parseParameters(m_index, m_index, parameters);
return true; return true;
@@ -1020,6 +1101,59 @@ bool SymbolGroupNode::typeCast(const std::string &desiredType, std::string *erro
return true; return true;
} }
// Find the index of the next symbol in the group, that is, right sibling.
// Go up the tree if we are that last sibling. 0 indicates none found (last sibling)
ULONG SymbolGroupNode::nextSymbolIndex() const
{
const SymbolGroupNode *sParent = symbolGroupNodeParent();
if (!sParent)
return 0;
const unsigned myIndex = sParent->indexOf(this);
const AbstractSymbolGroupNodePtrVector &siblings = sParent->children();
// Find any 'real' SymbolGroupNode to our right.
const unsigned size = unsigned(siblings.size());
for (unsigned i = myIndex + 1; i < size; i++)
if (const SymbolGroupNode *s = siblings.at(i)->asSymbolGroupNode())
return s->index();
return sParent->nextSymbolIndex();
}
// Remove self off parent and return the indexes to be shifted or unsigned(-1).
bool SymbolGroupNode::removeSelf(SymbolGroupNode *root, std::string *errorMessage)
{
SymbolGroupNode *sParent = symbolGroupNodeParent();
if (m_index == DEBUG_ANY_ID || !sParent) {
*errorMessage = "SymbolGroupNode::removeSelf: Internal error 1.";
return false;
}
const unsigned parentPosition = sParent->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) static inline std::string msgCannotAddSymbol(const std::string &name, const std::string &why)
{ {
std::ostringstream str; 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 // 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, const std::string &iname,
std::string *errorMessage) std::string *errorMessage)
{ {
@@ -1059,7 +1194,7 @@ SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name,
return 0; return 0;
} }
SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index, SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index,
name, iname.empty() ? name : iname); module, name, iname.empty() ? name : iname);
node->parseParameters(0, 0, parameters); node->parseParameters(0, 0, parameters);
node->addFlags(AdditionalSymbol); node->addFlags(AdditionalSymbol);
addChild(node); addChild(node);
@@ -1210,8 +1345,10 @@ SymbolGroupNodeVisitor::VisitResult
const std::string &fullIname, const std::string &fullIname,
unsigned /* child */, unsigned depth) unsigned /* child */, unsigned depth)
{ {
// Show container children only, no additional symbol below root. // Show container children only, no additional symbol below root (unless it is a watch node).
if (node->testFlags(SymbolGroupNode::Obscured|SymbolGroupNode::AdditionalSymbol)) if (node->testFlags(SymbolGroupNode::Obscured))
return VisitSkipChildren;
if (node->testFlags(SymbolGroupNode::AdditionalSymbol) && !node->testFlags(SymbolGroupNode::WatchNode))
return VisitSkipChildren; return VisitSkipChildren;
// Recurse to children only if expanded by explicit watchmodel request // Recurse to children only if expanded by explicit watchmodel request
// and initialized. // and initialized.

View File

@@ -103,8 +103,10 @@ public:
unsigned indexByIName(const char *) const; // (unsigned(-1) on failure unsigned indexByIName(const char *) const; // (unsigned(-1) on failure
AbstractSymbolGroupNode *childByIName(const char *) const; AbstractSymbolGroupNode *childByIName(const char *) const;
unsigned indexOf(const AbstractSymbolGroupNode *) const;
const AbstractSymbolGroupNode *parent() const { return m_parent; } const AbstractSymbolGroupNode *parent() const { return m_parent; }
AbstractSymbolGroupNode *parent() { return m_parent; }
unsigned flags() const { return m_flags; } unsigned flags() const { return m_flags; }
bool testFlags(unsigned f) const { return (m_flags & f) != 0; } bool testFlags(unsigned f) const { return (m_flags & f) != 0; }
@@ -151,20 +153,32 @@ class BaseSymbolGroupNode : public AbstractSymbolGroupNode
{ {
public: public:
virtual const AbstractSymbolGroupNodePtrVector &children() const { return m_children; } virtual const AbstractSymbolGroupNodePtrVector &children() const { return m_children; }
void addChild(AbstractSymbolGroupNode *c); // for watches
void removeChildAt(unsigned);
protected: protected:
explicit BaseSymbolGroupNode(const std::string &name, const std::string &iname); explicit BaseSymbolGroupNode(const std::string &name, const std::string &iname);
virtual ~BaseSymbolGroupNode(); virtual ~BaseSymbolGroupNode();
void reserveChildren(AbstractSymbolGroupNodePtrVector::size_type s) { m_children.reserve(s); } void reserveChildren(AbstractSymbolGroupNodePtrVector::size_type s) { m_children.reserve(s); }
void addChild(AbstractSymbolGroupNode *c);
private: private:
AbstractSymbolGroupNodePtrVector m_children; AbstractSymbolGroupNodePtrVector m_children;
void removeChildren(); 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 /* SymbolGroupNode: 'Real' node within a symbol group, identified by its index
* in IDebugSymbolGroup. * in IDebugSymbolGroup.
* Provides accessors for fixed-up symbol group value and a dumping facility * Provides accessors for fixed-up symbol group value and a dumping facility
@@ -187,6 +201,7 @@ class SymbolGroupNode : public BaseSymbolGroupNode
{ {
explicit SymbolGroupNode(SymbolGroup *symbolGroup, explicit SymbolGroupNode(SymbolGroup *symbolGroup,
ULONG index, ULONG index,
const std::string &module,
const std::string &name, const std::string &name,
const std::string &iname); const std::string &iname);
@@ -201,7 +216,8 @@ public:
ExpandedByDumper = 0x10, ExpandedByDumper = 0x10,
AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible
Obscured = 0x40, // Symbol is obscured by (for example) fake container children Obscured = 0x40, // Symbol is obscured by (for example) fake container children
ComplexDumperOk = 0x80 ComplexDumperOk = 0x80,
WatchNode = 0x100
}; };
typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector; typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
@@ -210,9 +226,10 @@ public:
SymbolParameterVector::size_type parameterOffset, SymbolParameterVector::size_type parameterOffset,
const SymbolParameterVector &vec); 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 // 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 const std::string &iname, // Desired iname, defaults to name
std::string *errorMessage); std::string *errorMessage);
@@ -239,6 +256,7 @@ public:
int dumperContainerSize() { return m_dumperContainerSize; } // Valid after dumper run int dumperContainerSize() { return m_dumperContainerSize; } // Valid after dumper run
unsigned size() const; // Size of value unsigned size() const; // Size of value
ULONG64 address() const; ULONG64 address() const;
std::string module() const { return m_module; }
bool expand(std::string *errorMessage); bool expand(std::string *errorMessage);
bool expandRunComplexDumpers(const SymbolGroupValueContext &ctx, std::string *errorMessage); bool expandRunComplexDumpers(const SymbolGroupValueContext &ctx, std::string *errorMessage);
@@ -254,15 +272,21 @@ public:
virtual SymbolGroupNode *asSymbolGroupNode() { return this; } virtual SymbolGroupNode *asSymbolGroupNode() { return this; }
virtual const SymbolGroupNode *asSymbolGroupNode() const { 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: private:
const SymbolGroupNode *symbolGroupNodeParent() const; const SymbolGroupNode *symbolGroupNodeParent() const;
SymbolGroupNode *symbolGroupNodeParent();
bool isArrayElement() const; bool isArrayElement() const;
// Notify about expansion of a node, shift indexes // Notify about expansion/collapsing of a node, shift indexes
bool notifyExpanded(ULONG index, ULONG insertedCount); bool notifyIndexesMoved(ULONG index, bool inserted, ULONG offset);
bool runSimpleDumpers(const SymbolGroupValueContext &ctx); bool runSimpleDumpers(const SymbolGroupValueContext &ctx);
std::wstring simpleDumpValue(const SymbolGroupValueContext &ctx); std::wstring simpleDumpValue(const SymbolGroupValueContext &ctx);
ULONG nextSymbolIndex() const;
SymbolGroup *const m_symbolGroup; SymbolGroup *const m_symbolGroup;
const std::string m_module;
ULONG m_index; ULONG m_index;
DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct. DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct.
std::wstring m_dumperValue; std::wstring m_dumperValue;

View File

@@ -198,6 +198,11 @@ ULONG64 SymbolGroupValue::address() const
return 0; return 0;
} }
std::string SymbolGroupValue::module() const
{
return isValid() ? m_node->module() : std::string();
}
// Temporary iname // Temporary iname
static inline std::string additionalSymbolIname(const SymbolGroup *g) static inline std::string additionalSymbolIname(const SymbolGroup *g)
{ {
@@ -236,7 +241,7 @@ SymbolGroupValue SymbolGroupValue::typeCastedValue(ULONG64 address, const char *
if (nonPointer) if (nonPointer)
str << " *"; str << " *";
str << ")(" << std::showbase << std::hex << address << ')'; str << ")(" << std::showbase << std::hex << address << ')';
if (SymbolGroupNode *node = sg->addSymbol(str.str(), if (SymbolGroupNode *node = sg->addSymbol(module(), str.str(),
additionalSymbolIname(sg), additionalSymbolIname(sg),
&m_errorMessage)) &m_errorMessage))
return SymbolGroupValue(node, m_context); return SymbolGroupValue(node, m_context);
@@ -277,6 +282,16 @@ unsigned SymbolGroupValue::isPointerType(const std::string &t)
return 0; 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() unsigned SymbolGroupValue::pointerSize()
{ {
static const unsigned ps = SymbolGroupValue::sizeOf("char *"); 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; 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'. /* 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 * as 'QtCored4![namespace::]qstrdup'. In the event someone really uses a different
* library prefix or namespaced Qt, this should be found. * library prefix or namespaced Qt, this should be found.
@@ -511,7 +532,7 @@ std::list<std::string>
// Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString') // Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString')
std::string SymbolGroupValue::resolveType(const std::string &typeIn, std::string SymbolGroupValue::resolveType(const std::string &typeIn,
const SymbolGroupValueContext &ctx, const SymbolGroupValueContext &ctx,
const SymbolGroup *current /* = 0 */) const std::string &currentModule /* = "" */)
{ {
enum { BufSize = 512 }; 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. // Use the module of the current symbol group for templates.
// This is because resolving some template types (std::list<> has been // This is because resolving some template types (std::list<> has been
// observed to result in 'QtGui4d!std::list', which subseqently fails. // observed to result in 'QtGui4d!std::list', which subseqently fails.
if (current && stripped.find('<') != std::string::npos) { if (!currentModule.empty() && stripped.find('<') != std::string::npos) {
std::string trc = current->module(); std::string trc = currentModule;
trc.push_back('!'); trc.push_back('!');
trc += stripped; trc += stripped;
return trc; 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() << ')'; str << '(' << qwPrivateType << "*)(" << std::showbase << std::hex << v.address() << ')';
const std::string name = str.str(); const std::string name = str.str();
SymbolGroupNode *qwPrivateNode 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()); qwPrivate = SymbolGroupValue(qwPrivateNode, v.context());
} }
const SymbolGroupValue oName = qwPrivate[unsigned(0)]["objectName"]; // QWidgetPrivate inherits QObjectPrivate const SymbolGroupValue oName = qwPrivate[unsigned(0)]["objectName"]; // QWidgetPrivate inherits QObjectPrivate
@@ -1414,8 +1435,8 @@ static inline std::string
const QtInfo &qtInfo, const QtInfo &qtInfo,
const SymbolGroupValue &contextHelper) const SymbolGroupValue &contextHelper)
{ {
const std::string module = contextHelper.node()->symbolGroup()->module(); std::string rc = QtInfo::prependModuleAndNameSpace(containerType, contextHelper.module(),
std::string rc = QtInfo::prependModuleAndNameSpace(containerType, module, qtInfo.nameSpace); qtInfo.nameSpace);
rc.push_back('<'); rc.push_back('<');
rc += QtInfo::prependModuleAndNameSpace(innerType1, std::string(), qtInfo.nameSpace); rc += QtInfo::prependModuleAndNameSpace(innerType1, std::string(), qtInfo.nameSpace);
if (!innerType2.empty()) { if (!innerType2.empty()) {

View File

@@ -97,6 +97,8 @@ public:
double floatValue(double defaultValue = -999) const; double floatValue(double defaultValue = -999) const;
ULONG64 pointerValue(ULONG64 defaultValue = 0) const; ULONG64 pointerValue(ULONG64 defaultValue = 0) const;
ULONG64 address() const; ULONG64 address() const;
std::string module() const;
// Return allocated array of data pointed to // Return allocated array of data pointed to
unsigned char *pointerData(unsigned length) const; unsigned char *pointerData(unsigned length) const;
// Return data pointed to as wchar_t/std::wstring (UTF16) // 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 addPointerType(const std::string &);
static std::string stripArrayType(const std::string &); static std::string stripArrayType(const std::string &);
static std::string stripModuleFromType(const std::string &type); static std::string stripModuleFromType(const std::string &type);
static std::string moduleOfType(const std::string &type);
// pointer type, return number of characters to strip // pointer type, return number of characters to strip
static unsigned isPointerType(const std::string &); 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') // Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString')
// Some operations on types (like adding symbols may fail non-deterministically // Some operations on types (like adding symbols may fail non-deterministically
// or be slow when the module specification is omitted). // or be slow when the module specification is omitted).
// If a current SymbolGroup is passed on, its module will be used for templates. // If a current SymbolGroup is passed on, its module will be used for templates.
static std::string resolveType(const std::string &type, static std::string resolveType(const std::string &type,
const SymbolGroupValueContext &ctx, const SymbolGroupValueContext &ctx,
const SymbolGroup *current = 0); const std::string &currentModule = std::string());
static std::list<std::string> resolveSymbol(const char *pattern, static std::list<std::string> resolveSymbol(const char *pattern,
const SymbolGroupValueContext &c, const SymbolGroupValueContext &c,

View File

@@ -85,6 +85,7 @@ Q_DECLARE_METATYPE(Debugger::Internal::MemoryAgent*)
enum { debug = 0 }; enum { debug = 0 };
enum { debugLocals = 0 }; enum { debugLocals = 0 };
enum { debugWatches = 0 };
enum { debugBreakpoints = 0 }; enum { debugBreakpoints = 0 };
#if 0 #if 0
@@ -703,16 +704,35 @@ void CdbEngine::detachDebugger()
postCommand(".detach", 0); postCommand(".detach", 0);
} }
static inline bool isWatchIName(const QByteArray &iname)
{
return iname.startsWith("watch");
}
void CdbEngine::updateWatchData(const WatchData &dataIn, void CdbEngine::updateWatchData(const WatchData &dataIn,
const WatchUpdateFlags & flags) const WatchUpdateFlags & flags)
{ {
if (debug) if (debug || debugLocals || debugWatches)
qDebug("CdbEngine::updateWatchData() %dms %s incr=%d: %s", qDebug("CdbEngine::updateWatchData() %dms accessible=%d %s incr=%d: %s",
elapsedLogTime(), stateName(state()), elapsedLogTime(), m_accessible, stateName(state()),
flags.tryIncremental, flags.tryIncremental,
qPrintable(dataIn.toString())); 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; WatchData data = dataIn;
data.setAllUnneeded(); data.setAllUnneeded();
watchHandler()->insertData(data); watchHandler()->insertData(data);
@@ -721,6 +741,22 @@ void CdbEngine::updateWatchData(const WatchData &dataIn,
updateLocalVariable(dataIn.iname); updateLocalVariable(dataIn.iname);
} }
void CdbEngine::handleAddWatch(const CdbExtensionCommandPtr &reply)
{
WatchData item = qvariant_cast<WatchData>(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 void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
{ {
if (debuggerCore()->boolSetting(VerboseLog)) if (debuggerCore()->boolSetting(VerboseLog))
@@ -737,22 +773,28 @@ void CdbEngine::addLocalsOptions(ByteArrayInputStream &str) const
void CdbEngine::updateLocalVariable(const QByteArray &iname) void CdbEngine::updateLocalVariable(const QByteArray &iname)
{ {
const int stackFrame = stackHandler()->currentIndex(); const bool isWatch = isWatchIName(iname);
if (stackFrame >= 0) { if (debugWatches)
QByteArray localsArguments; qDebug() << "updateLocalVariable watch=" << isWatch << iname;
ByteArrayInputStream str(localsArguments); QByteArray localsArguments;
addLocalsOptions(str); ByteArrayInputStream str(localsArguments);
str << blankSeparator << stackFrame << ' ' << iname; addLocalsOptions(str);
postExtensionCommand("locals", localsArguments, 0, &CdbEngine::handleLocals); if (!isWatch) {
} else { const int stackFrame = stackHandler()->currentIndex();
qWarning("Internal error; no stack frame in updateLocalVariable"); 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 unsigned CdbEngine::debuggerCapabilities() const
{ {
return DisassemblerCapability | RegisterCapability | ShowMemoryCapability return DisassemblerCapability | RegisterCapability | ShowMemoryCapability
|WatchpointCapability|JumpToLineCapability |WatchpointCapability|JumpToLineCapability|AddWatcherCapability
|BreakOnThrowAndCatchCapability; // Sort-of: Can break on throw(). |BreakOnThrowAndCatchCapability; // Sort-of: Can break on throw().
} }
@@ -1052,6 +1094,8 @@ void CdbEngine::activateFrame(int index)
void CdbEngine::updateLocals() void CdbEngine::updateLocals()
{ {
typedef QHash<QByteArray, int> WatcherHash;
const int frameIndex = stackHandler()->currentIndex(); const int frameIndex = stackHandler()->currentIndex();
if (frameIndex < 0) { if (frameIndex < 0) {
watchHandler()->beginCycle(); 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 // Required arguments: frame
str << blankSeparator << frameIndex; str << blankSeparator << frameIndex;
watchHandler()->beginCycle(); watchHandler()->beginCycle();

View File

@@ -177,6 +177,7 @@ private:
void handleThreads(const CdbExtensionCommandPtr &); void handleThreads(const CdbExtensionCommandPtr &);
void handlePid(const CdbExtensionCommandPtr &reply); void handlePid(const CdbExtensionCommandPtr &reply);
void handleLocals(const CdbExtensionCommandPtr &reply); void handleLocals(const CdbExtensionCommandPtr &reply);
void handleAddWatch(const CdbExtensionCommandPtr &reply);
void handleExpandLocals(const CdbExtensionCommandPtr &reply); void handleExpandLocals(const CdbExtensionCommandPtr &reply);
void handleRegisters(const CdbExtensionCommandPtr &reply); void handleRegisters(const CdbExtensionCommandPtr &reply);
void handleModules(const CdbExtensionCommandPtr &reply); void handleModules(const CdbExtensionCommandPtr &reply);