Debugger[New CDB]: Linked lists containers/container child expansion.

Introduce node hierarchy and move nodes to a separate file.
Introduce reference nodes that point to additional symbols and
symbols within the symbol tree (make deeply nested linked list
elements visible as array elements). Properly name container
elements as array elements 0..n. Fix pre-expansion of complex
dumpers.
This commit is contained in:
Friedemann Kleint
2010-12-15 11:01:01 +01:00
parent 95731517be
commit cc3dd43942
9 changed files with 1679 additions and 1268 deletions

View File

@@ -34,7 +34,7 @@
#include <functional> #include <functional>
typedef SymbolGroupNode::SymbolGroupNodePtrVector SymbolGroupNodePtrVector; typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
// Return size of container or -1 // Return size of container or -1
int containerSize(KnownType kt, SymbolGroupNode *n, const SymbolGroupValueContext &ctx) int containerSize(KnownType kt, SymbolGroupNode *n, const SymbolGroupValueContext &ctx)
@@ -150,16 +150,16 @@ int containerSize(KnownType kt, const SymbolGroupValue &v)
/* Generate a list of children by invoking the functions to obtain the value /* Generate a list of children by invoking the functions to obtain the value
* and the next link */ * and the next link */
template <class ValueFunction, class NextFunction> template <class ValueFunction, class NextFunction>
SymbolGroupNodePtrVector linkedListChildList(SymbolGroupValue headNode, AbstractSymbolGroupNodePtrVector linkedListChildList(SymbolGroupValue headNode,
int count, int count,
ValueFunction valueFunc, ValueFunction valueFunc,
NextFunction nextFunc) NextFunction nextFunc)
{ {
SymbolGroupNodePtrVector rc; AbstractSymbolGroupNodePtrVector rc;
rc.reserve(count); rc.reserve(count);
for (int i =0; i < count && headNode; i++) { for (int i =0; i < count && headNode; i++) {
if (const SymbolGroupValue value = valueFunc(headNode)) { if (const SymbolGroupValue value = valueFunc(headNode)) {
rc.push_back(value.node()); rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, value.node()));
headNode = nextFunc(headNode); headNode = nextFunc(headNode);
} else { } else {
break; break;
@@ -180,33 +180,33 @@ private:
}; };
// std::list<T>: Dummy head node and then a linked list of "_Next", "_Myval". // std::list<T>: Dummy head node and then a linked list of "_Next", "_Myval".
static inline SymbolGroupNodePtrVector stdListChildList(SymbolGroupNode *n, int count, static inline AbstractSymbolGroupNodePtrVector stdListChildList(SymbolGroupNode *n, int count,
const SymbolGroupValueContext &ctx) const SymbolGroupValueContext &ctx)
{ {
if (count) if (count)
if (const SymbolGroupValue head = SymbolGroupValue(n, ctx)[unsigned(0)][unsigned(0)]["_Myhead"]["_Next"]) if (const SymbolGroupValue head = SymbolGroupValue(n, ctx)[unsigned(0)][unsigned(0)]["_Myhead"]["_Next"])
return linkedListChildList(head, count, MemberByName("_Myval"), MemberByName("_Next")); return linkedListChildList(head, count, MemberByName("_Myval"), MemberByName("_Next"));
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }
// QLinkedList<T>: Dummy head node and then a linked list of "n", "t". // QLinkedList<T>: Dummy head node and then a linked list of "n", "t".
static inline SymbolGroupNodePtrVector qLinkedListChildList(SymbolGroupNode *n, int count, static inline AbstractSymbolGroupNodePtrVector qLinkedListChildList(SymbolGroupNode *n, int count,
const SymbolGroupValueContext &ctx) const SymbolGroupValueContext &ctx)
{ {
if (count) if (count)
if (const SymbolGroupValue head = SymbolGroupValue(n, ctx)["e"]["n"]) if (const SymbolGroupValue head = SymbolGroupValue(n, ctx)["e"]["n"])
return linkedListChildList(head, count, MemberByName("t"), MemberByName("n")); return linkedListChildList(head, count, MemberByName("t"), MemberByName("n"));
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }
/* Helper for array-type containers: /* Helper for array-type containers:
* Add a series of "*(innertype *)0x (address + n * size)" fake child symbols. * Add a series of "*(innertype *)0x (address + n * size)" fake child symbols.
* for a function generating a sequence of addresses. */ * for a function generating a sequence of addresses. */
template <class AddressFunc> template <class AddressFunc>
SymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc, AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc,
const std::string &innerType, int count) const std::string &innerType, int count)
{ {
SymbolGroupNodePtrVector rc; AbstractSymbolGroupNodePtrVector rc;
if (!count) if (!count)
return rc; return rc;
std::string errorMessage; std::string errorMessage;
@@ -217,8 +217,8 @@ SymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, AddressFunc addressFunc
if (!endsWith(innerType, '*')) if (!endsWith(innerType, '*'))
str << ' '; str << ' ';
str << "*)" << std::showbase << std::hex << addressFunc(); str << "*)" << std::showbase << std::hex << addressFunc();
if (SymbolGroupNode *child = sg->addSymbol(str.str(), toString(i), &errorMessage)) { if (SymbolGroupNode *child = sg->addSymbol(str.str(), std::string(), &errorMessage)) {
rc.push_back(child); rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, child));
} else { } else {
break; break;
} }
@@ -244,17 +244,17 @@ private:
const ULONG m_delta; const ULONG m_delta;
}; };
static inline SymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, ULONG64 address, static inline AbstractSymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, ULONG64 address,
const std::string &innerType, int count) 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); innerType, count);
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }
// std::vector<T> // std::vector<T>
static inline SymbolGroupNodePtrVector static inline AbstractSymbolGroupNodePtrVector
stdVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx) stdVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx)
{ {
if (count) { if (count) {
@@ -269,11 +269,11 @@ static inline SymbolGroupNodePtrVector
return arrayChildList(n->symbolGroup(), address, return arrayChildList(n->symbolGroup(), address,
SymbolGroupValue::stripPointerType(myFirst.type()), count); SymbolGroupValue::stripPointerType(myFirst.type()), count);
} }
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }
// QVector<T> // QVector<T>
static inline SymbolGroupNodePtrVector static inline AbstractSymbolGroupNodePtrVector
qVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx) qVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx)
{ {
if (count) { if (count) {
@@ -284,7 +284,7 @@ static inline SymbolGroupNodePtrVector
if (const ULONG64 arrayAddress = firstElementV.address()) if (const ULONG64 arrayAddress = firstElementV.address())
return arrayChildList(n->symbolGroup(), arrayAddress, firstElementV.type(), count); return arrayChildList(n->symbolGroup(), arrayAddress, firstElementV.type(), count);
} }
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }
// Helper function for arrayChildList() for use with QLists of large types that are an // Helper function for arrayChildList() for use with QLists of large types that are an
@@ -301,32 +301,32 @@ private:
}; };
// QList<>. // QList<>.
static inline SymbolGroupNodePtrVector static inline AbstractSymbolGroupNodePtrVector
qListChildList(const SymbolGroupValue &v, int count) qListChildList(const SymbolGroupValue &v, int count)
{ {
// QList<T>: d/array is declared as array of void *[]. Dereference first // QList<T>: d/array is declared as array of void *[]. Dereference first
// element to obtain address. // element to obtain address.
if (!count) if (!count)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
const SymbolGroupValue dV = v["d"]; const SymbolGroupValue dV = v["d"];
if (!dV) if (!dV)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
const int begin = dV["begin"].intValue(); const int begin = dV["begin"].intValue();
if (begin < 0) if (begin < 0)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
const SymbolGroupValue firstElementV = dV["array"][unsigned(0)]; const SymbolGroupValue firstElementV = dV["array"][unsigned(0)];
if (!firstElementV) if (!firstElementV)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
ULONG64 arrayAddress = firstElementV.address(); ULONG64 arrayAddress = firstElementV.address();
if (!arrayAddress) if (!arrayAddress)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
const std::vector<std::string> innerTypes = v.innerTypes(); const std::vector<std::string> innerTypes = v.innerTypes();
if (innerTypes.size() != 1) if (innerTypes.size() != 1)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
const std::string &innerType = innerTypes.front(); const std::string &innerType = innerTypes.front();
const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str()); const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str());
if (!innerTypeSize) if (!innerTypeSize)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
/* QList<> is: /* QList<> is:
* 1) An array of 'void *[]' where T values are coerced into the elements for * 1) An array of 'void *[]' where T values are coerced into the elements for
* POD/pointer types and small, movable or primitive Qt types. That is, smaller * POD/pointer types and small, movable or primitive Qt types. That is, smaller
@@ -355,10 +355,10 @@ static inline SymbolGroupNodePtrVector
const HRESULT hr = v.context().dataspaces->ReadVirtual(arrayAddress + begin * pointerSize, data, allocSize, &bytesRead); const HRESULT hr = v.context().dataspaces->ReadVirtual(arrayAddress + begin * pointerSize, data, allocSize, &bytesRead);
if (FAILED(hr) || bytesRead != allocSize) { if (FAILED(hr) || bytesRead != allocSize) {
delete [] data; delete [] data;
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }
// Generate sequence of addresses from pointer array // Generate sequence of addresses from pointer array
const SymbolGroupNodePtrVector 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(), AddressArraySequence<ULONG64>(reinterpret_cast<const ULONG64 *>(data)), innerType, count) :
arrayChildList(v.node()->symbolGroup(), AddressArraySequence<ULONG32>(reinterpret_cast<const ULONG32 *>(data)), innerType, count); arrayChildList(v.node()->symbolGroup(), AddressArraySequence<ULONG32>(reinterpret_cast<const ULONG32 *>(data)), innerType, count);
delete [] data; delete [] data;
@@ -369,11 +369,11 @@ static inline SymbolGroupNodePtrVector
innerType, count); innerType, count);
} }
SymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type, AbstractSymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type,
int size, const SymbolGroupValueContext &ctx) int size, const SymbolGroupValueContext &ctx)
{ {
if (!size) if (!size)
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
if (size > 100) if (size > 100)
size = 100; size = 100;
switch (type) { switch (type) {
@@ -400,5 +400,5 @@ SymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type,
case KT_StdList: case KT_StdList:
return stdListChildList(node, size , ctx); return stdListChildList(node, size , ctx);
} }
return SymbolGroupNodePtrVector(); return AbstractSymbolGroupNodePtrVector();
} }

View File

@@ -31,6 +31,7 @@
#define CONTAINERS_H #define CONTAINERS_H
struct SymbolGroupValueContext; struct SymbolGroupValueContext;
class AbstractSymbolGroupNode;
class SymbolGroupNode; class SymbolGroupNode;
class SymbolGroupValue; class SymbolGroupValue;
@@ -44,9 +45,9 @@ int containerSize(KnownType kt, const SymbolGroupValue &v);
int containerSize(KnownType kt, SymbolGroupNode *n, const SymbolGroupValueContext &ctx); int containerSize(KnownType kt, SymbolGroupNode *n, const SymbolGroupValueContext &ctx);
/* Create a list of children of containers. */ /* Create a list of children of containers. */
std::vector<SymbolGroupNode *> containerChildren(SymbolGroupNode *node, std::vector<AbstractSymbolGroupNode *> containerChildren(SymbolGroupNode *node,
int type, int type,
int size, int size,
const SymbolGroupValueContext &ctx); const SymbolGroupValueContext &ctx);
#endif // CONTAINERS_H #endif // CONTAINERS_H

