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