From 30e74df0baedcd3a5e2aa309892e2b98d435b0b0 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 26 Nov 2010 15:51:56 +0100 Subject: [PATCH] Debugger[New CDDB]: Dump strings and simple Qt types. Add infrastructure for simple dumpers in Symbol group. Fix display of class values. --- src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp | 13 +- src/libs/qtcreatorcdbext/symbolgroup.cpp | 272 ++++++++++++--------- src/libs/qtcreatorcdbext/symbolgroup.h | 64 +++-- src/libs/qtcreatorcdbext/symbolgroupvalue.cpp | 188 ++++++++++++-- src/libs/qtcreatorcdbext/symbolgroupvalue.h | 14 +- tests/manual/gdbdebugger/gui/mainwindow.cpp | 29 ++- tests/manual/gdbdebugger/gui/mainwindow.h | 2 + tests/manual/gdbdebugger/gui/mainwindow.ui | 8 +- 8 files changed, 421 insertions(+), 169 deletions(-) diff --git a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp index 53bef0ab7d..32ed01315b 100644 --- a/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp +++ b/src/libs/qtcreatorcdbext/qtcreatorcdbextension.cpp @@ -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) diff --git a/src/libs/qtcreatorcdbext/symbolgroup.cpp b/src/libs/qtcreatorcdbext/symbolgroup.cpp index 5c2a1e69b6..65334b47fc 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroup.cpp @@ -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,66 +473,72 @@ std::string SymbolGroupNode::type() const return SUCCEEDED(hr) ? std::string(buf) : std::string(); } -wchar_t *SymbolGroupNode::getValue(ULONG *obtainedSizeIn /* = 0 */) const +ULONG64 SymbolGroupNode::address() const +{ + ULONG64 address = 0; + const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolOffset(m_index, &address); + if (SUCCEEDED(hr)) + return address; + return 0; +} + +std::wstring SymbolGroupNode::symbolGroupRawValue() 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; + 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 - delete [] buffer; - return 0; - } - if (obtainedSizeIn) - *obtainedSizeIn = obtainedSize; - return buffer; + if (FAILED(hr)) // Whoops, should not happen + buffer[0] = 0; + const std::wstring rc(buffer); + delete [] buffer; + return rc; } -ULONG64 SymbolGroupNode::address() const +std::wstring SymbolGroupNode::symbolGroupFixedValue() const { - ULONG64 address = 0; - const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolOffset(m_index, &address); - if (SUCCEEDED(hr)) - return address; - return 0; + std::wstring value = symbolGroupRawValue(); + fixValue(type(), &value); + return value; } -std::wstring SymbolGroupNode::rawValue() const +// Value to be reported to debugger +std::wstring SymbolGroupNode::displayValue(const SymbolGroupValueContext &ctx) { - std::wstring rc; - if (const wchar_t *wbuf = getValue()) { - rc = wbuf; - delete[] wbuf; - } - return rc; + if (m_flags & Uninitialized) + return L""; + if ((m_flags & DumperMask) == 0) + m_flags |= dumpSimpleType(this , ctx, &m_dumperValue); + if (m_flags & DumperOk) + return m_dumperValue; + return symbolGroupFixedValue(); } -std::wstring SymbolGroupNode::fixedValue() const +SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const { - std::wstring value = rawValue(); - fixValue(type(), &value); - return value; + return i < m_children.size() ? m_children.at(i) : static_cast(0); } -SymbolGroupNode *SymbolGroupNode::childAt(unsigned i) const +unsigned SymbolGroupNode::indexByIName(const char *n) const { - return i < m_children.size() ? m_children.at(i) : static_cast(0); + 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=\"\""; + 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(wbuf), valueSize * sizeof(wchar_t)); - str << '"'; - } - delete [] wbuf; - } + str << ",valueencoded=\"2\",value=\""; + base64Encode(str, reinterpret_cast(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 &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'; } diff --git a/src/libs/qtcreatorcdbext/symbolgroup.h b/src/libs/qtcreatorcdbext/symbolgroup.h index 59d8c47c63..950066bca4 100644 --- a/src/libs/qtcreatorcdbext/symbolgroup.h +++ b/src/libs/qtcreatorcdbext/symbolgroup.h @@ -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 SymbolParameterVector; typedef std::vector 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 diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp index 9e279e5dd2..f89db161f6 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.cpp @@ -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,std::allocator >"; +static const char stdWStringTypeC[] = "class std::basic_string,std::allocator >"; + // Dump a QString. -static bool dumpQString(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; - } +static unsigned dumpQString(const std::string &type, const SymbolGroupValue &v, std::wstring *s) +{ + 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 +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 +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; } diff --git a/src/libs/qtcreatorcdbext/symbolgroupvalue.h b/src/libs/qtcreatorcdbext/symbolgroupvalue.h index bb18ab223c..b581d7d91a 100644 --- a/src/libs/qtcreatorcdbext/symbolgroupvalue.h +++ b/src/libs/qtcreatorcdbext/symbolgroupvalue.h @@ -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 diff --git a/tests/manual/gdbdebugger/gui/mainwindow.cpp b/tests/manual/gdbdebugger/gui/mainwindow.cpp index 1cb112fdc3..727c812185 100644 --- a/tests/manual/gdbdebugger/gui/mainwindow.cpp +++ b/tests/manual/gdbdebugger/gui/mainwindow.cpp @@ -33,9 +33,17 @@ #define _USE_MATH_DEFINES #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include -#include #include #include #include @@ -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 stringVector(1, "bla"); std::vector 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; + +} diff --git a/tests/manual/gdbdebugger/gui/mainwindow.h b/tests/manual/gdbdebugger/gui/mainwindow.h index 617c404434..077cf15047 100644 --- a/tests/manual/gdbdebugger/gui/mainwindow.h +++ b/tests/manual/gdbdebugger/gui/mainwindow.h @@ -71,6 +71,8 @@ private slots: void on_actionStdTypes_triggered(); + void on_actionVariousQtTypes_triggered(); + private: void terminateThread(); int m_w; diff --git a/tests/manual/gdbdebugger/gui/mainwindow.ui b/tests/manual/gdbdebugger/gui/mainwindow.ui index 892b1b092b..639fd6c998 100644 --- a/tests/manual/gdbdebugger/gui/mainwindow.ui +++ b/tests/manual/gdbdebugger/gui/mainwindow.ui @@ -20,7 +20,7 @@ 0 0 600 - 27 + 26 @@ -47,6 +47,7 @@ + @@ -157,6 +158,11 @@ stdTypes + + + variousQtTypes + + -- 2.11.0