Commit d3619e80 authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Debugger[New CDB]: First stab at containers.

Set up infrastructure for having 'fake' children in a symbolgroup
that merely reference others. Print inames correctly, do index
bookkeeping accordingly, adapt visitor.
Prototypically implement complex dumpers for array-type containers.
adding children as additional symbols.
parent ddf360d4
/**************************************************************************
**
** 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.
**
**************************************************************************/
#include "containers.h"
#include "symbolgroupvalue.h"
#include "symbolgroup.h"
#include "stringutils.h"
typedef SymbolGroupNode::SymbolGroupNodePtrVector SymbolGroupNodePtrVector;
/* Helper for array-type containers:
* Add a series of "*(innertype *)0x (address + n * size)" fake child symbols. */
static SymbolGroupNodePtrVector arrayChildList(SymbolGroup *sg, ULONG64 address,
int count, const std::string &innerType)
{
SymbolGroupNodePtrVector rc;
const unsigned innerTypeSize = SymbolGroupValue::sizeOf(innerType.c_str());
if (!innerTypeSize)
return rc;
std::string errorMessage;
rc.reserve(count);
for (int i = 0; i < count; i++, address += innerTypeSize) {
std::ostringstream str;
str << "*(" << innerType << " *)" << std::showbase << std::hex << address;
if (SymbolGroupNode *child = sg->addSymbol(str.str(), toString(i), &errorMessage)) {
rc.push_back(child);
} else {
break;
}
}
return rc;
}
// std::vector<T>
static inline SymbolGroupNodePtrVector
stdVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx)
{
if (count) {
// std::vector<T>: _Myfirst is a pointer of T*. Get address
// element to obtain address.
const SymbolGroupValue vec(n, ctx);
SymbolGroupValue myFirst = vec[unsigned(0)]["_Myfirst"]; // MSVC2010
if (!myFirst)
myFirst = vec["_Myfirst"]; // MSVC2008
if (myFirst)
if (const ULONG64 address = myFirst.pointerValue())
return arrayChildList(n->symbolGroup(), address, count,
SymbolGroupValue::stripPointerType(myFirst.type()));
}
return SymbolGroupNodePtrVector();
}
// QVector<T>
static inline SymbolGroupNodePtrVector
qVectorChildList(SymbolGroupNode *n, int count, const SymbolGroupValueContext &ctx)
{
if (count) {
// QVector<T>: p/array is declared as array of T. Dereference first
// element to obtain address.
const SymbolGroupValue vec(n, ctx);
if (const SymbolGroupValue firstElementV = vec["p"]["array"][unsigned(0)])
if (const ULONG64 arrayAddress = firstElementV.address())
return arrayChildList(n->symbolGroup(), arrayAddress, count,
firstElementV.type());
}
return SymbolGroupNodePtrVector();
}
// QList<> of type array
static inline SymbolGroupNodePtrVector
qListOfArraryTypeChildren(SymbolGroup *sg, const SymbolGroupValue &v, int count)
{
// QList<T>: d/array is declared as array of void *[]. Dereference first
// element to obtain address.
if (count) {
if (const SymbolGroupValue firstElementV = v["d"]["array"][unsigned(0)])
if (const ULONG64 arrayAddress = firstElementV.address()) {
const std::vector<std::string> innerTypes = v.innerTypes();
if (innerTypes.size() == 1)
return arrayChildList(sg, arrayAddress,
count, innerTypes.front());
}
}
return SymbolGroupNodePtrVector();
}
SymbolGroupNodePtrVector containerChildren(SymbolGroupNode *node, int type,
int size, const SymbolGroupValueContext &ctx)
{
if (!size)
return SymbolGroupNodePtrVector();
if (size > 100)
size = 100;
switch (type) {
case KT_QVector:
return qVectorChildList(node, size, ctx);
case KT_StdVector:
return stdVectorChildList(node, size, ctx);
case KT_QList:
// Differentiate between array and list
break;
case KT_QStringList:
if (const SymbolGroupValue qList = SymbolGroupValue(node, ctx)[unsigned(0)])
return qListOfArraryTypeChildren(node->symbolGroup(), qList, size);
break;
}
return SymbolGroupNodePtrVector();
}
/**************************************************************************
**
** 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 CONTAINERS_H
#define CONTAINERS_H
struct SymbolGroupValueContext;
class SymbolGroupNode;
#include "common.h"
#include <vector>
/* Create a list of children of containers. */
std::vector<SymbolGroupNode *> containerChildren(SymbolGroupNode *node,
int type,
int size,
const SymbolGroupValueContext &ctx);
#endif // CONTAINERS_H
......@@ -54,7 +54,8 @@ SOURCES += qtcreatorcdbextension.cpp \
gdbmihelpers.cpp \
outputcallback.cpp \
base64.cpp \
symbolgroupvalue.cpp
symbolgroupvalue.cpp \
containers.cpp
HEADERS += extensioncontext.h \
common.h \
......@@ -65,4 +66,5 @@ HEADERS += extensioncontext.h \
gdbmihelpers.h \
outputcallback.h \
base64.h \
symbolgroupvalue.h
symbolgroupvalue.h \
containers.h
......@@ -31,6 +31,7 @@
#include "symbolgroupvalue.h"
#include "stringutils.h"
#include "base64.h"
#include "containers.h"
#include <algorithm>
#include <sstream>
......@@ -383,8 +384,10 @@ SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup,
const std::string &name,
const std::string &iname,
SymbolGroupNode *parent) :
m_symbolGroup(symbolGroup), m_parent(parent), m_index(index),
m_name(name), m_iname(iname), m_flags(0)
m_symbolGroup(symbolGroup), m_parent(parent),
m_index(index), m_referencedBy(0),
m_name(name), m_iname(iname), m_flags(0), m_dumperType(-1),
m_dumperContainerSize(-1)
{
memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS));
m_parameters.ParentSymbol = DEBUG_ANY_ID;
......@@ -394,12 +397,23 @@ void SymbolGroupNode::removeChildren()
{
if (!m_children.empty()) {
const SymbolGroupNodePtrVectorIterator end = m_children.end();
for (SymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it)
delete *it;
for (SymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it) {
SymbolGroupNode *child = *it;
if (child->parent() == this) // Do not delete references
delete child;
}
m_children.clear();
}
}
void SymbolGroupNode::setReferencedBy(SymbolGroupNode *n)
{
if (m_referencedBy)
dprintf("Internal error: Node %s Clearing reference by %s",
name().c_str(), m_referencedBy->name().c_str());
m_referencedBy = n;
}
bool SymbolGroupNode::isArrayElement() const
{
return m_parent && (m_parent->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY);
......@@ -415,9 +429,12 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount)
// Looping backwards over the children. If a subtree has no modifications,
// (meaning all other indexes are smaller) we can stop.
const ReverseIt rend = m_children.rend();
for (ReverseIt it = m_children.rbegin(); it != rend; ++it)
for (ReverseIt it = m_children.rbegin(); it != rend; ++it) {
SymbolGroupNode *c = *it;
if (c->parent() == this) // Skip fake children that are referenced only
if (!(*it)->notifyExpanded(index, insertedCount))
return false;
}
// Correct our own + parent index if applicable.
if (m_index == DEBUG_ANY_ID || m_index < index)
......@@ -433,7 +450,7 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount)
std::string SymbolGroupNode::fullIName() const
{
std::string rc = iName();
for (const SymbolGroupNode *p = parent(); p; p = p->parent()) {
for (const SymbolGroupNode *p = referencedParent(); p; p = p->referencedParent()) {
rc.insert(0, 1, '.');
rc.insert(0, p->iName());
}
......@@ -706,15 +723,45 @@ std::wstring SymbolGroupNode::symbolGroupFixedValue() const
return value;
}
// Value to be reported to debugger
std::wstring SymbolGroupNode::displayValue(const SymbolGroupValueContext &ctx)
// Complex dumpers: Get container/fake children
void SymbolGroupNode::runComplexDumpers(const SymbolGroupValueContext &ctx)
{
if (m_dumperContainerSize <= 0 || (m_flags & ComplexDumperOk) || !(m_flags & SimpleDumperOk))
return;
m_flags |= ComplexDumperOk;
const SymbolGroupNodePtrVector children =
containerChildren(this, m_dumperType, m_dumperContainerSize, ctx);
m_dumperContainerSize = int(children.size()); // Just in case...
if (children.empty())
return;
clearFlags(ExpandedByDumper);
// Mark current children as obscured. We cannot show both currently
// as this would upset the numerical sorting of the watch model
SymbolGroupNodePtrVectorConstIterator cend = m_children.end();
for (SymbolGroupNodePtrVectorConstIterator it = m_children.begin(); it != cend; ++it)
(*it)->addFlags(Obscured);
// Add children and mark them as referenced by us.
cend = children.end();
for (SymbolGroupNodePtrVectorConstIterator it = children.begin(); it != cend; ++it) {
SymbolGroupNode *c = *it;
c->setReferencedBy(this);
m_children.push_back(c);
}
}
// Run dumpers, format simple in-line dumper value and retrieve fake children
std::wstring SymbolGroupNode::simpleDumpValue(const SymbolGroupValueContext &ctx,
const DumpParameters &)
{
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)
if ((m_flags & SimpleDumperMask) == 0) {
m_flags |= dumpSimpleType(this , ctx, &m_dumperValue,
&m_dumperType, &m_dumperContainerSize);
if (m_flags & SimpleDumperOk)
return m_dumperValue;
}
return symbolGroupFixedValue();
}
......@@ -765,7 +812,7 @@ void SymbolGroupNode::dump(std::ostream &str,
bool valueEnabled = !uninitialized;
// Shall it be recoded?
std::wstring value = displayValue(ctx);
std::wstring value = simpleDumpValue(ctx, p);
int encoding = 0;
if (p.recode(t, iname, ctx, &value, &encoding)) {
str << ",valueencoded=\"" << encoding
......@@ -780,9 +827,19 @@ void SymbolGroupNode::dump(std::ostream &str,
str << '"';
}
}
// Children: Dump all known or subelements (guess).
const VectorIndexType childCountGuess = uninitialized ? 0 :
(m_children.empty() ? m_parameters.SubElements : m_children.size());
// Children: Dump all known non-obscured or subelements
unsigned childCountGuess = 0;
if (!uninitialized) {
if (m_dumperContainerSize > 0) {
childCountGuess = m_dumperContainerSize; // See Obscured handling
} else {
if (m_children.empty()) {
childCountGuess = m_parameters.SubElements; // Guess
} else {
childCountGuess = unsigned(m_children.size());
}
}
}
// No children..suppose we are editable and enabled
if (childCountGuess != 0)
valueEditable = false;
......@@ -822,6 +879,8 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
{
indentStream(str, depth);
str << '"' << fullIName() << "\",index=" << m_index;
if (m_referencedBy)
str << ",referenced by \"" << m_referencedBy->fullIName() << '"';
if (const VectorIndexType childCount = m_children.size())
str << ", Children=" << childCount;
str << ' ' << m_parameters;
......@@ -829,23 +888,36 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
str << " node-flags=" << m_flags;
if (m_flags & Uninitialized)
str << " UNINITIALIZED";
if (m_flags & DumperNotApplicable)
if (m_flags & SimpleDumperNotApplicable)
str << " DumperNotApplicable";
if (m_flags & DumperOk)
if (m_flags & SimpleDumperOk)
str << " DumperOk";
if (m_flags & DumperFailed)
if (m_flags & SimpleDumperFailed)
str << " DumperFailed";
if (m_flags & ExpandedByDumper)
str << " ExpandedByDumper";
if (m_flags & AdditionalSymbol)
str << " AdditionalSymbol";
if (m_flags & Obscured)
str << " Obscured";
if (m_flags & ComplexDumperOk)
str << " ComplexDumperOk";
str << ' ';
}
if (verbosity) {
str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec
<< " Type=\"" << type() << '"';
if (m_dumperType >= 0) {
str << " ,dumperType=" << m_dumperType;
if (m_dumperType & KT_Qt_Type)
str << " qt";
if (m_dumperType & KT_STL_Type)
str << " STL";
if (m_dumperType & KT_ContainerType)
str << " container(" << m_dumperContainerSize << ')';
}
if (!(m_flags & Uninitialized))
str << "\" Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"';
str << " Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"';
}
str << '\n';
}
......@@ -997,6 +1069,9 @@ std::string SymbolGroup::dump(const std::string &iname,
if (node->canExpand() && !node->expand(errorMessage))
return false;
}
// After expansion, run the complex dumpers
if (p.dumpFlags & DumpParameters::DumpComplexDumpers)
node->runComplexDumpers(ctx);
std::ostringstream str;
if (p.humanReadable())
str << '\n';
......@@ -1237,22 +1312,28 @@ DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
const SymbolGroupValueContext &context,
const DumpParameters &parameters) :
m_os(os), m_context(context), m_parameters(parameters),
m_visitChildren(false)
m_visitChildren(false),m_lastDepth(unsigned(-1))
{
}
SymbolGroupNodeVisitor::VisitResult
DumpSymbolGroupNodeVisitor::visit(SymbolGroupNode *node, unsigned child, unsigned depth)
DumpSymbolGroupNodeVisitor::visit(SymbolGroupNode *node, unsigned /* child */, unsigned depth)
{
// Recurse to children if expanded by explicit watchmodel request
// Show container children only
if (node->flags() & SymbolGroupNode::Obscured)
return VisitSkipChildren;
// Recurse to children only 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
// Comma between same level children given obscured children
if (depth == m_lastDepth) {
m_os << ',';
} else {
m_lastDepth = depth;
}
if (m_parameters.humanReadable()) {
m_os << '\n';
indentStream(m_os, depth * 2);
......@@ -1269,9 +1350,9 @@ SymbolGroupNodeVisitor::VisitResult
return m_visitChildren ? VisitContinue : VisitSkipChildren;
}
void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *, unsigned)
void DumpSymbolGroupNodeVisitor::childrenVisited(const SymbolGroupNode *n, unsigned)
{
m_os << "]}"; // Close children array and self
if (m_parameters.humanReadable())
m_os << '\n';
m_os << " /* end of '" << n->fullIName() << "' */\n";
}
......@@ -68,13 +68,21 @@ struct DumpParameters
FormatMap individualFormats;
};
// 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. 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&);
......@@ -89,12 +97,14 @@ class SymbolGroupNode {
public:
enum Flags {
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,
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
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;
......@@ -104,6 +114,9 @@ public:
~SymbolGroupNode() { removeChildren(); }
// Indicate reference
void setReferencedBy(SymbolGroupNode *n);
void removeChildren();
void parseParameters(SymbolParameterVector::size_type index,
SymbolParameterVector::size_type parameterOffset,
......@@ -126,6 +139,7 @@ public:
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
......@@ -135,9 +149,10 @@ public:
std::wstring symbolGroupRawValue() const;
std::wstring symbolGroupFixedValue() const;
std::wstring displayValue(const SymbolGroupValueContext &ctx);
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;
......@@ -146,6 +161,7 @@ public:
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);
......@@ -161,9 +177,13 @@ 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;
......@@ -171,6 +191,8 @@ private:
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
......@@ -312,6 +334,7 @@ private:
const SymbolGroupValueContext &m_context;
const DumpParameters &m_parameters;
bool m_visitChildren;
unsigned m_lastDepth;
};
#endif // SYMBOLGROUP_H
......@@ -220,6 +220,62 @@ std::string SymbolGroupValue::stripPointerType(const std::string &t)
return endsWith(t, " *") ? t.substr(0, t.size() - 2) : t;
}
std::string SymbolGroupValue::stripArrayType(const std::string &t)
{
const std::string::size_type bracket = t.rfind('[');
if (bracket != std::string::npos) {
std::string rc = t.substr(0, bracket);
trimBack(rc);
return rc;
}
return t;
}
// get the inner types: "QMap<int, double>" -> "int", "double"
std::vector<std::string> SymbolGroupValue::innerTypesOf(const std::string &t)
{
std::vector<std::string> rc;
std::string::size_type pos = t.find('<');
if (pos == std::string::npos)
return rc;
rc.reserve(5);
const std::string::size_type size = t.size();
// Record all elements of level 1 to work correctly for
// 'std::map<std::basic_string< .. > >'
unsigned level = 0;
std::string::size_type start = 0;
for ( ; pos < size ; pos++) {
const char c = t.at(pos);
switch (c) {