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*')
|
// 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,
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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, ¶meters, errorMessage))
|
|
||||||
break;
|
|
||||||
|
|
||||||
success = true;
|
|
||||||
} while (false);
|
|
||||||
delete [] frames;
|
|
||||||
if (!success) {
|
|
||||||
if (idebugSymbols)
|
|
||||||
idebugSymbols->Release();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
return new SymbolGroup(idebugSymbols, parameters, threadId, frame, func);
|
if (!ok && errorMessage->empty())
|
||||||
|
*errorMessage = "SymbolGroup::removeSymbol: Unimplemented removal operation for " + an->name();
|
||||||
|
|
||||||
|
if (!ok && SymbolGroupValue::verbose)
|
||||||
|
DebugPrint() << "SymbolGroup::removeSymbol failed: " << *errorMessage;
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline std::string msgNotFound(const std::string &nodeName)
|
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, ¶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 "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
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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 ¤tModule /* = "" */)
|
||||||
{
|
{
|
||||||
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()) {
|
||||||
|
|||||||
@@ -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 ¤tModule = 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,
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user