forked from qt-creator/qt-creator
Debugger[New CDDB]: Dump strings and simple Qt types.
Add infrastructure for simple dumpers in Symbol group. Fix display of class values.
This commit is contained in:
@@ -204,11 +204,14 @@ static std::string commmandLocals(ExtensionCommandContext &exc,PCSTR args, int *
|
||||
symGroup->expandList(expandedInames, errorMessage);
|
||||
if (!uninitializedInames.empty())
|
||||
symGroup->markUninitialized(uninitializedInames);
|
||||
// Complete dump
|
||||
if (iname.empty())
|
||||
return debugOutput ? symGroup->debug(debugOutput - 1) : symGroup->dump(humanReadableGdbmi);
|
||||
// Look up iname
|
||||
return symGroup->dump(iname, humanReadableGdbmi, errorMessage);
|
||||
|
||||
if (debugOutput)
|
||||
return symGroup->debug(iname, debugOutput - 1);
|
||||
|
||||
const SymbolGroupValueContext dumpContext(exc.dataSpaces());
|
||||
return iname.empty() ?
|
||||
symGroup->dump(dumpContext, humanReadableGdbmi) :
|
||||
symGroup->dump(iname, dumpContext, humanReadableGdbmi, errorMessage);
|
||||
}
|
||||
|
||||
extern "C" HRESULT CALLBACK locals(CIDebugClient *client, PCSTR args)
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
**************************************************************************/
|
||||
|
||||
#include "symbolgroup.h"
|
||||
#include "symbolgroupvalue.h"
|
||||
#include "stringutils.h"
|
||||
#include "base64.h"
|
||||
|
||||
@@ -443,10 +444,18 @@ static void fixValue(const std::string &type, std::wstring *value)
|
||||
value->erase(0, 2);
|
||||
return;
|
||||
}
|
||||
// Fix long class names on std containers 'class std::tree<...>' -> 'class std::tree<>'
|
||||
if (value->compare(0, 6, L"class ") == 0) {
|
||||
const std::string::size_type openTemplate = value->find(L'<');
|
||||
if (openTemplate != std::string::npos) {
|
||||
value->erase(openTemplate + 1, value->size() - openTemplate - 2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for ASCII-encode-able stuff. Plain characters + tabs at the most, no newline.
|
||||
static bool isSevenBitClean(const wchar_t *buf, ULONG size)
|
||||
static bool isSevenBitClean(const wchar_t *buf, size_t size)
|
||||
{
|
||||
const wchar_t *bufEnd = buf + size;
|
||||
for (const wchar_t *bufPtr = buf; bufPtr < bufEnd; bufPtr++) {
|
||||
@@ -464,29 +473,6 @@ std::string SymbolGroupNode::type() const
|
||||
return SUCCEEDED(hr) ? std::string(buf) : std::string();
|
||||
}
|
||||
|
||||
wchar_t *SymbolGroupNode::getValue(ULONG *obtainedSizeIn /* = 0 */) const
|
||||
{
|
||||
// Determine size and return allocated buffer
|
||||
if (obtainedSizeIn)
|
||||
*obtainedSizeIn = 0;
|
||||
const ULONG maxValueSize = 262144;
|
||||
ULONG obtainedSize = 0;
|
||||
HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, NULL, maxValueSize, &obtainedSize);
|
||||
if (FAILED(hr))
|
||||
return 0;
|
||||
if (obtainedSize > maxValueSize)
|
||||
obtainedSize = maxValueSize;
|
||||
wchar_t *buffer = new wchar_t[obtainedSize];
|
||||
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, buffer, obtainedSize, &obtainedSize);
|
||||
if (FAILED(hr)) { // Whoops, should not happen
|
||||
delete [] buffer;
|
||||
return 0;
|
||||
}
|
||||
if (obtainedSizeIn)
|
||||
*obtainedSizeIn = obtainedSize;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ULONG64 SymbolGroupNode::address() const
|
||||
{
|
||||
ULONG64 address = 0;
|
||||
@@ -496,34 +482,63 @@ ULONG64 SymbolGroupNode::address() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::wstring SymbolGroupNode::rawValue() const
|
||||
std::wstring SymbolGroupNode::symbolGroupRawValue() const
|
||||
{
|
||||
std::wstring rc;
|
||||
if (const wchar_t *wbuf = getValue()) {
|
||||
rc = wbuf;
|
||||
delete[] wbuf;
|
||||
}
|
||||
// Determine size and return allocated buffer
|
||||
const ULONG maxValueSize = 262144;
|
||||
ULONG obtainedSize = 0;
|
||||
HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, NULL, maxValueSize, &obtainedSize);
|
||||
if (FAILED(hr))
|
||||
return std::wstring();
|
||||
if (obtainedSize > maxValueSize)
|
||||
obtainedSize = maxValueSize;
|
||||
wchar_t *buffer = new wchar_t[obtainedSize];
|
||||
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolValueTextWide(m_index, buffer, obtainedSize, &obtainedSize);
|
||||
if (FAILED(hr)) // Whoops, should not happen
|
||||
buffer[0] = 0;
|
||||
const std::wstring rc(buffer);
|
||||
delete [] buffer;
|
||||
return rc;
|
||||
}
|
||||
|
||||
std::wstring SymbolGroupNode::fixedValue() const
|
||||
std::wstring SymbolGroupNode::symbolGroupFixedValue() const
|
||||
{
|
||||
std::wstring value = rawValue();
|
||||
std::wstring value = symbolGroupRawValue();
|
||||
fixValue(type(), &value);
|
||||
return value;
|
||||
}
|
||||
|
||||
// Value to be reported to debugger
|
||||
std::wstring SymbolGroupNode::displayValue(const SymbolGroupValueContext &ctx)
|
||||
{
|
||||
if (m_flags & Uninitialized)
|
||||
return L"<not in scope>";
|
||||
if ((m_flags & DumperMask) == 0)
|
||||
m_flags |= dumpSimpleType(this , ctx, &m_dumperValue);
|
||||
if (m_flags & DumperOk)
|
||||
return m_dumperValue;
|
||||
return symbolGroupFixedValue();
|
||||
}
|
||||
|
||||
SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const
|
||||
{
|
||||
return i < m_children.size() ? m_children.at(i) : static_cast<SymbolGroupNode *>(0);
|
||||
}
|
||||
|
||||
unsigned SymbolGroupNode::indexByIName(const char *n) const
|
||||
{
|
||||
const VectorIndexType size = m_children.size();
|
||||
for (VectorIndexType i = 0; i < size; i++)
|
||||
if ( m_children.at(i)->iName() == n )
|
||||
return unsigned(i);
|
||||
return unsigned(-1);
|
||||
}
|
||||
|
||||
SymbolGroupNode *SymbolGroupNode::childByIName(const char *n) const
|
||||
{
|
||||
const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_children.end();
|
||||
for (SymbolGroupNodePtrVector::const_iterator it = m_children.begin(); it != childrenEnd; ++it)
|
||||
if ( (*it)->iName() == n )
|
||||
return *it;
|
||||
const unsigned index = indexByIName(n);
|
||||
if (index != unsigned(-1))
|
||||
return m_children.at(index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -533,51 +548,32 @@ static inline void indentStream(std::ostream &str, unsigned depth)
|
||||
str << " ";
|
||||
}
|
||||
|
||||
void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth,
|
||||
bool humanReadable) const
|
||||
void SymbolGroupNode::dump(std::ostream &str, const SymbolGroupValueContext &ctx)
|
||||
{
|
||||
const std::string iname = fullIName();
|
||||
const std::string t = type();
|
||||
|
||||
if (child) { // Separate list of children
|
||||
str << ',';
|
||||
if (humanReadable)
|
||||
str << '\n';
|
||||
}
|
||||
|
||||
if (humanReadable)
|
||||
indentStream(str, depth);
|
||||
str << "{iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name
|
||||
str << "iname=\"" << iname << "\",exp=\"" << iname << "\",name=\"" << m_name
|
||||
<< "\",type=\"" << t << '"';
|
||||
|
||||
if (const ULONG64 addr = address())
|
||||
str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec
|
||||
<< '"';
|
||||
|
||||
bool valueEditable = true;
|
||||
bool valueEnabled = true;
|
||||
|
||||
const bool uninitialized = m_flags & Uninitialized;
|
||||
if (uninitialized) {
|
||||
valueEditable = valueEnabled = false;
|
||||
str << ",valueencoded=\"0\",value=\"<not in scope>\"";
|
||||
bool valueEditable = !uninitialized;
|
||||
bool valueEnabled = !uninitialized;
|
||||
|
||||
const std::wstring value = displayValue(ctx);
|
||||
// ASCII or base64?
|
||||
if (isSevenBitClean(value.c_str(), value.size())) {
|
||||
str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"';
|
||||
} else {
|
||||
ULONG obtainedSize = 0;
|
||||
if (const wchar_t *wbuf = getValue(&obtainedSize)) {
|
||||
const ULONG valueSize = obtainedSize - 1;
|
||||
// ASCII or base64?
|
||||
if (isSevenBitClean(wbuf, valueSize)) {
|
||||
std::wstring value = wbuf;
|
||||
fixValue(t, &value);
|
||||
str << ",valueencoded=\"0\",value=\"" << gdbmiWStringFormat(value) << '"';
|
||||
} else {
|
||||
str << ",valueencoded=\"2\",value=\"";
|
||||
base64Encode(str, reinterpret_cast<const unsigned char *>(wbuf), valueSize * sizeof(wchar_t));
|
||||
str << '"';
|
||||
}
|
||||
delete [] wbuf;
|
||||
}
|
||||
str << ",valueencoded=\"2\",value=\"";
|
||||
base64Encode(str, reinterpret_cast<const unsigned char *>(value.c_str()), value.size() * sizeof(wchar_t));
|
||||
str << '"';
|
||||
}
|
||||
|
||||
// Children: Dump all known or subelements (guess).
|
||||
const VectorIndexType childCountGuess = uninitialized ? 0 :
|
||||
(m_children.empty() ? m_parameters.SubElements : m_children.size());
|
||||
@@ -587,39 +583,32 @@ void SymbolGroupNode::dump(std::ostream &str, unsigned child, unsigned depth,
|
||||
str << ",valueenabled=\"" << (valueEnabled ? "true" : "false") << '"'
|
||||
<< ",valueeditable=\"" << (valueEditable ? "true" : "false") << '"'
|
||||
<< ",numchild=\"" << childCountGuess << '"';
|
||||
if (!uninitialized && !m_children.empty()) {
|
||||
str << ",children=[";
|
||||
if (humanReadable)
|
||||
str << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolGroupNode::dumpChildrenVisited(std::ostream &str, bool humanReadable) const
|
||||
{
|
||||
if (!m_children.empty())
|
||||
str << ']';
|
||||
str << '}';
|
||||
if (humanReadable)
|
||||
str << '\n';
|
||||
|
||||
}
|
||||
bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth) const
|
||||
bool SymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth)
|
||||
{
|
||||
// If we happen to be the root node, just skip over
|
||||
const bool invisibleRoot = m_index == DEBUG_ANY_ID;
|
||||
const unsigned childDepth = invisibleRoot ? 0 : depth + 1;
|
||||
|
||||
if (!invisibleRoot) { // Visit us and move index forward.
|
||||
if (visitor.visit(this, child, depth))
|
||||
return true;
|
||||
const SymbolGroupNodeVisitor::VisitResult vr =
|
||||
invisibleRoot ? SymbolGroupNodeVisitor::VisitContinue :
|
||||
visitor.visit(this, child, depth);
|
||||
switch (vr) {
|
||||
case SymbolGroupNodeVisitor::VisitStop:
|
||||
return true;
|
||||
case SymbolGroupNodeVisitor::VisitSkipChildren:
|
||||
break;
|
||||
case SymbolGroupNodeVisitor::VisitContinue: {
|
||||
const unsigned childCount = unsigned(m_children.size());
|
||||
for (unsigned c = 0; c < childCount; c++)
|
||||
if (m_children.at(c)->accept(visitor, c, childDepth))
|
||||
return true;
|
||||
if (!invisibleRoot)
|
||||
visitor.childrenVisited(this, depth);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const unsigned childCount = unsigned(m_children.size());
|
||||
for (unsigned c = 0; c < childCount; c++)
|
||||
if (m_children.at(c)->accept(visitor, c, childDepth))
|
||||
return true;
|
||||
if (!invisibleRoot)
|
||||
visitor.childrenVisited(this, depth);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -630,13 +619,25 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
|
||||
if (const VectorIndexType childCount = m_children.size())
|
||||
str << ", Children=" << childCount;
|
||||
str << ' ' << m_parameters;
|
||||
if (m_flags)
|
||||
if (m_flags) {
|
||||
str << " node-flags=" << m_flags;
|
||||
if (m_flags & Uninitialized)
|
||||
str << " UNINITIALIZED";
|
||||
if (m_flags & DumperNotApplicable)
|
||||
str << " DumperNotApplicable";
|
||||
if (m_flags & DumperOk)
|
||||
str << " DumperOk";
|
||||
if (m_flags & DumperFailed)
|
||||
str << " DumperFailed";
|
||||
if (m_flags & ExpandedByDumper)
|
||||
str << " ExpandedByDumper";
|
||||
str << ' ';
|
||||
}
|
||||
if (verbosity) {
|
||||
str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec
|
||||
<< " Type=\"" << type() << '"';
|
||||
if (!(m_flags & Uninitialized))
|
||||
str << "\" Value=\"" << gdbmiWStringFormat(rawValue()) << '"';
|
||||
str << "\" Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"';
|
||||
}
|
||||
str << '\n';
|
||||
}
|
||||
@@ -646,9 +647,12 @@ bool SymbolGroupNode::expand(std::string *errorMessage)
|
||||
{
|
||||
if (::debug > 1)
|
||||
DebugPrint() << "SymbolGroupNode::expand " << m_name << ' ' << m_index;
|
||||
if (!m_children.empty())
|
||||
if (isExpanded()) {
|
||||
// Clear the flag indication dumper expansion on a second, explicit request
|
||||
clearFlags(ExpandedByDumper);
|
||||
return true;
|
||||
if (m_parameters.SubElements == 0) {
|
||||
}
|
||||
if (!canExpand()) {
|
||||
*errorMessage = "No subelements to expand in node: " + fullIName();
|
||||
return false;
|
||||
}
|
||||
@@ -684,10 +688,10 @@ static inline std::string msgNotFound(const std::string &nodeName)
|
||||
return str.str();
|
||||
}
|
||||
|
||||
std::string SymbolGroup::dump(bool humanReadable) const
|
||||
std::string SymbolGroup::dump(const SymbolGroupValueContext &ctx, bool humanReadable) const
|
||||
{
|
||||
std::ostringstream str;
|
||||
DumpSymbolGroupNodeVisitor visitor(str, humanReadable);
|
||||
DumpSymbolGroupNodeVisitor visitor(str, ctx, humanReadable);
|
||||
if (humanReadable)
|
||||
str << '\n';
|
||||
str << '[';
|
||||
@@ -697,33 +701,43 @@ std::string SymbolGroup::dump(bool humanReadable) const
|
||||
}
|
||||
|
||||
// Dump a node, potentially expand
|
||||
std::string SymbolGroup::dump(const std::string &name, bool humanReadable, std::string *errorMessage)
|
||||
std::string SymbolGroup::dump(const std::string &iname, const SymbolGroupValueContext &ctx, bool humanReadable, std::string *errorMessage)
|
||||
{
|
||||
SymbolGroupNode *const node = find(name);
|
||||
SymbolGroupNode *const node = find(iname);
|
||||
if (node == 0) {
|
||||
*errorMessage = msgNotFound(name);
|
||||
*errorMessage = msgNotFound(iname);
|
||||
return std::string();
|
||||
}
|
||||
if (node->subElements() && node->children().empty()) {
|
||||
if (!expand(name, errorMessage))
|
||||
if (node->isExpanded()) { // Mark expand request by watch model
|
||||
node->clearFlags(SymbolGroupNode::ExpandedByDumper);
|
||||
} else {
|
||||
if (node->canExpand() && !node->expand(errorMessage))
|
||||
return false;
|
||||
}
|
||||
std::ostringstream str;
|
||||
if (humanReadable)
|
||||
str << '\n';
|
||||
DumpSymbolGroupNodeVisitor visitor(str, humanReadable);
|
||||
DumpSymbolGroupNodeVisitor visitor(str, ctx, humanReadable);
|
||||
str << '[';
|
||||
node->accept(visitor, 0, 0);
|
||||
str << ']';
|
||||
return str.str();
|
||||
}
|
||||
|
||||
std::string SymbolGroup::debug(unsigned verbosity) const
|
||||
std::string SymbolGroup::debug(const std::string &iname, unsigned verbosity) const
|
||||
{
|
||||
std::ostringstream str;
|
||||
str << '\n';
|
||||
DebugSymbolGroupNodeVisitor visitor(str, verbosity);
|
||||
accept(visitor);
|
||||
if (iname.empty()) {
|
||||
accept(visitor);
|
||||
} else {
|
||||
if (SymbolGroupNode *const node = find(iname)) {
|
||||
node->accept(visitor, 0, 0);
|
||||
} else {
|
||||
str << msgNotFound(iname);
|
||||
}
|
||||
}
|
||||
return str.str();
|
||||
}
|
||||
|
||||
@@ -807,7 +821,7 @@ void SymbolGroup::markUninitialized(const std::vector<std::string> &uniniNodes)
|
||||
const SymbolGroupNodePtrVector::const_iterator childrenEnd = m_root->children().end();
|
||||
for (SymbolGroupNodePtrVector::const_iterator it = m_root->children().begin(); it != childrenEnd; ++it) {
|
||||
if (std::find(unIniNodesBegin, unIniNodesEnd, (*it)->fullIName()) != unIniNodesEnd)
|
||||
(*it)->setFlags((*it)->flags() | SymbolGroupNode::Uninitialized);
|
||||
(*it)->addFlags(SymbolGroupNode::Uninitialized);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -907,27 +921,53 @@ DebugSymbolGroupNodeVisitor::DebugSymbolGroupNodeVisitor(std::ostream &os, unsig
|
||||
{
|
||||
}
|
||||
|
||||
bool DebugSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node,
|
||||
unsigned /* child */, unsigned depth)
|
||||
SymbolGroupNodeVisitor::VisitResult
|
||||
DebugSymbolGroupNodeVisitor::visit(SymbolGroupNode *node,
|
||||
unsigned /* child */, unsigned depth)
|
||||
{
|
||||
node->debug(m_os, m_verbosity, depth);
|
||||
return false;
|
||||
return VisitContinue;
|
||||
}
|
||||
|
||||
// --------------------- DumpSymbolGroupNodeVisitor
|
||||
DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
|
||||
const SymbolGroupValueContext &context,
|
||||
bool humanReadable) :
|
||||
m_os(os), m_humanReadable(humanReadable)
|
||||
m_os(os), m_humanReadable(humanReadable),m_context(context), m_visitChildren(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool DumpSymbolGroupNodeVisitor::visit(const SymbolGroupNode *node, unsigned child, unsigned depth)
|
||||
SymbolGroupNodeVisitor::VisitResult
|
||||
DumpSymbolGroupNodeVisitor::visit(SymbolGroupNode *node, unsigned child, unsigned depth)
|
||||
{
|
||||
node->dump(m_os, child, depth, m_humanReadable);
|
||||
return false;
|
||||
// Recurse to children if expanded by explicit watchmodel request
|
||||
// and initialized.
|
||||
const unsigned flags = node->flags();
|
||||
m_visitChildren = node->isExpanded()
|
||||
&& (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0;
|
||||
|
||||
// Do not recurse into children unless the node was expanded by the watch model
|
||||
if (child)
|
||||
m_os << ','; // Separator in parents list
|
||||
if (m_humanReadable) {
|
||||
m_os << '\n';
|
||||
indentStream(m_os, depth * 2);
|
||||
}
|
||||
m_os << '{';
|
||||
node->dump(m_os, m_context);
|
||||
if (m_visitChildren) { // open children array
|
||||
m_os << ",children=[";
|
||||
} else { // No children, close array.
|
||||
m_os << '}';
|
||||
}
|
||||
if (m_humanReadable)
|
||||
m_os << '\n';
|
||||
return m_visitChildren ? VisitContinue : VisitSkipChildren;
|
||||
}
|
||||
|
||||
void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *node, unsigned)
|
||||
void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *, unsigned)
|
||||
{
|
||||
node->dumpChildrenVisited(m_os, m_humanReadable);
|
||||
m_os << "]}"; // Close children array and self
|
||||
if (m_humanReadable)
|
||||
m_os << '\n';
|
||||
}
|
||||
|
||||
@@ -40,14 +40,27 @@ std::ostream &operator<<(std::ostream &, const DEBUG_SYMBOL_PARAMETERS&p);
|
||||
|
||||
class SymbolGroupNodeVisitor;
|
||||
class SymbolGroup;
|
||||
struct SymbolGroupValueContext;
|
||||
|
||||
// Thin wrapper around a symbol group entry. Provides accessors for fixed-up
|
||||
// symbol group value and a dumping facility triggered by dump()/displayValue()
|
||||
// calling dumpSimpleType() based on SymbolGroupValue expressions. These values
|
||||
// values should be displayed, still allowing for expansion of the structure
|
||||
// in the debugger. Evaluating the 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.
|
||||
|
||||
// Thin wrapper around a symbol group entry.
|
||||
class SymbolGroupNode {
|
||||
SymbolGroupNode(const SymbolGroupNode&);
|
||||
SymbolGroupNode& operator=(const SymbolGroupNode&);
|
||||
public:
|
||||
enum Flags {
|
||||
Uninitialized = 0x1
|
||||
Uninitialized = 0x1,
|
||||
DumperNotApplicable = 0x2, // No dumper available for type
|
||||
DumperOk = 0x4, // Internal dumper ran, value set
|
||||
DumperFailed = 0x8, // Internal dumper failed
|
||||
DumperMask = DumperNotApplicable|DumperOk|DumperFailed,
|
||||
ExpandedByDumper = 0x10
|
||||
};
|
||||
typedef std::vector<DEBUG_SYMBOL_PARAMETERS> SymbolParameterVector;
|
||||
typedef std::vector<SymbolGroupNode *> SymbolGroupNodePtrVector;
|
||||
@@ -75,35 +88,39 @@ public:
|
||||
|
||||
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; }
|
||||
|
||||
// I/O: Gdbmi dump for Visitors
|
||||
void dump(std::ostream &str, unsigned child, unsigned depth,
|
||||
bool humanReadable) const;
|
||||
void dumpChildrenVisited(std::ostream &str, bool humanReadable) const;
|
||||
void dump(std::ostream &str, const SymbolGroupValueContext &ctx);
|
||||
// I/O: debug for Visitors
|
||||
void debug(std::ostream &os, unsigned verbosity, unsigned depth) const;
|
||||
|
||||
std::wstring rawValue() const;
|
||||
std::wstring fixedValue() const;
|
||||
std::wstring symbolGroupRawValue() const;
|
||||
std::wstring symbolGroupFixedValue() const;
|
||||
std::wstring displayValue(const SymbolGroupValueContext &ctx);
|
||||
|
||||
std::string type() const;
|
||||
ULONG64 address() const;
|
||||
|
||||
bool accept(SymbolGroupNodeVisitor &visitor, unsigned child, unsigned depth) 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; }
|
||||
|
||||
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:
|
||||
// Return allocated wide string array of value
|
||||
wchar_t *getValue(ULONG *obtainedSize = 0) const;
|
||||
bool isArrayElement() const;
|
||||
// Notify about expansion of a node, shift indexes
|
||||
bool notifyExpanded(ULONG index, ULONG insertedCount);
|
||||
@@ -116,6 +133,7 @@ private:
|
||||
const std::string m_name;
|
||||
const std::string m_iname;
|
||||
unsigned m_flags;
|
||||
std::wstring m_dumperValue;
|
||||
};
|
||||
|
||||
/* Visitor that takes care of iterating over the nodes
|
||||
@@ -133,8 +151,15 @@ protected:
|
||||
public:
|
||||
virtual ~SymbolGroupNodeVisitor() {}
|
||||
|
||||
protected:
|
||||
enum VisitResult {
|
||||
VisitContinue,
|
||||
VisitSkipChildren,
|
||||
VisitStop
|
||||
};
|
||||
|
||||
private:
|
||||
virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth) = 0;
|
||||
virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth) = 0;
|
||||
// Helper for formatting output.
|
||||
virtual void childrenVisited(const SymbolGroupNode * /* node */, unsigned /* depth */) {}
|
||||
};
|
||||
@@ -167,10 +192,11 @@ public:
|
||||
~SymbolGroup();
|
||||
|
||||
// Dump all
|
||||
std::string dump(bool humanReadable = false) const;
|
||||
std::string dump(const SymbolGroupValueContext &ctx, bool humanReadable = false) const;
|
||||
// Expand node and dump
|
||||
std::string dump(const std::string &name, bool humanReadable, std::string *errorMessage);
|
||||
std::string debug(unsigned verbosity = 0) const;
|
||||
std::string dump(const std::string &iname, const SymbolGroupValueContext &ctx,
|
||||
bool humanReadable, std::string *errorMessage);
|
||||
std::string debug(const std::string &iname = std::string(), unsigned verbosity = 0) const;
|
||||
|
||||
unsigned frame() const { return m_frame; }
|
||||
ULONG threadId() const { return m_threadId; }
|
||||
@@ -221,7 +247,7 @@ public:
|
||||
explicit DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity = 0);
|
||||
|
||||
private:
|
||||
virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth);
|
||||
virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth);
|
||||
|
||||
std::ostream &m_os;
|
||||
const unsigned m_verbosity;
|
||||
@@ -230,14 +256,18 @@ private:
|
||||
// Gdbmi dump output visitor.
|
||||
class DumpSymbolGroupNodeVisitor : public SymbolGroupNodeVisitor {
|
||||
public:
|
||||
explicit DumpSymbolGroupNodeVisitor(std::ostream &os, bool humanReadable);
|
||||
explicit DumpSymbolGroupNodeVisitor(std::ostream &os,
|
||||
const SymbolGroupValueContext &context,
|
||||
bool humanReadable);
|
||||
|
||||
private:
|
||||
virtual bool visit(const SymbolGroupNode *node, unsigned child, unsigned depth);
|
||||
virtual VisitResult visit(SymbolGroupNode *node, unsigned child, unsigned depth);
|
||||
virtual void childrenVisited(const SymbolGroupNode * node, unsigned depth);
|
||||
|
||||
std::ostream &m_os;
|
||||
const bool m_humanReadable;
|
||||
const SymbolGroupValueContext &m_context;
|
||||
bool m_visitChildren;
|
||||
};
|
||||
|
||||
#endif // SYMBOLGROUP_H
|
||||
|
||||
@@ -31,15 +31,6 @@
|
||||
#include "symbolgroup.h"
|
||||
#include "stringutils.h"
|
||||
|
||||
SymbolGroupValueContext::SymbolGroupValueContext(CIDebugDataSpaces *ds)
|
||||
: dataspaces(ds)
|
||||
{
|
||||
}
|
||||
|
||||
SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0)
|
||||
{
|
||||
}
|
||||
|
||||
SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node,
|
||||
const SymbolGroupValueContext &ctx) :
|
||||
m_node(node), m_context(ctx)
|
||||
@@ -56,9 +47,34 @@ bool SymbolGroupValue::isValid() const
|
||||
return m_node != 0 && m_context.dataspaces != 0;
|
||||
}
|
||||
|
||||
SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const
|
||||
{
|
||||
if (ensureExpanded())
|
||||
if (index < m_node->children().size())
|
||||
return SymbolGroupValue(m_node->children().at(index), m_context);
|
||||
return SymbolGroupValue();
|
||||
}
|
||||
|
||||
bool SymbolGroupValue::ensureExpanded() const
|
||||
{
|
||||
if (!isValid() || !m_node->canExpand())
|
||||
return false;
|
||||
|
||||
if (m_node->isExpanded())
|
||||
return true;
|
||||
|
||||
// Set a flag indicating the node was expanded by SymbolGroupValue
|
||||
// and not by an explicit request from the watch model.
|
||||
if (m_node->expand(&m_errorMessage)) {
|
||||
m_node->addFlags(SymbolGroupNode::ExpandedByDumper);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SymbolGroupValue SymbolGroupValue::operator[](const char *name) const
|
||||
{
|
||||
if (isValid() && m_node->expand(&m_errorMessage))
|
||||
if (ensureExpanded())
|
||||
if (SymbolGroupNode *child = m_node->childByIName(name))
|
||||
return SymbolGroupValue(child, m_context);
|
||||
return SymbolGroupValue();
|
||||
@@ -71,7 +87,19 @@ std::string SymbolGroupValue::type() const
|
||||
|
||||
std::wstring SymbolGroupValue::value() const
|
||||
{
|
||||
return isValid() ? m_node->fixedValue() : std::wstring();
|
||||
return isValid() ? m_node->symbolGroupFixedValue() : std::wstring();
|
||||
}
|
||||
|
||||
double SymbolGroupValue::floatValue(double defaultValue) const
|
||||
{
|
||||
double f = defaultValue;
|
||||
if (isValid()) {
|
||||
std::wistringstream str(value());
|
||||
str >> f;
|
||||
if (str.fail())
|
||||
f = defaultValue;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
int SymbolGroupValue::intValue(int defaultValue) const
|
||||
@@ -136,26 +164,138 @@ std::string SymbolGroupValue::error() const
|
||||
return m_errorMessage;
|
||||
}
|
||||
|
||||
static const char stdStringTypeC[] = "class std::basic_string<char,std::char_traits<char>,std::allocator<char> >";
|
||||
static const char stdWStringTypeC[] = "class std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >";
|
||||
|
||||
// Dump a QString.
|
||||
static bool dumpQString(const SymbolGroupValue &v, std::wstring *s)
|
||||
static unsigned dumpQString(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
|
||||
{
|
||||
if (endsWith(v.type(), "QString")) {
|
||||
if (SymbolGroupValue d = v["d"]) {
|
||||
if (SymbolGroupValue sizeValue = d["size"]) {
|
||||
const int size = sizeValue.intValue();
|
||||
if (size >= 0) {
|
||||
*s = d["data"].wcharPointerData(size);
|
||||
return true;
|
||||
}
|
||||
if (!endsWith(type, "QString")) // namespaced Qt?
|
||||
return SymbolGroupNode::DumperNotApplicable;
|
||||
|
||||
if (SymbolGroupValue d = v["d"]) {
|
||||
if (SymbolGroupValue sizeValue = d["size"]) {
|
||||
const int size = sizeValue.intValue();
|
||||
if (size >= 0) {
|
||||
*s = d["data"].wcharPointerData(size);
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return SymbolGroupNode::DumperFailed;
|
||||
}
|
||||
|
||||
// Dump a QByteArray
|
||||
static unsigned dumpQByteArray(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
|
||||
{
|
||||
if (!endsWith(type, "QByteArray")) // namespaced Qt?
|
||||
return SymbolGroupNode::DumperNotApplicable;
|
||||
// TODO: More sophisticated dumping of binary data?
|
||||
if (SymbolGroupValue data = v["d"]["data"]) {
|
||||
*s = data.value();
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
return SymbolGroupNode::DumperFailed;
|
||||
}
|
||||
|
||||
// Dump a rectangle in X11 syntax
|
||||
template <class T>
|
||||
inline void dumpRect(std::wostringstream &str, T x, T y, T width, T height)
|
||||
{
|
||||
str << width << 'x' << height;
|
||||
if (x >= 0)
|
||||
str << '+';
|
||||
str << x;
|
||||
if (y >= 0)
|
||||
str << '+';
|
||||
str << y;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void dumpRectPoints(std::wostringstream &str, T x1, T y1, T x2, T y2)
|
||||
{
|
||||
dumpRect(str, x1, y1, (x2 - x1), (y2 - y1));
|
||||
}
|
||||
|
||||
// Dump Qt's simple geometrical types
|
||||
static unsigned dumpQtGeometryTypes(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
|
||||
{
|
||||
if (endsWith(type, "QSize") || endsWith(type, "QSizeF")) { // namespaced Qt?
|
||||
std::wostringstream str;
|
||||
str << '(' << v["wd"].value() << ", " << v["ht"].value() << ')';
|
||||
*s = str.str();
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
if (endsWith(type, "QPoint") || endsWith(type, "QPointF")) { // namespaced Qt?
|
||||
std::wostringstream str;
|
||||
str << '(' << v["xp"].value() << ", " << v["yp"].value() << ')';
|
||||
*s = str.str();
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
if (endsWith(type, "QLine") || endsWith(type, "QLineF")) { // namespaced Qt?
|
||||
const SymbolGroupValue p1 = v["pt1"];
|
||||
const SymbolGroupValue p2 = v["pt2"];
|
||||
if (p1 && p2) {
|
||||
std::wostringstream str;
|
||||
str << '(' << p1["xp"].value() << ", " << p1["yp"].value() << ") ("
|
||||
<< p2["xp"].value() << ", " << p2["yp"].value() << ')';
|
||||
*s = str.str();
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
return SymbolGroupNode::DumperFailed;
|
||||
}
|
||||
if (endsWith(type, "QRect")) {
|
||||
std::wostringstream str;
|
||||
dumpRectPoints(str, v["x1"].intValue(), v["y1"].intValue(), v["x2"].intValue(), v["y2"].intValue());
|
||||
*s = str.str();
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
if (endsWith(type, "QRectF")) {
|
||||
std::wostringstream str;
|
||||
dumpRect(str, v["xp"].floatValue(), v["yp"].floatValue(), v["w"].floatValue(), v["h"].floatValue());
|
||||
*s = str.str();
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
return SymbolGroupNode::DumperNotApplicable;
|
||||
}
|
||||
|
||||
// Dump a std::string.
|
||||
static unsigned dumpStdString(const std::string &type, const SymbolGroupValue &v, std::wstring *s)
|
||||
{
|
||||
if (type != stdStringTypeC && type != stdWStringTypeC)
|
||||
return SymbolGroupNode::DumperNotApplicable;
|
||||
// MSVC 2010: Access Bx/_Buf in base class
|
||||
SymbolGroupValue buf = v[unsigned(0)]["_Bx"]["_Buf"];
|
||||
if (!buf) // MSVC2008: Bx/Buf are members
|
||||
buf = v["_Bx"]["_Buf"];
|
||||
if (buf) {
|
||||
*s = buf.value();
|
||||
return SymbolGroupNode::DumperOk;
|
||||
}
|
||||
return SymbolGroupNode::DumperFailed;
|
||||
}
|
||||
|
||||
// Dump builtin simple types using SymbolGroupValue expressions.
|
||||
bool dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s)
|
||||
unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s)
|
||||
{
|
||||
// Check for class types and strip pointer types (references appear as pointers as well)
|
||||
std::string type = n->type();
|
||||
if (type.compare(0, 6, "class ") != 0)
|
||||
return SymbolGroupNode::DumperNotApplicable;
|
||||
if (endsWith(type, " *"))
|
||||
type.erase(type.size() - 2, 2);
|
||||
const SymbolGroupValue v(n, ctx);
|
||||
return dumpQString(v, s);
|
||||
unsigned rc = dumpQString(type, v, s);
|
||||
if (rc != SymbolGroupNode::DumperNotApplicable)
|
||||
return rc;
|
||||
rc = dumpQByteArray(type, v, s);
|
||||
if (rc != SymbolGroupNode::DumperNotApplicable)
|
||||
return rc;
|
||||
rc = dumpStdString(type, v, s);
|
||||
if (rc != SymbolGroupNode::DumperNotApplicable)
|
||||
return rc;
|
||||
rc = dumpQtGeometryTypes(type, v, s);
|
||||
if (rc != SymbolGroupNode::DumperNotApplicable)
|
||||
return rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -39,8 +39,8 @@ class SymbolGroupNode;
|
||||
// Structure to pass all IDebug interfaces used for SymbolGroupValue
|
||||
struct SymbolGroupValueContext
|
||||
{
|
||||
SymbolGroupValueContext(CIDebugDataSpaces *ds);
|
||||
SymbolGroupValueContext();
|
||||
SymbolGroupValueContext(CIDebugDataSpaces *ds) : dataspaces(ds) {}
|
||||
SymbolGroupValueContext::SymbolGroupValueContext() : dataspaces(0) {}
|
||||
|
||||
CIDebugDataSpaces *dataspaces;
|
||||
};
|
||||
@@ -58,11 +58,14 @@ public:
|
||||
operator bool() const { return isValid(); }
|
||||
bool isValid() const;
|
||||
|
||||
// Access children by name or index (0-based)
|
||||
SymbolGroupValue operator[](const char *name) const;
|
||||
SymbolGroupValue operator[](unsigned) const;
|
||||
|
||||
std::string type() const;
|
||||
std::wstring value() const;
|
||||
int intValue(int defaultValue = -1) const;
|
||||
double floatValue(double defaultValue = -999) const;
|
||||
ULONG64 pointerValue(ULONG64 defaultValue = 0) const;
|
||||
// Return allocated array of data pointed to
|
||||
unsigned char *pointerData(unsigned length) const;
|
||||
@@ -72,12 +75,15 @@ public:
|
||||
std::string error() const;
|
||||
|
||||
private:
|
||||
bool ensureExpanded() const;
|
||||
|
||||
SymbolGroupNode *m_node;
|
||||
SymbolGroupValueContext m_context;
|
||||
mutable std::string m_errorMessage;
|
||||
};
|
||||
|
||||
// Dump builtin simple types using SymbolGroupValue expressions.
|
||||
bool dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s);
|
||||
// Dump builtin simple types using SymbolGroupValue expressions,
|
||||
// returning SymbolGroupNode dumper flags.
|
||||
unsigned dumpSimpleType(SymbolGroupNode *n, const SymbolGroupValueContext &ctx, std::wstring *s);
|
||||
|
||||
#endif // SYMBOLGROUPVALUE_H
|
||||
|
||||
@@ -33,9 +33,17 @@
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <math.h>
|
||||
#include <QDebug>
|
||||
#include <QRect>
|
||||
#include <QRectF>
|
||||
#include <QLine>
|
||||
#include <QLineF>
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QSize>
|
||||
#include <QSizeF>
|
||||
|
||||
#include <QThread>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <QLibrary>
|
||||
#include <QLibraryInfo>
|
||||
@@ -78,7 +86,7 @@ void MainWindow::simpleBP(int inc, const QString &inx)
|
||||
{
|
||||
int array[2] = {1,2};
|
||||
m_w++;
|
||||
QString x = "h\"allo";
|
||||
QString x = QLatin1String("h\344all\366");
|
||||
QString *xp = &x;
|
||||
qDebug() << inc << inx << *xp;
|
||||
Q_UNUSED(array)
|
||||
@@ -379,3 +387,20 @@ void Foo::MainWindow::on_actionStdTypes_triggered()
|
||||
std::vector<std::string> stringVector(1, "bla");
|
||||
std::vector<std::wstring> wStringVector(1, L"bla");
|
||||
}
|
||||
|
||||
void Foo::MainWindow::on_actionVariousQtTypes_triggered()
|
||||
{
|
||||
const QByteArray ba = "hallo\t";
|
||||
QSize size = QSize(42, 43);
|
||||
QSizeF sizeF(size);
|
||||
QPoint p1 = QPoint(42, 43);
|
||||
QPoint p2 = QPoint(100, 100);
|
||||
QLine line(p1, p2);
|
||||
QPointF p1f(p1);
|
||||
QPointF p2f(p2);
|
||||
QLineF linef(p1f, p2f);
|
||||
QRect rect(p1, p2);
|
||||
QRectF rectf(rect);
|
||||
qDebug() << sizeF << linef << rectf;
|
||||
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ private slots:
|
||||
|
||||
void on_actionStdTypes_triggered();
|
||||
|
||||
void on_actionVariousQtTypes_triggered();
|
||||
|
||||
private:
|
||||
void terminateThread();
|
||||
int m_w;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>600</width>
|
||||
<height>27</height>
|
||||
<height>26</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
@@ -47,6 +47,7 @@
|
||||
<addaction name="actionScopes"/>
|
||||
<addaction name="actionLongString"/>
|
||||
<addaction name="actionStdTypes"/>
|
||||
<addaction name="actionVariousQtTypes"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusBar"/>
|
||||
<widget class="QToolBar" name="toolBar">
|
||||
@@ -157,6 +158,11 @@
|
||||
<string>stdTypes</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionVariousQtTypes">
|
||||
<property name="text">
|
||||
<string>variousQtTypes</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<resources/>
|
||||
|
||||
Reference in New Issue
Block a user