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 \ ...@@ -54,7 +54,8 @@ SOURCES += qtcreatorcdbextension.cpp \
gdbmihelpers.cpp \ gdbmihelpers.cpp \
outputcallback.cpp \ outputcallback.cpp \
base64.cpp \ base64.cpp \
symbolgroupvalue.cpp symbolgroupvalue.cpp \
containers.cpp
HEADERS += extensioncontext.h \ HEADERS += extensioncontext.h \
common.h \ common.h \
...@@ -65,4 +66,5 @@ HEADERS += extensioncontext.h \ ...@@ -65,4 +66,5 @@ HEADERS += extensioncontext.h \
gdbmihelpers.h \ gdbmihelpers.h \
outputcallback.h \ outputcallback.h \
base64.h \ base64.h \
symbolgroupvalue.h symbolgroupvalue.h \
containers.h
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include "symbolgroupvalue.h" #include "symbolgroupvalue.h"
#include "stringutils.h" #include "stringutils.h"
#include "base64.h" #include "base64.h"
#include "containers.h"
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
...@@ -383,8 +384,10 @@ SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup, ...@@ -383,8 +384,10 @@ SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup,
const std::string &name, const std::string &name,
const std::string &iname, const std::string &iname,
SymbolGroupNode *parent) : SymbolGroupNode *parent) :
m_symbolGroup(symbolGroup), m_parent(parent), m_index(index), m_symbolGroup(symbolGroup), m_parent(parent),
m_name(name), m_iname(iname), m_flags(0) 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)); memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS));
m_parameters.ParentSymbol = DEBUG_ANY_ID; m_parameters.ParentSymbol = DEBUG_ANY_ID;
...@@ -394,12 +397,23 @@ void SymbolGroupNode::removeChildren() ...@@ -394,12 +397,23 @@ void SymbolGroupNode::removeChildren()
{ {
if (!m_children.empty()) { if (!m_children.empty()) {
const SymbolGroupNodePtrVectorIterator end = m_children.end(); const SymbolGroupNodePtrVectorIterator end = m_children.end();
for (SymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it) for (SymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it) {
delete *it; SymbolGroupNode *child = *it;
if (child->parent() == this) // Do not delete references
delete child;
}
m_children.clear(); 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 bool SymbolGroupNode::isArrayElement() const
{ {
return m_parent && (m_parent->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY); return m_parent && (m_parent->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY);
...@@ -415,9 +429,12 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount) ...@@ -415,9 +429,12 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount)
// Looping backwards over the children. If a subtree has no modifications, // Looping backwards over the children. If a subtree has no modifications,
// (meaning all other indexes are smaller) we can stop. // (meaning all other indexes are smaller) we can stop.
const ReverseIt rend = m_children.rend(); const ReverseIt rend = m_children.rend();
for (ReverseIt it = m_children.rbegin(); it != rend; ++it) for (ReverseIt it = m_children.rbegin(); it != rend; ++it) {
if (!(*it)->notifyExpanded(index, insertedCount)) SymbolGroupNode *c = *it;
return false; 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. // Correct our own + parent index if applicable.
if (m_index == DEBUG_ANY_ID || m_index < index) if (m_index == DEBUG_ANY_ID || m_index < index)
...@@ -433,7 +450,7 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount) ...@@ -433,7 +450,7 @@ bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount)
std::string SymbolGroupNode::fullIName() const std::string SymbolGroupNode::fullIName() const
{ {
std::string rc = iName(); 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, 1, '.');
rc.insert(0, p->iName()); rc.insert(0, p->iName());
} }
...@@ -706,15 +723,45 @@ std::wstring SymbolGroupNode::symbolGroupFixedValue() const ...@@ -706,15 +723,45 @@ std::wstring SymbolGroupNode::symbolGroupFixedValue() const
return value; return value;
} }
// Value to be reported to debugger // Complex dumpers: Get container/fake children
std::wstring SymbolGroupNode::displayValue(const SymbolGroupValueContext &ctx) 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) if (m_flags & Uninitialized)
return L"<not in scope>"; return L"<not in scope>";
if ((m_flags & DumperMask) == 0) if ((m_flags & SimpleDumperMask) == 0) {
m_flags |= dumpSimpleType(this , ctx, &m_dumperValue); m_flags |= dumpSimpleType(this , ctx, &m_dumperValue,
if (m_flags & DumperOk) &m_dumperType, &m_dumperContainerSize);
return m_dumperValue; if (m_flags & SimpleDumperOk)
return m_dumperValue;
}
return symbolGroupFixedValue(); return symbolGroupFixedValue();
} }
...@@ -765,7 +812,7 @@ void SymbolGroupNode::dump(std::ostream &str, ...@@ -765,7 +812,7 @@ void SymbolGroupNode::dump(std::ostream &str,
bool valueEnabled = !uninitialized; bool valueEnabled = !uninitialized;
// Shall it be recoded? // Shall it be recoded?
std::wstring value = displayValue(ctx); std::wstring value = simpleDumpValue(ctx, p);
int encoding = 0; int encoding = 0;
if (p.recode(t, iname, ctx, &value, &encoding)) { if (p.recode(t, iname, ctx, &value, &encoding)) {
str << ",valueencoded=\"" << encoding str << ",valueencoded=\"" << encoding
...@@ -780,9 +827,19 @@ void SymbolGroupNode::dump(std::ostream &str, ...@@ -780,9 +827,19 @@ void SymbolGroupNode::dump(std::ostream &str,
str << '"'; str << '"';
} }
} }
// Children: Dump all known or subelements (guess). // Children: Dump all known non-obscured or subelements
const VectorIndexType childCountGuess = uninitialized ? 0 : unsigned childCountGuess = 0;
(m_children.empty() ? m_parameters.SubElements : m_children.size()); 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 // No children..suppose we are editable and enabled
if (childCountGuess != 0) if (childCountGuess != 0)
valueEditable = false; valueEditable = false;
...@@ -822,6 +879,8 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept ...@@ -822,6 +879,8 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
{ {
indentStream(str, depth); indentStream(str, depth);
str << '"' << fullIName() << "\",index=" << m_index; str << '"' << fullIName() << "\",index=" << m_index;
if (m_referencedBy)
str << ",referenced by \"" << m_referencedBy->fullIName() << '"';
if (const VectorIndexType childCount = m_children.size()) if (const VectorIndexType childCount = m_children.size())
str << ", Children=" << childCount; str << ", Children=" << childCount;
str << ' ' << m_parameters; str << ' ' << m_parameters;
...@@ -829,23 +888,36 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept ...@@ -829,23 +888,36 @@ void SymbolGroupNode::debug(std::ostream &str, unsigned verbosity, unsigned dept
str << " node-flags=" << m_flags; str << " node-flags=" << m_flags;
if (m_flags & Uninitialized) if (m_flags & Uninitialized)
str << " UNINITIALIZED"; str << " UNINITIALIZED";
if (m_flags & DumperNotApplicable) if (m_flags & SimpleDumperNotApplicable)
str << " DumperNotApplicable"; str << " DumperNotApplicable";
if (m_flags & DumperOk) if (m_flags & SimpleDumperOk)
str << " DumperOk"; str << " DumperOk";
if (m_flags & DumperFailed) if (m_flags & SimpleDumperFailed)
str << " DumperFailed"; str << " DumperFailed";
if (m_flags & ExpandedByDumper) if (m_flags & ExpandedByDumper)
str << " ExpandedByDumper"; str << " ExpandedByDumper";
if (m_flags & AdditionalSymbol) if (m_flags & AdditionalSymbol)
str << " AdditionalSymbol"; str << " AdditionalSymbol";
if (m_flags & Obscured)
str << " Obscured";
if (m_flags & ComplexDumperOk)
str << " ComplexDumperOk";
str << ' '; str << ' ';
} }
if (verbosity) { if (verbosity) {
str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec str << ",name=\"" << m_name << "\", Address=0x" << std::hex << address() << std::dec
<< " Type=\"" << type() << '"'; << " 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)) if (!(m_flags & Uninitialized))
str << "\" Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"'; str << " Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"';
} }
str << '\n'; str << '\n';
} }
...@@ -997,6 +1069,9 @@ std::string SymbolGroup::dump(const std::string &iname, ...@@ -997,6 +1069,9 @@ std::string SymbolGroup::dump(const std::string &iname,
if (node->canExpand() && !node->expand(errorMessage)) if (node->canExpand() && !node->expand(errorMessage))
return false; return false;
} }
// After expansion, run the complex dumpers
if (p.dumpFlags & DumpParameters::DumpComplexDumpers)
node->runComplexDumpers(ctx);
std::ostringstream str; std::ostringstream str;
if (p.humanReadable()) if (p.humanReadable())
str << '\n'; str << '\n';
...@@ -1237,22 +1312,28 @@ DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os, ...@@ -1237,22 +1312,28 @@ DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
const SymbolGroupValueContext &context, const SymbolGroupValueContext &context,
const DumpParameters &parameters) : const DumpParameters &parameters) :
m_os(os), m_context(context), m_parameters(parameters), m_os(os), m_context(context), m_parameters(parameters),
m_visitChildren(false) m_visitChildren(false),m_lastDepth(unsigned(-1))
{ {
} }
SymbolGroupNodeVisitor::VisitResult 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. // and initialized.
const unsigned flags = node->flags(); const unsigned flags = node->flags();
m_visitChildren = node->isExpanded() m_visitChildren = node->isExpanded()
&& (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0; && (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0;
// Do not recurse into children unless the node was expanded by the watch model // Comma between same level children given obscured children
if (child) if (depth == m_lastDepth) {
m_os << ','; // Separator in parents list m_os << ',';
} else {
m_lastDepth = depth;
}
if (m_parameters.humanReadable()) { if (m_parameters.humanReadable()) {
m_os << '\n'; m_os << '\n';
indentStream(m_os, depth * 2); indentStream(m_os, depth * 2);
...@@ -1269,9 +1350,9 @@ SymbolGroupNodeVisitor::VisitResult ...@@ -1269,9 +1350,9 @@ SymbolGroupNodeVisitor::VisitResult
return m_visitChildren ? VisitContinue : VisitSkipChildren; 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 m_os << "]}"; // Close children array and self
if (m_parameters.humanReadable()) if (m_parameters.humanReadable())
m_os << '\n'; m_os << " /* end of '" << n->fullIName() << "' */\n";
} }
...@@ -68,13 +68,21 @@ struct DumpParameters ...@@ -68,13 +68,21 @@ struct DumpParameters
FormatMap individualFormats; FormatMap individualFormats;
}; };
// Thin wrapper around a symbol group entry. Provides accessors for fixed-up /* Thin wrapper around a symbol group entry. Provides accessors for fixed-up
// symbol group value and a dumping facility triggered by dump()/displayValue() * symbol group value and a dumping facility consisting of:
// calling dumpSimpleType() based on SymbolGroupValue expressions. These values * - 'Simple' dumping done when running the DumpVisitor. This produces one
// values should be displayed, still allowing for expansion of the structure * line of formatted output shown for the class. These values
// in the debugger. Evaluating the dumpers might expand symbol nodes, which are * values should are displayed, while still allowing for expansion of the structure
// then marked as 'ExpandedByDumper'. This stops the dump recursion to prevent * in the debugger.
// outputting data that were not explicitly expanded by the watch handler. * 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 { class SymbolGroupNode {
SymbolGroupNode(const SymbolGroupNode&); SymbolGroupNode(const SymbolGroupNode&);
...@@ -89,12 +97,14 @@ class SymbolGroupNode { ...@@ -89,12 +97,14 @@ class SymbolGroupNode {
public: public:
enum Flags { enum Flags {
Uninitialized = 0x1, Uninitialized = 0x1,
DumperNotApplicable = 0x2, // No dumper available for type SimpleDumperNotApplicable = 0x2, // No dumper available for type
DumperOk = 0x4, // Internal dumper ran, value set SimpleDumperOk = 0x4, // Internal dumper ran, value set
DumperFailed = 0x8, // Internal dumper failed SimpleDumperFailed = 0x8, // Internal dumper failed
DumperMask = DumperNotApplicable|DumperOk|DumperFailed, SimpleDumperMask = SimpleDumperNotApplicable|SimpleDumperOk|SimpleDumperFailed,
ExpandedByDumper = 0x10, ExpandedByDumper = 0x10,
AdditionalSymbol = 0x20 // Introduced by addSymbol, should not be visible AdditionalSymbol = 0x20, // Introduced by addSymbol, should not be visible
Obscured = 0x40, // Symbol is obscured by (for example) fake container children
ComplexDumperOk = 0x80
}; };