View File

@@ -48,6 +48,7 @@ QT -= core
SOURCES += qtcreatorcdbextension.cpp \ SOURCES += qtcreatorcdbextension.cpp \
extensioncontext.cpp \ extensioncontext.cpp \
eventcallback.cpp \ eventcallback.cpp \
symbolgroupnode.cpp \
symbolgroup.cpp \ symbolgroup.cpp \
common.cpp \ common.cpp \
stringutils.cpp \ stringutils.cpp \
@@ -68,4 +69,5 @@ HEADERS += extensioncontext.h \
base64.h \ base64.h \
symbolgroupvalue.h \ symbolgroupvalue.h \
containers.h \ containers.h \
knowntype.h knowntype.h \
symbolgroupnode.h

View File

@@ -100,8 +100,9 @@ static const CommandDescription commandDescriptions[] = {
"Prints inferior process id and hooks up output callbacks.", "Prints inferior process id and hooks up output callbacks.",
"[-t token]"}, "[-t token]"},
{"expandlocals", "Expands local variables by iname in symbol group.", {"expandlocals", "Expands local variables by iname in symbol group.",
"[-t token] <frame-number> <iname1-list>\n" "[-t token] [-c] <frame-number> <iname1-list>\n"
"iname1-list: Comma-separated list of inames"}, "iname1-list: Comma-separated list of inames\n"
"-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] [T formats] [-I formats] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n" "[-t token] [T formats] [-I formats] [-c] [-h] [-d] [-e expand-list] [-u uninitialized-list]\n<frame-number> [iname]\n"
@@ -247,21 +248,40 @@ extern "C" HRESULT CALLBACK expandlocals(CIDebugClient *client, PCSTR args)
std::string errorMessage; std::string errorMessage;
int token; int token;
const StringVector tokens = commandTokens<StringVector>(args, &token); StringList tokens = commandTokens<StringList>(args, &token);
StringVector inames; StringVector inames;
if (tokens.size() == 2u && integerFromString(tokens.front(), &frame)) { bool runComplexDumpers = false;
do {
if (!tokens.empty() && tokens.front() == "-c") {
runComplexDumpers = true;
tokens.pop_front();
}
if (tokens.empty() || !integerFromString(tokens.front(), &frame)) {
errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
break;
}
tokens.pop_front();
if (tokens.empty()) {
errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
break;
}
split(tokens.front(), ',', std::back_inserter(inames));
} while (false);
if (errorMessage.empty())
symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage); symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, &errorMessage);
split(tokens.at(1), ',', std::back_inserter(inames));
} else { if (!symGroup) {
errorMessage = singleLineUsage(commandDescriptions[CmdExpandlocals]);
}
if (symGroup) {
const unsigned succeeded = symGroup->expandList(inames, &errorMessage);
ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s",
succeeded, unsigned(inames.size()), errorMessage.c_str());
} else {
ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str()); ExtensionContext::instance().report('N', token, "expandlocals", errorMessage.c_str());
return S_OK;
} }
const unsigned succeeded = runComplexDumpers ?
symGroup->expandListRunComplexDumpers(inames, SymbolGroupValueContext(exc.dataSpaces()), &errorMessage) :
symGroup->expandList(inames, &errorMessage);
ExtensionContext::instance().report('R', token, "expandlocals", "%u/%u %s",
succeeded, unsigned(inames.size()), errorMessage.c_str());
return S_OK; return S_OK;
} }
@@ -338,18 +358,25 @@ 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());
SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage); SymbolGroup * const symGroup = ExtensionContext::instance().symbolGroup(exc.symbols(), exc.threadId(), frame, errorMessage);
if (!symGroup) if (!symGroup)
return std::string(); return std::string();
if (!expandedInames.empty())
symGroup->expandList(expandedInames, errorMessage);
if (!uninitializedInames.empty()) if (!uninitializedInames.empty())
symGroup->markUninitialized(uninitializedInames); symGroup->markUninitialized(uninitializedInames);
if (!expandedInames.empty()) {
if (parameters.dumpFlags & DumpParameters::DumpComplexDumpers) {
symGroup->expandListRunComplexDumpers(expandedInames, dumpContext, errorMessage);
} else {
symGroup->expandList(expandedInames, errorMessage);
}
}
if (debugOutput) if (debugOutput)
return symGroup->debug(iname, debugOutput - 1); return symGroup->debug(iname, debugOutput - 1);
const SymbolGroupValueContext dumpContext(exc.dataSpaces());
return iname.empty() ? return iname.empty() ?
symGroup->dump(dumpContext, parameters) : symGroup->dump(dumpContext, parameters) :
symGroup->dump(iname, dumpContext, parameters, errorMessage); symGroup->dump(iname, dumpContext, parameters, errorMessage);
@@ -393,13 +420,13 @@ static std::string dumplocalHelper(ExtensionCommandContext &exc,PCSTR args, int
if (!symGroup) if (!symGroup)
return std::string(); return std::string();
SymbolGroupNode *n = symGroup->find(iname); AbstractSymbolGroupNode *n = symGroup->find(iname);
if (!n) { if (!n || !n->asSymbolGroupNode()) {
*errorMessage = "No such iname " + iname; *errorMessage = "No such iname " + iname;
return std::string(); return std::string();
} }
std::wstring value; std::wstring value;
if (!dumpSimpleType(n, SymbolGroupValueContext(exc.dataSpaces()), &value)) { if (!dumpSimpleType(n->asSymbolGroupNode(), SymbolGroupValueContext(exc.dataSpaces()), &value)) {
*errorMessage = "Cannot dump " + iname; *errorMessage = "Cannot dump " + iname;
return std::string(); return std::string();
} }

File diff suppressed because it is too large Load Diff

View File

@@ -31,197 +31,7 @@
#define SYMBOLGROUP_H #define SYMBOLGROUP_H
#include "common.h" #include "common.h"
#include "symbolgroupnode.h"
#include <string>
#include <vector>
#include <map>
#include <iosfwd>
std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p);
class SymbolGroupNodeVisitor;
class SymbolGroup;
struct SymbolGroupValueContext;
// All parameters for dumping in one struct.
struct DumpParameters
{
typedef std::map<std::string, int> FormatMap; // type or iname to format
enum DumpFlags
{
DumpHumanReadable = 0x1,
DumpComplexDumpers = 0x2
};
DumpParameters();
bool humanReadable() const { return dumpFlags & DumpHumanReadable; }
// Helper to decode format option arguments.
static FormatMap decodeFormatArgument(const std::string &f);
bool recode(const std::string &type, const std::string &iname,
const SymbolGroupValueContext &ctx,
std::wstring *value, int *encoding) const;
int format(const std::string &type, const std::string &iname) const;
unsigned dumpFlags;
FormatMap typeFormats;
FormatMap individualFormats;
};
/* Thin wrapper around a symbol group entry. Provides accessors for fixed-up
* symbol group value and a dumping facility consisting of:
* - 'Simple' dumping done when running the DumpVisitor. This produces one
* line of formatted output shown for the class. These values
* values should are displayed, while still allowing for expansion of the structure
* in the debugger.
* It also pre-determines some information for complex dumping (type, container).
* - 'Complex' dumping: Obscures the symbol group children by fake children, for
* example container children, run when calling SymbolGroup::dump with an iname.
* The fake children are appended to the child list (other children are just marked as
* obscured for GDBMI dumping so that SymbolGroupValue expressions still work as before).
* The dumping is mostly based on SymbolGroupValue expressions.
* in the debugger. Evaluating those dumpers might expand symbol nodes, which are
* then marked as 'ExpandedByDumper'. This stops the dump recursion to prevent
* outputting data that were not explicitly expanded by the watch handler. */
class SymbolGroupNode {
SymbolGroupNode(const SymbolGroupNode&);
SymbolGroupNode& operator=(const SymbolGroupNode&);
explicit SymbolGroupNode(SymbolGroup *symbolGroup,
ULONG index,
const std::string &name,
const std::string &iname,
SymbolGroupNode *parent = 0);
public:
enum Flags {
Uninitialized = 0x1,
SimpleDumperNotApplicable = 0x2, // No dumper available for type
SimpleDumperOk = 0x4, // Internal dumper ran, value set
SimpleDumperFailed = 0x8, // Internal dumper failed
SimpleDumperMask = SimpleDumperNotApplicable|SimpleDumperOk|SimpleDumperFailed,
ExpandedByDumper = 0x10,
AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible
Obscured = 0x40, // Symbol is obscured by (for example) fake container children
ComplexDumperOk = 0x80
};
typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
typedef std::vector<SymbolGroupNode *> SymbolGroupNodePtrVector;
typedef SymbolGroupNodePtrVector::iterator SymbolGroupNodePtrVectorIterator;
typedef SymbolGroupNodePtrVector::const_iterator SymbolGroupNodePtrVectorConstIterator;
~SymbolGroupNode() { removeChildren(); }
// Indicate reference
void setReferencedBy(SymbolGroupNode *n);
void removeChildren();
void parseParameters(SymbolParameterVector::size_type index,
SymbolParameterVector::size_type parameterOffset,
const SymbolParameterVector &vec);
static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec);
// For root nodes, only: Add a new symbol by name
SymbolGroupNode *addSymbolByName(const std::string &name, // Expression like 'myarray[1]'
const std::string &iname, // Desired iname, defaults to name
std::string *errorMessage);
const std::string &name() const { return m_name; }
std::string fullIName() const;
const std::string &iName() const { return m_iname; }
const SymbolGroupNodePtrVector &children() const { return m_children; }
SymbolGroupNode *childAt(unsigned) const;
unsigned indexByIName(const char *) const; // (unsigned(-1) on failure
SymbolGroupNode *childByIName(const char *) const;
const SymbolGroupNode *parent() const { return m_parent; }
const SymbolGroupNode *referencedParent() const { return m_referencedBy ? m_referencedBy : m_parent; }
SymbolGroup *symbolGroup() const { return m_symbolGroup; }
// I/O: Gdbmi dump for Visitors
void dump(std::ostream &str, const DumpParameters &p, const SymbolGroupValueContext &ctx);
// I/O: debug for Visitors
void debug(std::ostream &os, unsigned verbosity, unsigned depth) const;
std::wstring symbolGroupRawValue() const;
std::wstring symbolGroupFixedValue() const;
std::string type() const;
int dumperType() const { return m_dumperType; } // Valid after dumper run
int dumperContainerSize() { return m_dumperContainerSize; } // Valid after dumper run
unsigned size() const; // Size of value
ULONG64 address() const;
bool accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth);
bool expand(std::string *errorMessage);
bool isExpanded() const { return !m_children.empty(); }
bool canExpand() const { return m_parameters.SubElements > 0; }
void runComplexDumpers(const SymbolGroupValueContext &ctx);
// Cast to a different type. Works only on unexpanded nodes
bool typeCast(const std::string &desiredType, std::string *errorMessage);
ULONG subElements() const { return m_parameters.SubElements; }
ULONG index() const { return m_index; }
unsigned flags() const { return m_flags; }
void setFlags(unsigned f) { m_flags = f; }
void addFlags(unsigned f) { m_flags |= f; }
void clearFlags(unsigned f) { m_flags &= ~f; }
private:
bool isArrayElement() const;
// Notify about expansion of a node, shift indexes
bool notifyExpanded(ULONG index, ULONG insertedCount);
std::wstring simpleDumpValue(const SymbolGroupValueContext &ctx,
const DumpParameters &p);
SymbolGroup *const m_symbolGroup;
SymbolGroupNode *m_parent;
// Indicates a fake child (container value). Used for the full iname
SymbolGroupNode *m_referencedBy;
ULONG m_index;
DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct.
SymbolGroupNodePtrVector m_children;
const std::string m_name;
const std::string m_iname;
unsigned m_flags;
std::wstring m_dumperValue;
int m_dumperType;
int m_dumperContainerSize;
};
/* Visitor that takes care of iterating over the nodes
* visit() is not called for the (invisible) root node, but starting with the
* root's children with depth=0.
* Return true from visit() to terminate the recursion. */
class SymbolGroupNodeVisitor {
SymbolGroupNodeVisitor(const SymbolGroupNodeVisitor&);
SymbolGroupNodeVisitor& operator=(const SymbolGroupNodeVisitor&);
friend class SymbolGroupNode;
protected:
SymbolGroupNodeVisitor() {}
public:
virtual ~SymbolGroupNodeVisitor() {}
protected:
enum VisitResult {
VisitContinue,
VisitSkipChildren,
VisitStop
};
private:
virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth) = 0;
// Helper for formatting output.
virtual void childrenVisited(const SymbolGroupNode * /* node */, unsigned /* depth */) {}
};
// Thin wrapper around a symbol group storing a tree of expanded symbols rooted on // Thin wrapper around a symbol group storing a tree of expanded symbols rooted on
// a fake "locals" root element. // a fake "locals" root element.
@@ -233,15 +43,15 @@ public:
typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector; typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
private: private:
SymbolGroup(const SymbolGroup &);
SymbolGroup &operator=(const SymbolGroup &);
explicit SymbolGroup(CIDebugSymbolGroup *, explicit SymbolGroup(CIDebugSymbolGroup *,
const SymbolParameterVector &vec, const SymbolParameterVector &vec,
ULONG threadId, unsigned frame); ULONG threadId, unsigned frame);
SymbolGroup(const SymbolGroup &);
SymbolGroup &operator=(const SymbolGroup &);
public: public:
typedef SymbolGroupNode::SymbolGroupNodePtrVector SymbolGroupNodePtrVector; typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector AbstractSymbolGroupNodePtrVector;
static SymbolGroup *create(CIDebugControl *control, static SymbolGroup *create(CIDebugControl *control,
CIDebugSymbols *, CIDebugSymbols *,
@@ -262,17 +72,22 @@ public:
ULONG threadId() const { return m_threadId; } 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; }
SymbolGroupNode *find(const std::string &iname) const; AbstractSymbolGroupNode *find(const std::string &iname) const;
// Expand a node list "locals.i1,locals.i2", expanding all nested child nodes
// (think mkdir -p).
unsigned expandList(const std::vector<std::string> &nodes, std::string *errorMessage);
// Mark uninitialized (top level only)
void markUninitialized(const std::vector<std::string> &nodes);
// Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible // Expand a single node "locals.A.B" requiring that "locals.A.B" is already visible
// (think mkdir without -p). // (think mkdir without -p).
bool expand(const std::string &node, std::string *errorMessage); bool expand(const std::string &node, std::string *errorMessage);
bool expandRunComplexDumpers(const std::string &node, const SymbolGroupValueContext &ctx, std::string *errorMessage);
// Expand a node list "locals.i1,locals.i2", expanding all nested child nodes
// (think mkdir -p).
unsigned expandList(const std::vector<std::string> &nodes, std::string *errorMessage);
unsigned expandListRunComplexDumpers(const std::vector<std::string> &nodes,
const SymbolGroupValueContext &ctx,
std::string *errorMessage);
// Mark uninitialized (top level only)
void markUninitialized(const std::vector<std::string> &nodes);
// 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
@@ -296,7 +111,7 @@ public:
std::string *errorMessage); std::string *errorMessage);
private: private:
inline SymbolGroupNode *findI(const std::string &iname) const; 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);
@@ -307,34 +122,4 @@ private:
SymbolGroupNode *m_root; SymbolGroupNode *m_root;
}; };
// Debug output visitor.
class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
public:
explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
private:
virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth);
std::ostream &m_os;
const unsigned m_verbosity;
};
// Gdbmi dump output visitor.
class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
public:
explicit DumpSymbolGroupNodeVisitor(std::ostream &os,
const SymbolGroupValueContext &context,
const DumpParameters &parameters = DumpParameters());
private:
virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth);
virtual void childrenVisited(const SymbolGroupNode * node, unsigned depth);
std::ostream &m_os;
const SymbolGroupValueContext &m_context;
const DumpParameters &m_parameters;
bool m_visitChildren;
unsigned m_lastDepth;
};
#endif // SYMBOLGROUP_H #endif // SYMBOLGROUP_H

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,362 @@
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/
#ifndef SYMBOLGROUPNODE_H
#define SYMBOLGROUPNODE_H
#include "common.h"
#include <vector>
#include <string>
#include <map>
#include <iosfwd>
std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p);
class SymbolGroupNodeVisitor;
class SymbolGroup;
struct SymbolGroupValueContext;
class SymbolGroupNode;
// All parameters for GDBMI dumping in one struct.
struct DumpParameters
{
typedef std::map<std::string, int> FormatMap; // type or iname to format
enum DumpFlags
{
DumpHumanReadable = 0x1,
DumpComplexDumpers = 0x2
};
DumpParameters();
bool humanReadable() const { return dumpFlags & DumpHumanReadable; }
// Helper to decode format option arguments.
static FormatMap decodeFormatArgument(const std::string &f);
bool recode(const std::string &type, const std::string &iname,
const SymbolGroupValueContext &ctx,
std::wstring *value, int *encoding) const;
int format(const std::string &type, const std::string &iname) const;
unsigned dumpFlags;
FormatMap typeFormats;
FormatMap individualFormats;
};
// Base class for a node of SymbolGroup, handling the list of children.
class AbstractSymbolGroupNode
{
AbstractSymbolGroupNode(const AbstractSymbolGroupNode&);
AbstractSymbolGroupNode& operator=(const AbstractSymbolGroupNode&);
public:
typedef std::vector<AbstractSymbolGroupNode *> AbstractSymbolGroupNodePtrVector;
typedef AbstractSymbolGroupNodePtrVector::iterator AbstractSymbolGroupNodePtrVectorIterator;
typedef AbstractSymbolGroupNodePtrVector::const_iterator AbstractSymbolGroupNodePtrVectorConstIterator;
virtual ~AbstractSymbolGroupNode();
// Name to appear in watchwindow
const std::string &name() const { return m_name; }
// 'iname' used as an internal id.
const std::string &iName() const { return m_iname; }
// Full iname 'local.x.foo': WARNING: this returns the absolute path not
// taking reference nodes into account.
std::string absoluteFullIName() const;
virtual const AbstractSymbolGroupNodePtrVector &children() const = 0;
AbstractSymbolGroupNode *childAt(unsigned) const;
unsigned indexByIName(const char *) const; // (unsigned(-1) on failure
AbstractSymbolGroupNode *childByIName(const char *) const;
const AbstractSymbolGroupNode *parent() const { return m_parent; }
unsigned flags() const { return m_flags; }
bool testFlags(unsigned f) const { return (m_flags & f) != 0; }
void setFlags(unsigned f) { m_flags = f; }
void addFlags(unsigned f) { m_flags |= f; }
void clearFlags(unsigned f) { m_flags &= ~f; }
virtual SymbolGroupNode *asSymbolGroupNode() { return 0; }
virtual const SymbolGroupNode *asSymbolGroupNode() const { return 0; }
virtual const AbstractSymbolGroupNode *resolveReference() const { return this; }
virtual AbstractSymbolGroupNode *resolveReference() { return this; }
bool accept(SymbolGroupNodeVisitor &visitor,
const std::string &visitingParentIname,
unsigned child, unsigned depth);
// I/O: GDBMI dump for Visitors
virtual void dump(std::ostream &str, const std::string &visitingFullIname,
const DumpParameters &p, const SymbolGroupValueContext &ctx) = 0;
// I/O: debug output for Visitors
virtual void debug(std::ostream &os, const std::string &visitingFullIname,
unsigned verbosity, unsigned depth) const;
// For BaseSymbolGroupNode only.
void setParent(AbstractSymbolGroupNode *n);
protected:
explicit AbstractSymbolGroupNode(const std::string &name, const std::string &iname);
// Basic GDBMI dumping
static inline void dumpBasicData(std::ostream &str, const std::string &aName,
const std::string &aFullIname,
const std::string &type = std::string(),
const std::string &expression = std::string());
private:
const std::string m_name;
const std::string m_iname;
AbstractSymbolGroupNode *m_parent;
unsigned m_flags;
};
// Base class for a node of SymbolGroup with a flat list of children.
class BaseSymbolGroupNode : public AbstractSymbolGroupNode
{
public:
virtual const AbstractSymbolGroupNodePtrVector &children() const { return m_children; }
protected:
explicit BaseSymbolGroupNode(const std::string &name, const std::string &iname);
virtual ~BaseSymbolGroupNode();
void reserveChildren(AbstractSymbolGroupNodePtrVector::size_type s) { m_children.reserve(s); }
void addChild(AbstractSymbolGroupNode *c);
private:
AbstractSymbolGroupNodePtrVector m_children;
void removeChildren();
};
/* SymbolGroupNode: 'Real' node within a symbol group, identified by its index.
* Provides accessors for fixed-up symbol group value and a dumping facility
* consisting of:
* - 'Simple' dumping done when running the DumpVisitor. This produces one
* line of formatted output shown for the class. These values
* values should are displayed, while still allowing for expansion of the structure
* in the debugger.
* It also pre-determines some information for complex dumping (type, container).
* - 'Complex' dumping: Obscures the symbol group children by fake children, for
* example container children, run when calling SymbolGroup::dump with an iname.
* The fake children are appended to the child list (other children are just marked as
* obscured for GDBMI dumping so that SymbolGroupValue expressions still work as before).
* The dumping is mostly based on SymbolGroupValue expressions.
* in the debugger. Evaluating those dumpers might expand symbol nodes, which are
* then marked as 'ExpandedByDumper'. This stops the dump recursion to prevent
* outputting data that were not explicitly expanded by the watch handler. */
class SymbolGroupNode : public BaseSymbolGroupNode
{
explicit SymbolGroupNode(SymbolGroup *symbolGroup,
ULONG index,
const std::string &name,
const std::string &iname);
public:
enum Flags
{
Uninitialized = 0x1,
SimpleDumperNotApplicable = 0x2, // No dumper available for type
SimpleDumperOk = 0x4, // Internal dumper ran, value set
SimpleDumperFailed = 0x8, // Internal dumper failed
SimpleDumperMask = SimpleDumperNotApplicable|SimpleDumperOk|SimpleDumperFailed,
ExpandedByDumper = 0x10,
AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible
Obscured = 0x40, // Symbol is obscured by (for example) fake container children
ComplexDumperOk = 0x80
};
typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
void parseParameters(SymbolParameterVector::size_type index,
SymbolParameterVector::size_type parameterOffset,
const SymbolParameterVector &vec);
static SymbolGroupNode *create(SymbolGroup *sg, const std::string &name, const SymbolParameterVector &vec);
// For root nodes, only: Add a new symbol by name
SymbolGroupNode *addSymbolByName(const std::string &name, // Expression like 'myarray[1]'
const std::string &iname, // Desired iname, defaults to name
std::string *errorMessage);
SymbolGroup *symbolGroup() const { return m_symbolGroup; }
// I/O: Gdbmi dump for Visitors
virtual void dump(std::ostream &str, const std::string &fullIname,
const DumpParameters &p, const SymbolGroupValueContext &ctx);
// Dumper functionality for reference nodes.
void dumpNode(std::ostream &str, const std::string &aName, const std::string &aFullIName,
const DumpParameters &p, const SymbolGroupValueContext &ctx);
// I/O: debug for Visitors
virtual void debug(std::ostream &os, const std::string &visitingFullIname,
unsigned verbosity, unsigned depth) const;
std::wstring symbolGroupRawValue() const;
std::wstring symbolGroupFixedValue() const;
std::string type() const;
int dumperType() const { return m_dumperType; } // Valid after dumper run
int dumperContainerSize() { return m_dumperContainerSize; } // Valid after dumper run
unsigned size() const; // Size of value
ULONG64 address() const;
bool expand(std::string *errorMessage);
bool expandRunComplexDumpers(const SymbolGroupValueContext &ctx, std::string *errorMessage);
bool isExpanded() const { return !children().empty(); }
bool canExpand() const { return m_parameters.SubElements > 0; }
void runComplexDumpers(const SymbolGroupValueContext &ctx);
// Cast to a different type. Works only on unexpanded nodes
bool typeCast(const std::string &desiredType, std::string *errorMessage);
ULONG subElements() const { return m_parameters.SubElements; }
ULONG index() const { return m_index; }
virtual SymbolGroupNode *asSymbolGroupNode() { return this; }
virtual const SymbolGroupNode *asSymbolGroupNode() const { return this; }
private:
const SymbolGroupNode *symbolGroupNodeParent() const;
bool isArrayElement() const;
// Notify about expansion of a node, shift indexes
bool notifyExpanded(ULONG index, ULONG insertedCount);
std::wstring simpleDumpValue(const SymbolGroupValueContext &ctx);
SymbolGroup *const m_symbolGroup;
ULONG m_index;
DEBUG_SYMBOL_PARAMETERS m_parameters; // Careful when using ParentSymbol. It might not be correct.
std::wstring m_dumperValue;
int m_dumperType;
int m_dumperContainerSize;
};
// Artificial node referencing another (real) SymbolGroupNode (added symbol or
// symbol from within a linked list structure. Forwards dumping to referenced node
// using its own name/iname.
class ReferenceSymbolGroupNode : public AbstractSymbolGroupNode
{
public:
explicit ReferenceSymbolGroupNode(const std::string &name,
const std::string &iname,
SymbolGroupNode *referencedNode);
// Convenience to create a node name name='[1]', iname='1' for arrays
static ReferenceSymbolGroupNode *createArrayNode(int index,
SymbolGroupNode *referencedNode);
virtual void dump(std::ostream &str, const std::string &visitingFullIname,
const DumpParameters &p, const SymbolGroupValueContext &ctx);
virtual void debug(std::ostream &os, const std::string &visitingFullIname,
unsigned verbosity, unsigned depth) const;
virtual const AbstractSymbolGroupNodePtrVector &children() const { return m_referencedNode->children(); }
virtual const AbstractSymbolGroupNode *resolveReference() const { return m_referencedNode; }
virtual AbstractSymbolGroupNode *resolveReference() { return m_referencedNode; }
private:
SymbolGroupNode * const m_referencedNode;
};
/* Visitor that takes care of iterating over the nodes and
* building the full iname path ('local.foo.bar') that is required for
* GDBMI dumping. The full name depends on the path on which a node was reached
* for referenced nodes (a linked list element can be reached via array index
* or by expanding the whole structure).
* visit() is not called for the (invisible) root node, but starting with the
* root's children with depth=0.
* Return true from visit() to terminate the recursion. */
class SymbolGroupNodeVisitor {
SymbolGroupNodeVisitor(const SymbolGroupNodeVisitor&);
SymbolGroupNodeVisitor& operator=(const SymbolGroupNodeVisitor&);
friend class AbstractSymbolGroupNode;
protected:
SymbolGroupNodeVisitor() {}
public:
virtual ~SymbolGroupNodeVisitor() {}
// "local.vi" -> "local"
static std::string parentIname(const std::string &iname);
static const char iNamePathSeparator = '.';
protected:
enum VisitResult
{
VisitContinue,
VisitSkipChildren,
VisitStop
};
private:
virtual VisitResult visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth) = 0;
// Helper for formatting output.
virtual void childrenVisited(const AbstractSymbolGroupNode * /* node */, unsigned /* depth */) {}
};
// Debug output visitor.
class DebugSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
public:
explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
private:
virtual VisitResult visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth);
std::ostream &m_os;
const unsigned m_verbosity;
};
// GDBMI dump output visitor.
class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
public:
explicit DumpSymbolGroupNodeVisitor(std::ostream &os,
const SymbolGroupValueContext &context,
const DumpParameters &parameters = DumpParameters());
private:
virtual VisitResult visit(AbstractSymbolGroupNode *node,
const std::string &fullIname,
unsigned child, unsigned depth);
virtual void childrenVisited(const AbstractSymbolGroupNode * node, unsigned depth);
std::ostream &m_os;
const SymbolGroupValueContext &m_context;
const DumpParameters &m_parameters;
bool m_visitChildren;
unsigned m_lastDepth;
};
#endif // SYMBOLGROUPNODE_H

View File

@@ -54,7 +54,8 @@ SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const
{ {
if (ensureExpanded()) if (ensureExpanded())
if (index < m_node->children().size()) if (index < m_node->children().size())
return SymbolGroupValue(m_node->children().at(index), m_context); if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode())
return SymbolGroupValue(n, m_context);
return SymbolGroupValue(); return SymbolGroupValue();
} }
@@ -78,8 +79,9 @@ bool SymbolGroupValue::ensureExpanded() const
SymbolGroupValue SymbolGroupValue::operator[](const char *name) const SymbolGroupValue SymbolGroupValue::operator[](const char *name) const
{ {
if (ensureExpanded()) if (ensureExpanded())
if (SymbolGroupNode *child = m_node->childByIName(name)) if (AbstractSymbolGroupNode *child = m_node->childByIName(name))
return SymbolGroupValue(child, m_context); if (SymbolGroupNode *n = child->asSymbolGroupNode())
return SymbolGroupValue(n, m_context);
return SymbolGroupValue(); return SymbolGroupValue();
} }