forked from qt-creator/qt-creator
1236 lines
47 KiB
C++
1236 lines
47 KiB
C++
/**************************************************************************
|
|
**
|
|
** This file is part of Qt Creator
|
|
**
|
|
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
**
|
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
|
**
|
|
** No Commercial Usage
|
|
**
|
|
** This file contains pre-release code and may not be distributed.
|
|
** You may use this file in accordance with the terms and conditions
|
|
** contained in the Technology Preview License Agreement accompanying
|
|
** this package.
|
|
**
|
|
** 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.
|
|
**
|
|
** In addition, as a special exception, Nokia gives you certain additional
|
|
** rights. These rights are described in the Nokia Qt LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** If you have questions regarding the use of this file, please contact
|
|
** Nokia at qt-info@nokia.com.
|
|
**
|
|
**************************************************************************/
|
|
|
|
#include "symbolgroupnode.h"
|
|
#include "symbolgroup.h"
|
|
#include "symbolgroupvalue.h"
|
|
#include "stringutils.h"
|
|
#include "base64.h"
|
|
#include "containers.h"
|
|
#include "extensioncontext.h"
|
|
|
|
#include <algorithm>
|
|
|
|
typedef std::vector<int>::size_type VectorIndexType;
|
|
typedef std::vector<std::string> StringVector;
|
|
|
|
enum { BufSize = 2048 };
|
|
|
|
static inline void indentStream(std::ostream &str, unsigned depth)
|
|
{
|
|
for (unsigned d = 0; d < depth; d++)
|
|
str << " ";
|
|
}
|
|
|
|
static inline void debugNodeFlags(std::ostream &str, unsigned f)
|
|
{
|
|
if (!f)
|
|
return;
|
|
str << " node-flags=" << f;
|
|
if (f & SymbolGroupNode::Uninitialized)
|
|
str << " UNINITIALIZED";
|
|
if (f & SymbolGroupNode::SimpleDumperNotApplicable)
|
|
str << " DumperNotApplicable";
|
|
if (f & SymbolGroupNode::SimpleDumperOk)
|
|
str << " DumperOk";
|
|
if (f & SymbolGroupNode::SimpleDumperFailed)
|
|
str << " DumperFailed";
|
|
if (f & SymbolGroupNode::ExpandedByDumper)
|
|
str << " ExpandedByDumper";
|
|
if (f & SymbolGroupNode::AdditionalSymbol)
|
|
str << " AdditionalSymbol";
|
|
if (f & SymbolGroupNode::Obscured)
|
|
str << " Obscured";
|
|
if (f & SymbolGroupNode::ComplexDumperOk)
|
|
str << " ComplexDumperOk";
|
|
str << ' ';
|
|
}
|
|
|
|
// Some helper to conveniently dump flags to a stream
|
|
struct DebugNodeFlags
|
|
{
|
|
DebugNodeFlags(unsigned f) : m_f(f) {}
|
|
const unsigned m_f;
|
|
};
|
|
|
|
inline std::ostream &operator<<(std::ostream &str, const DebugNodeFlags &f)
|
|
{
|
|
debugNodeFlags(str, f.m_f);
|
|
return str;
|
|
}
|
|
|
|
// -------------- AbstractSymbolGroupNode
|
|
|
|
AbstractSymbolGroupNode::AbstractSymbolGroupNode(const std::string &name,
|
|
const std::string &iname) :
|
|
m_name(name), m_iname(iname), m_parent(0), m_flags(0)
|
|
{
|
|
}
|
|
|
|
AbstractSymbolGroupNode::~AbstractSymbolGroupNode()
|
|
{
|
|
}
|
|
|
|
std::string AbstractSymbolGroupNode::absoluteFullIName() const
|
|
{
|
|
std::string rc = iName();
|
|
for (const AbstractSymbolGroupNode *p = m_parent; p; p = p->m_parent) {
|
|
rc.insert(0, 1, SymbolGroupNodeVisitor::iNamePathSeparator);
|
|
rc.insert(0, p->iName());
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
AbstractSymbolGroupNode *AbstractSymbolGroupNode::childAt(unsigned i) const
|
|
{
|
|
const AbstractSymbolGroupNodePtrVector &c = children();
|
|
return i < c.size() ? c.at(i) : static_cast<AbstractSymbolGroupNode *>(0);
|
|
}
|
|
|
|
unsigned AbstractSymbolGroupNode::indexByIName(const char *n) const
|
|
{
|
|
const AbstractSymbolGroupNodePtrVector &c = children();
|
|
const VectorIndexType size = c.size();
|
|
for (VectorIndexType i = 0; i < size; i++)
|
|
if ( c.at(i)->iName() == n )
|
|
return unsigned(i);
|
|
return unsigned(-1);
|
|
}
|
|
|
|
AbstractSymbolGroupNode *AbstractSymbolGroupNode::childByIName(const char *n) const
|
|
{
|
|
const unsigned index = indexByIName(n);
|
|
if (index != unsigned(-1))
|
|
return children().at(index);
|
|
return 0;
|
|
}
|
|
|
|
bool AbstractSymbolGroupNode::accept(SymbolGroupNodeVisitor &visitor,
|
|
const std::string &parentIname,
|
|
unsigned child, unsigned depth)
|
|
{
|
|
// If we happen to be the root node, just skip over
|
|
const bool invisibleRoot = !m_parent;
|
|
const unsigned childDepth = invisibleRoot ? 0 : depth + 1;
|
|
|
|
std::string fullIname = parentIname;
|
|
if (!fullIname.empty())
|
|
fullIname.push_back(SymbolGroupNodeVisitor::iNamePathSeparator);
|
|
fullIname += m_iname;
|
|
|
|
const SymbolGroupNodeVisitor::VisitResult vr =
|
|
invisibleRoot ? SymbolGroupNodeVisitor::VisitContinue :
|
|
visitor.visit(this, fullIname, child, depth);
|
|
switch (vr) {
|
|
case SymbolGroupNodeVisitor::VisitStop:
|
|
return true;
|
|
case SymbolGroupNodeVisitor::VisitSkipChildren:
|
|
break;
|
|
case SymbolGroupNodeVisitor::VisitContinue: {
|
|
const AbstractSymbolGroupNodePtrVector &c = children();
|
|
const unsigned childCount = unsigned(c.size());
|
|
for (unsigned i = 0; i < childCount; i++)
|
|
if (c.at(i)->accept(visitor, fullIname, i, childDepth))
|
|
return true;
|
|
if (!invisibleRoot)
|
|
visitor.childrenVisited(this, depth);
|
|
}
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void AbstractSymbolGroupNode::debug(std::ostream &str, const std::string &visitingFullIname,
|
|
unsigned /* verbosity */, unsigned depth) const
|
|
{
|
|
indentStream(str, 2 * depth);
|
|
str << "AbstractSymbolGroupNode " << visitingFullIname
|
|
<< " with " << children().size() << " children\n";
|
|
}
|
|
|
|
void AbstractSymbolGroupNode::dumpBasicData(std::ostream &str, const std::string &aName,
|
|
const std::string &aFullIname,
|
|
const std::string &type /* = "" */,
|
|
const std::string &expression /* = "" */)
|
|
{
|
|
str << "iname=\"" << aFullIname << "\",name=\"" << aName << '"';
|
|
if (!type.empty())
|
|
str << ",type=\"" << type << '"';
|
|
if (!expression.empty())
|
|
str << ",exp=\"" << expression << '"';
|
|
}
|
|
|
|
void AbstractSymbolGroupNode::setParent(AbstractSymbolGroupNode *n)
|
|
{
|
|
if (m_parent)
|
|
dprintf("Internal error: Attempt to change non-null parent of %s", m_name.c_str());
|
|
m_parent = n;
|
|
}
|
|
|
|
// -------- BaseSymbolGroupNode
|
|
BaseSymbolGroupNode::BaseSymbolGroupNode(const std::string &name, const std::string &iname) :
|
|
AbstractSymbolGroupNode(name, iname)
|
|
{
|
|
}
|
|
|
|
BaseSymbolGroupNode::~BaseSymbolGroupNode()
|
|
{
|
|
removeChildren();
|
|
}
|
|
|
|
void BaseSymbolGroupNode::removeChildren()
|
|
{
|
|
if (!m_children.empty()) {
|
|
const AbstractSymbolGroupNodePtrVectorIterator end = m_children.end();
|
|
for (AbstractSymbolGroupNodePtrVectorIterator it = m_children.begin(); it != end; ++it)
|
|
delete *it;
|
|
m_children.clear();
|
|
}
|
|
}
|
|
|
|
void BaseSymbolGroupNode::addChild(AbstractSymbolGroupNode *c)
|
|
{
|
|
c->setParent(this);
|
|
m_children.push_back(c);
|
|
}
|
|
|
|
// ----------- Helpers: Stream DEBUG_SYMBOL_PARAMETERS
|
|
|
|
std::ostream &operator<<(std::ostream &str, const DEBUG_SYMBOL_PARAMETERS ¶meters)
|
|
{
|
|
str << "parent=";
|
|
if (parameters.ParentSymbol == DEBUG_ANY_ID) {
|
|
str << "DEBUG_ANY_ID";
|
|
} else {
|
|
str << parameters.ParentSymbol ;
|
|
}
|
|
if (parameters.Flags != 0 && parameters.Flags != 1)
|
|
str << " flags=" << parameters.Flags;
|
|
// Detailed flags:
|
|
if (parameters.Flags & DEBUG_SYMBOL_EXPANDED)
|
|
str << " EXPANDED";
|
|
if (parameters.Flags & DEBUG_SYMBOL_READ_ONLY)
|
|
str << " READONLY";
|
|
if (parameters.Flags & DEBUG_SYMBOL_IS_ARRAY)
|
|
str << " ARRAY";
|
|
if (parameters.Flags & DEBUG_SYMBOL_IS_FLOAT)
|
|
str << " FLOAT";
|
|
if (parameters.Flags & DEBUG_SYMBOL_IS_ARGUMENT)
|
|
str << " ARGUMENT";
|
|
if (parameters.Flags & DEBUG_SYMBOL_IS_LOCAL)
|
|
str << " LOCAL";
|
|
str << " typeId=" << parameters.TypeId;
|
|
if (parameters.SubElements)
|
|
str << " subElements=" << parameters.SubElements;
|
|
return str;
|
|
}
|
|
|
|
// --------------- DumpParameters
|
|
DumpParameters::DumpParameters() : dumpFlags(0)
|
|
{
|
|
}
|
|
|
|
// typeformats: decode hex-encoded name, value pairs:
|
|
// '414A=2,...' -> map of "AB:2".
|
|
DumpParameters::FormatMap DumpParameters::decodeFormatArgument(const std::string &f)
|
|
{
|
|
FormatMap rc;
|
|
const std::string::size_type size = f.size();
|
|
// Split 'hexname=4,'
|
|
for (std::string::size_type pos = 0; pos < size ; ) {
|
|
// Cut out key
|
|
const std::string::size_type equalsPos = f.find('=', pos);
|
|
if (equalsPos == std::string::npos)
|
|
return rc;
|
|
const std::string name = stringFromHex(f.c_str() + pos, f.c_str() + equalsPos);
|
|
// Search for number
|
|
const std::string::size_type numberPos = equalsPos + 1;
|
|
std::string::size_type nextPos = f.find(',', numberPos);
|
|
if (nextPos == std::string::npos)
|
|
nextPos = size;
|
|
int format;
|
|
if (!integerFromString(f.substr(numberPos, nextPos - numberPos), &format))
|
|
return rc;
|
|
rc.insert(FormatMap::value_type(name, format));
|
|
pos = nextPos + 1;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int DumpParameters::format(const std::string &type, const std::string &iname) const
|
|
{
|
|
if (!individualFormats.empty()) {
|
|
const FormatMap::const_iterator iit = individualFormats.find(iname);
|
|
if (iit != individualFormats.end())
|
|
return iit->second;
|
|
}
|
|
if (!typeFormats.empty()) {
|
|
const FormatMap::const_iterator tit = typeFormats.find(type);
|
|
if (tit != typeFormats.end())
|
|
return tit->second;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
enum PointerFormats // Watch data pointer format requests
|
|
{
|
|
FormatAuto = 0,
|
|
FormatLatin1String = 1,
|
|
FormatUtf8String = 2,
|
|
FormatUtf16String = 3,
|
|
FormatUcs4String = 4
|
|
};
|
|
|
|
enum DumpEncoding // WatchData encoding of GDBMI values
|
|
{
|
|
DumpEncodingAscii = 0,
|
|
DumpEncodingBase64 = 1,
|
|
DumpEncodingBase64_Utf16 = 2,
|
|
DumpEncodingBase64_Ucs4 = 3,
|
|
DumpEncodingHex_Latin1 = 6,
|
|
DumpEncodingHex_Utf16 = 7,
|
|
DumpEncodingHex_Ucs4_LittleEndian = 8,
|
|
DumpEncodingHex_Utf8_LittleEndian = 9,
|
|
DumpEncodingHex_Ucs4_BigEndian = 10,
|
|
DumpEncodingHex_Utf16_BigEndian = 11,
|
|
DumpEncodingHex_Utf16_LittleEndian = 12
|
|
};
|
|
|
|
/* Recode arrays/pointers of char*, wchar_t according to users
|
|
* sepcification. Handles char formats for 'char *', '0x834478 "hallo.."'
|
|
* and 'wchar_t *', '0x834478 "hallo.."'.
|
|
* This is done by retrieving the address and the length (in characters)
|
|
* of the CDB output, converting it to memory size, fetching the data
|
|
* from memory, zero-terminating and recoding it using the encoding
|
|
* defined in watchutils.cpp.
|
|
* As a special case, if there is no user-defined format and the
|
|
* CDB output contains '?' indicating non-printable characters,
|
|
* append a hex dump of the memory (auto-format). */
|
|
|
|
bool DumpParameters::recode(const std::string &type,
|
|
const std::string &iname,
|
|
const SymbolGroupValueContext &ctx,
|
|
std::wstring *value, int *encoding) const
|
|
{
|
|
// We basically handle char formats for 'char *', '0x834478 "hallo.."'
|
|
// and 'wchar_t *', '0x834478 "hallo.."'
|
|
// Determine address and length from the pointer value output,
|
|
// read the raw memory and recode if that is possible.
|
|
if (type.empty() || type.at(type.size() - 1) != '*')
|
|
return false;
|
|
const int newFormat = format(type, iname);
|
|
if (value->compare(0, 2, L"0x"))
|
|
return false;
|
|
const std::wstring::size_type quote1 = value->find(L'"', 2);
|
|
if (quote1 == std::wstring::npos)
|
|
return false;
|
|
// The user did not specify any format, still, there are '?'
|
|
// (indicating non-printable) in what the debugger prints. In that case,
|
|
// append a hex dump to the normal output. If there are no '?'-> all happy.
|
|
if (newFormat < FormatLatin1String && value->find(L'?', quote1 + 1) == std::wstring::npos)
|
|
return false;
|
|
const std::wstring::size_type quote2 = value->find(L'"', quote1 + 1);
|
|
if (quote2 == std::wstring::npos)
|
|
return false;
|
|
std::wstring::size_type length = quote2 - quote1 - 1;
|
|
if (!length)
|
|
return false;
|
|
// Get address from value
|
|
ULONG64 address = 0;
|
|
if (!integerFromWString(value->substr(0, quote1 - 1), &address) || !address)
|
|
return false;
|
|
// Get real size if this is for example a wchar_t *.
|
|
const unsigned elementSize = SymbolGroupValue::sizeOf(SymbolGroupValue::stripPointerType(type).c_str());
|
|
if (!elementSize)
|
|
return false;
|
|
length *= elementSize;
|
|
// Allocate real length + 8 bytes ('\0') for largest format (Ucs4).
|
|
// '\0' is not listed in the CDB output.
|
|
const std::wstring::size_type allocLength = length + 8;
|
|
unsigned char *buffer = new unsigned char[allocLength];
|
|
std::fill(buffer, buffer + allocLength, 0);
|
|
ULONG obtained = 0;
|
|
if (FAILED(ctx.dataspaces->ReadVirtual(address, buffer, ULONG(length), &obtained))) {
|
|
delete [] buffer;
|
|
return false;
|
|
}
|
|
// Recode raw memory
|
|
switch (newFormat) {
|
|
case FormatLatin1String:
|
|
*value = dataToHexW(buffer, buffer + length + 1); // Latin1 + 0
|
|
*encoding = DumpEncodingHex_Latin1;
|
|
break;
|
|
case FormatUtf8String:
|
|
*value = dataToHexW(buffer, buffer + length + 1); // UTF8 + 0
|
|
*encoding = DumpEncodingHex_Utf8_LittleEndian;
|
|
break;
|
|
case FormatUtf16String: // Paranoia: make sure buffer is terminated at 2 byte borders
|
|
if (length % 2) {
|
|
length &= ~1;
|
|
buffer[length] = '\0';
|
|
buffer[length + 1] = '\0';
|
|
}
|
|
*value = base64EncodeToWString(buffer, length + 2);
|
|
*encoding = DumpEncodingBase64_Utf16;
|
|
break;
|
|
case FormatUcs4String: // Paranoia: make sure buffer is terminated at 4 byte borders
|
|
if (length % 4) {
|
|
length &= ~3;
|
|
std::fill(buffer + length, buffer + length + 4, 0);
|
|
}
|
|
*value = dataToHexW(buffer, buffer + length + 2); // UTF16 + 0
|
|
*encoding = DumpEncodingHex_Ucs4_LittleEndian;
|
|
break;
|
|
default: // See above, append hex dump
|
|
value->push_back(' ');
|
|
value->append(dataToReadableHexW(buffer, buffer + length));
|
|
*encoding = DumpEncodingAscii;
|
|
break;
|
|
}
|
|
delete [] buffer;
|
|
return true;
|
|
}
|
|
|
|
// ------- SymbolGroupNode
|
|
|
|
SymbolGroupNode::SymbolGroupNode(SymbolGroup *symbolGroup,
|
|
ULONG index,
|
|
const std::string &name,
|
|
const std::string &iname) :
|
|
BaseSymbolGroupNode(name, iname),
|
|
m_symbolGroup(symbolGroup),
|
|
m_index(index), m_dumperType(-1), m_dumperContainerSize(-1)
|
|
{
|
|
memset(&m_parameters, 0, sizeof(DEBUG_SYMBOL_PARAMETERS));
|
|
m_parameters.ParentSymbol = DEBUG_ANY_ID;
|
|
}
|
|
|
|
const SymbolGroupNode *SymbolGroupNode::symbolGroupNodeParent() const
|
|
{
|
|
if (const AbstractSymbolGroupNode *p = parent())
|
|
return p->asSymbolGroupNode();
|
|
return 0;
|
|
}
|
|
|
|
bool SymbolGroupNode::isArrayElement() const
|
|
{
|
|
if (const SymbolGroupNode *p = symbolGroupNodeParent())
|
|
return (p->m_parameters.Flags & DEBUG_SYMBOL_IS_ARRAY) != 0;
|
|
return false;
|
|
}
|
|
|
|
// Notify about expansion of a node:
|
|
// Adapt our index and those of our children if we are behind it.
|
|
// Return true if a modification was required to be able to terminate the
|
|
// recursion.
|
|
bool SymbolGroupNode::notifyExpanded(ULONG index, ULONG insertedCount)
|
|
{
|
|
typedef AbstractSymbolGroupNodePtrVector::const_reverse_iterator ReverseIt;
|
|
// Looping backwards over the children. If a subtree has no modifications,
|
|
// (meaning all other indexes are smaller) we can stop.
|
|
const ReverseIt rend = children().rend();
|
|
for (ReverseIt it = children().rbegin(); it != rend; ++it) {
|
|
if (SymbolGroupNode *c = (*it)->asSymbolGroupNode())
|
|
if (!c->notifyExpanded(index, insertedCount))
|
|
return false;
|
|
}
|
|
|
|
// Correct our own + parent index if applicable.
|
|
if (m_index == DEBUG_ANY_ID || m_index < index)
|
|
return false;
|
|
|
|
m_index += insertedCount;
|
|
if (m_parameters.ParentSymbol != DEBUG_ANY_ID && m_parameters.ParentSymbol >= index)
|
|
m_parameters.ParentSymbol += insertedCount;
|
|
return true;
|
|
}
|
|
|
|
// Fix names: fix complicated template base names
|
|
static inline void fixName(std::string *name)
|
|
{
|
|
// Long template base classes 'std::tree_base<Key....>' -> 'std::tree<>'
|
|
// for nice display
|
|
const std::string::size_type templatePos = name->find('<');
|
|
if (templatePos != std::string::npos) {
|
|
name->erase(templatePos + 1, name->size() - templatePos - 1);
|
|
name->push_back('>');
|
|
}
|
|
}
|
|
|
|
// Fix inames: arrays and long, complicated template base names
|
|
static inline void fixIname(unsigned &id, std::string *iname)
|
|
{
|
|
// Fix array iname "[0]" -> "0" for sorting to work correctly
|
|
if (!iname->empty() && iname->at(0) == '[') {
|
|
const std::string::size_type last = iname->size() - 1;
|
|
if (iname->at(last) == ']') {
|
|
iname->erase(last, 1);
|
|
iname->erase(0, 1);
|
|
return;
|
|
}
|
|
}
|
|
// Long template base classes 'std::tree_base<Key....' -> 'tree@t1',
|
|
// usable as identifier and command line parameter
|
|
const std::string::size_type templatePos = iname->find('<');
|
|
if (templatePos != std::string::npos) {
|
|
iname->erase(templatePos, iname->size() - templatePos);
|
|
if (iname->compare(0, 5, "std::") == 0)
|
|
iname->erase(0, 5);
|
|
iname->append("@t");
|
|
iname->append(toString(id++));
|
|
}
|
|
}
|
|
|
|
// Fix up names and inames
|
|
static inline void fixNames(bool isTopLevel, StringVector *names, StringVector *inames)
|
|
{
|
|
if (names->empty())
|
|
return;
|
|
unsigned unnamedId = 1;
|
|
unsigned templateId = 1;
|
|
/* 1) Fix name="__formal", which occurs when someone writes "void foo(int /* x * /)..."
|
|
* 2) Fix array inames for sorting: "[6]" -> name="[6]",iname="6"
|
|
* 3) For toplevels: Fix shadowed variables in the order the debugger expects them:
|
|
\code
|
|
int x; // Occurrence (1), should be reported as name="x <shadowed 1>"/iname="x#1"
|
|
if (true) {
|
|
int x = 5; (2) // Occurrence (2), should be reported as name="x"/iname="x"
|
|
}
|
|
\endcode */
|
|
StringVector::iterator nameIt = names->begin();
|
|
const StringVector::iterator namesEnd = names->end();
|
|
for (StringVector::iterator iNameIt = inames->begin(); nameIt != namesEnd ; ++nameIt, ++iNameIt) {
|
|
std::string &name = *nameIt;
|
|
std::string &iname = *iNameIt;
|
|
if (name.empty() || name == "__formal") {
|
|
const std::string number = toString(unnamedId++);
|
|
name = "<unnamed " + number + '>';
|
|
iname = "unnamed#" + number;
|
|
} else {
|
|
fixName(&name);
|
|
fixIname(templateId, &iname);
|
|
}
|
|
if (isTopLevel) {
|
|
if (const StringVector::size_type shadowCount = std::count(nameIt + 1, namesEnd, name)) {
|
|
const std::string number = toString(shadowCount);
|
|
name += " <shadowed ";
|
|
name += number;
|
|
name += '>';
|
|
iname += '#';
|
|
iname += number;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Index: Index of symbol, parameterOffset: Looking only at a part of the symbol array, offset
|
|
void SymbolGroupNode::parseParameters(VectorIndexType index,
|
|
VectorIndexType parameterOffset,
|
|
const SymbolGroup::SymbolParameterVector &vec)
|
|
{
|
|
static char buf[BufSize];
|
|
ULONG obtainedSize;
|
|
|
|
const bool isTopLevel = index == DEBUG_ANY_ID;
|
|
if (isTopLevel) {
|
|
m_parameters.Flags |= DEBUG_SYMBOL_EXPANDED;
|
|
} else {
|
|
m_parameters = vec.at(index - parameterOffset);
|
|
if (m_parameters.SubElements == 0 || !(m_parameters.Flags & DEBUG_SYMBOL_EXPANDED))
|
|
return; // No children
|
|
}
|
|
if (m_parameters.SubElements > 1)
|
|
reserveChildren(m_parameters.SubElements);
|
|
|
|
const VectorIndexType size = vec.size();
|
|
// Scan the top level elements
|
|
StringVector names;
|
|
names.reserve(size);
|
|
// Pass 1) Determine names. We need the complete set first in order to do some corrections.
|
|
const VectorIndexType startIndex = isTopLevel ? 0 : index + 1;
|
|
for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) {
|
|
if (vec.at(pos).ParentSymbol == index) {
|
|
const VectorIndexType symbolGroupIndex = pos + parameterOffset;
|
|
if (FAILED(m_symbolGroup->debugSymbolGroup()->GetSymbolName(ULONG(symbolGroupIndex), buf, BufSize, &obtainedSize)))
|
|
buf[0] = '\0';
|
|
names.push_back(std::string(buf));
|
|
}
|
|
}
|
|
// 2) Fix names
|
|
StringVector inames = names;
|
|
fixNames(isTopLevel, &names, &inames);
|
|
// Pass 3): Add nodes with fixed names
|
|
StringVector::size_type nameIndex = 0;
|
|
for (VectorIndexType pos = startIndex - parameterOffset; pos < size ; pos++ ) {
|
|
if (vec.at(pos).ParentSymbol == index) {
|
|
const VectorIndexType symbolGroupIndex = pos + parameterOffset;
|
|
SymbolGroupNode *child = new SymbolGroupNode(m_symbolGroup,
|
|
ULONG(symbolGroupIndex),
|
|
names.at(nameIndex),
|
|
inames.at(nameIndex));
|
|
child->parseParameters(symbolGroupIndex, parameterOffset, vec);
|
|
addChild(child);
|
|
nameIndex++;
|
|
}
|
|
}
|
|
if (isTopLevel)
|
|
m_parameters.SubElements = ULONG(children().size());
|
|
}
|
|
|
|
SymbolGroupNode *SymbolGroupNode::create(SymbolGroup *sg, const std::string &name, const SymbolGroup::SymbolParameterVector &vec)
|
|
{
|
|
SymbolGroupNode *rc = new SymbolGroupNode(sg, DEBUG_ANY_ID, name, name);
|
|
rc->parseParameters(DEBUG_ANY_ID, 0, vec);
|
|
return rc;
|
|
}
|
|
|
|
// Fix some oddities in CDB values
|
|
|
|
static inline bool isHexDigit(wchar_t c)
|
|
{
|
|
return (c >= L'0' && c <= L'9') || (c >= L'a' && c <= L'f') || (c >= L'A' && c <= L'F');
|
|
}
|
|
|
|
static void fixValue(const std::string &type, std::wstring *value)
|
|
{
|
|
// Pointers/Unsigned integers: fix '0x00000000`00000AD bla' ... to "0xAD bla"
|
|
const bool isHexNumber = value->size() > 3 && value->compare(0, 2, L"0x") == 0 && isHexDigit(value->at(2));
|
|
if (isHexNumber) {
|
|
// Remove dumb 64bit separator
|
|
if (value->size() > 10 && value->at(10) == L'`')
|
|
value->erase(10, 1);
|
|
const std::string::size_type firstNonNullDigit = value->find_first_not_of(L"0", 2);
|
|
// No on-null digits: plain null ptr.
|
|
if (firstNonNullDigit == std::string::npos || value->at(firstNonNullDigit) == ' ') {
|
|
*value = L"0x0";
|
|
} else {
|
|
// Strip
|
|
if (firstNonNullDigit > 2)
|
|
value->erase(2, firstNonNullDigit - 2);
|
|
}
|
|
}
|
|
|
|
// Pointers: fix '0x00000000`00000AD class bla' ... to "0xAD", but leave
|
|
// 'const char *' values as is ('0x00000000`00000AD "hallo").
|
|
if (!type.empty() && type.at(type.size() - 1) == L'*') {
|
|
// Strip ' Class bla"
|
|
std::wstring::size_type classPos = value->find(L" struct", 2);
|
|
if (classPos == std::string::npos)
|
|
classPos = value->find(L" class", 2);
|
|
if (classPos != std::string::npos)
|
|
value->erase(classPos, value->size() - classPos);
|
|
return;
|
|
}
|
|
|
|
// unsigned hex ints that are not pointers: Convert to decimal as not to confuse watch model:
|
|
if (isHexNumber) {
|
|
ULONG64 uv;
|
|
std::wistringstream str(*value);
|
|
str >> std::hex >> uv;
|
|
if (!str.fail()) {
|
|
*value = toWString(uv);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Integers: fix '0n10' -> '10'
|
|
if (value->size() >= 3 && value->compare(0, 2, L"0n") == 0
|
|
&& (isdigit(value->at(2)) || value->at(2) == L'-')) {
|
|
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 || value->compare(0, 7, L"struct ") == 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, size_t size)
|
|
{
|
|
const wchar_t *bufEnd = buf + size;
|
|
for (const wchar_t *bufPtr = buf; bufPtr < bufEnd; bufPtr++) {
|
|
const wchar_t c = *bufPtr;
|
|
if (c > 127 || (c < 32 && c != 9))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string SymbolGroupNode::type() const
|
|
{
|
|
static char buf[BufSize];
|
|
const HRESULT hr = m_symbolGroup->debugSymbolGroup()->GetSymbolTypeName(m_index, buf, BufSize, NULL);
|
|
return SUCCEEDED(hr) ? std::string(buf) : std::string();
|
|
}
|
|
|
|
unsigned SymbolGroupNode::size() const
|
|
{
|
|
DEBUG_SYMBOL_ENTRY entry;
|
|
if (SUCCEEDED(m_symbolGroup->debugSymbolGroup()->GetSymbolEntryInformation(m_index, &entry)))
|
|
return entry.Size;
|
|
return 0;
|
|
}
|
|
|
|
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
|
|
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::symbolGroupFixedValue() const
|
|
{
|
|
std::wstring value = symbolGroupRawValue();
|
|
fixValue(type(), &value);
|
|
return value;
|
|
}
|
|
|
|
// A quick check if symbol is valid by checking for inaccessible value
|
|
bool SymbolGroupNode::isMemoryAccessible() const
|
|
{
|
|
static const char notAccessibleValueC[] = "<Memory access error>";
|
|
char buffer[sizeof(notAccessibleValueC)];
|
|
ULONG obtained = 0;
|
|
if (FAILED(symbolGroup()->debugSymbolGroup()->GetSymbolValueText(m_index, buffer, sizeof(notAccessibleValueC), &obtained)))
|
|
return false;
|
|
if (obtained < sizeof(notAccessibleValueC))
|
|
return true;
|
|
return strcmp(buffer, notAccessibleValueC) != 0;
|
|
}
|
|
|
|
// Complex dumpers: Get container/fake children
|
|
void SymbolGroupNode::runComplexDumpers(const SymbolGroupValueContext &ctx)
|
|
{
|
|
if (symbolGroupDebug)
|
|
DebugPrint() << "SymbolGroupNode::runComplexDumpers " << name() << '/'
|
|
<< absoluteFullIName() << ' ' << m_index << DebugNodeFlags(flags());
|
|
|
|
if (m_dumperContainerSize <= 0 || (testFlags(ComplexDumperOk) || !testFlags(SimpleDumperOk)))
|
|
return;
|
|
addFlags(ComplexDumperOk);
|
|
const AbstractSymbolGroupNodePtrVector ctChildren =
|
|
containerChildren(this, m_dumperType, m_dumperContainerSize, ctx);
|
|
m_dumperContainerSize = int(ctChildren.size()); // Just in case...
|
|
if (ctChildren.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
|
|
AbstractSymbolGroupNodePtrVectorConstIterator cend = children().end();
|
|
for (AbstractSymbolGroupNodePtrVectorConstIterator it = children().begin(); it != cend; ++it)
|
|
(*it)->addFlags(Obscured);
|
|
// Add children and mark them as referenced by us.
|
|
cend = ctChildren.end();
|
|
for (AbstractSymbolGroupNodePtrVectorConstIterator it = ctChildren.begin(); it != cend; ++it)
|
|
addChild(*it);
|
|
}
|
|
|
|
// Run dumpers, format simple in-line dumper value and retrieve fake children
|
|
bool SymbolGroupNode::runSimpleDumpers(const SymbolGroupValueContext &ctx)
|
|
{
|
|
if (symbolGroupDebug)
|
|
DebugPrint() << "SymbolGroupNode::runSimpleDumpers " << name() << '/'
|
|
<< absoluteFullIName() << ' ' << m_index << DebugNodeFlags(flags());
|
|
if (testFlags(Uninitialized))
|
|
return false;
|
|
if (testFlags(SimpleDumperOk))
|
|
return true;
|
|
if (testFlags(SimpleDumperMask))
|
|
return false;
|
|
addFlags(dumpSimpleType(this , ctx, &m_dumperValue,
|
|
&m_dumperType, &m_dumperContainerSize));
|
|
if (symbolGroupDebug)
|
|
DebugPrint() << "-> '" << wStringToString(m_dumperValue) << "' Type="
|
|
<< m_dumperType << ' ' << DebugNodeFlags(flags());
|
|
return testFlags(SimpleDumperOk);
|
|
}
|
|
|
|
std::wstring SymbolGroupNode::simpleDumpValue(const SymbolGroupValueContext &ctx)
|
|
{
|
|
if (testFlags(Uninitialized))
|
|
return L"<not in scope>";
|
|
if (runSimpleDumpers(ctx))
|
|
return m_dumperValue;
|
|
return symbolGroupFixedValue();
|
|
}
|
|
|
|
void SymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname,
|
|
const DumpParameters &p, const SymbolGroupValueContext &ctx)
|
|
{
|
|
dumpNode(str, name(), visitingFullIname, p, ctx);
|
|
}
|
|
|
|
void SymbolGroupNode::dumpNode(std::ostream &str,
|
|
const std::string &aName,
|
|
const std::string &aFullIName,
|
|
const DumpParameters &dumpParameters,
|
|
const SymbolGroupValueContext &ctx)
|
|
{
|
|
const std::string t = type();
|
|
SymbolGroupNode::dumpBasicData(str, aName, aFullIName, t, aFullIName);
|
|
|
|
if (const ULONG64 addr = address())
|
|
str << ",addr=\"" << std::hex << std::showbase << addr << std::noshowbase << std::dec
|
|
<< '"';
|
|
|
|
const bool uninitialized = flags() & Uninitialized;
|
|
bool valueEditable = !uninitialized;
|
|
bool valueEnabled = !uninitialized;
|
|
|
|
// Shall it be recoded?
|
|
std::wstring value = simpleDumpValue(ctx);
|
|
int encoding = 0;
|
|
if (dumpParameters.recode(t, aFullIName, ctx, &value, &encoding)) {
|
|
str << ",valueencoded=\"" << encoding
|
|
<< "\",value=\"" << gdbmiWStringFormat(value) <<'"';
|
|
} else { // As is: ASCII or base64?
|
|
if (isSevenBitClean(value.c_str(), value.size())) {
|
|
str << ",valueencoded=\"" << DumpEncodingAscii << "\",value=\""
|
|
<< gdbmiWStringFormat(value) << '"';
|
|
} else {
|
|
str << ",valueencoded=\"" << DumpEncodingBase64_Utf16 << "\",value=\"";
|
|
base64Encode(str, reinterpret_cast<const unsigned char *>(value.c_str()), value.size() * sizeof(wchar_t));
|
|
str << '"';
|
|
}
|
|
}
|
|
// 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 (children().empty()) {
|
|
childCountGuess = m_parameters.SubElements; // Guess
|
|
} else {
|
|
childCountGuess = unsigned(children().size());
|
|
}
|
|
}
|
|
}
|
|
// No children..suppose we are editable and enabled
|
|
if (childCountGuess != 0)
|
|
valueEditable = false;
|
|
str << ",valueenabled=\"" << (valueEnabled ? "true" : "false") << '"'
|
|
<< ",valueeditable=\"" << (valueEditable ? "true" : "false") << '"'
|
|
<< ",numchild=\"" << childCountGuess << '"';
|
|
}
|
|
|
|
void SymbolGroupNode::debug(std::ostream &str,
|
|
const std::string &visitingFullIname,
|
|
unsigned verbosity, unsigned depth) const
|
|
{
|
|
indentStream(str, depth);
|
|
const std::string fullIname = absoluteFullIName();
|
|
str << "AbsIname=" << fullIname << '"';
|
|
if (fullIname != visitingFullIname)
|
|
str << ",VisitIname=\"" <<visitingFullIname;
|
|
str << "\",index=" << m_index;
|
|
if (const VectorIndexType childCount = children().size())
|
|
str << ", Children=" << childCount;
|
|
str << ' ' << m_parameters << DebugNodeFlags(flags());
|
|
if (verbosity) {
|
|
str << ",name=\"" << 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 (!testFlags(Uninitialized))
|
|
str << " Value=\"" << gdbmiWStringFormat(symbolGroupRawValue()) << '"';
|
|
str << '\n'; // Potentially multiline
|
|
}
|
|
str << '\n';
|
|
}
|
|
|
|
static inline std::string msgCannotCast(const std::string &nodeName,
|
|
const std::string &fromType,
|
|
const std::string &toType,
|
|
const std::string &why)
|
|
{
|
|
std::ostringstream str;
|
|
str << "Cannot cast node '" << nodeName << "' from '" << fromType
|
|
<< "' to '" << toType << "': " << why;
|
|
return str.str();
|
|
}
|
|
|
|
static std::string msgExpandFailed(const std::string &name, const std::string &iname,
|
|
ULONG index, const std::string &why)
|
|
{
|
|
std::ostringstream str;
|
|
str << "Expansion of '" << name << "'/'" << iname << " (index: " << index
|
|
<< ") failed: " << why;
|
|
return str.str();
|
|
}
|
|
|
|
// Expand!
|
|
bool SymbolGroupNode::expand(std::string *errorMessage)
|
|
{
|
|
if (symbolGroupDebug)
|
|
DebugPrint() << "SymbolGroupNode::expand " << name()
|
|
<<'/' << absoluteFullIName() << ' '
|
|
<< m_index << DebugNodeFlags(flags());
|
|
if (isExpanded()) {
|
|
// Clear the flag indication dumper expansion on a second, explicit request
|
|
clearFlags(ExpandedByDumper);
|
|
return true;
|
|
}
|
|
if (!canExpand()) {
|
|
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index,
|
|
"No subelements to expand in node.");
|
|
return false;
|
|
}
|
|
if (flags() & Uninitialized) {
|
|
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index,
|
|
"Refusing to expand uninitialized node.");
|
|
return false;
|
|
}
|
|
|
|
const HRESULT hr = m_symbolGroup->debugSymbolGroup()->ExpandSymbol(m_index, TRUE);
|
|
|
|
if (FAILED(hr)) {
|
|
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, msgDebugEngineComFailed("ExpandSymbol", hr));
|
|
ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
|
|
return false;
|
|
}
|
|
SymbolGroup::SymbolParameterVector parameters;
|
|
// Retrieve parameters (including self, re-retrieve symbol parameters to get new 'expanded' flag
|
|
// and corrected SubElement count (might be estimate))
|
|
if (!SymbolGroup::getSymbolParameters(m_symbolGroup->debugSymbolGroup(),
|
|
m_index, m_parameters.SubElements + 1,
|
|
¶meters, errorMessage)) {
|
|
*errorMessage = msgExpandFailed(name(), absoluteFullIName(), m_index, *errorMessage);
|
|
return false;
|
|
}
|
|
// Before inserting children, correct indexes on whole group
|
|
m_symbolGroup->root()->notifyExpanded(m_index + 1, parameters.at(0).SubElements);
|
|
// Parse parameters, correct our own) and create child nodes.
|
|
parseParameters(m_index, m_index, parameters);
|
|
return true;
|
|
}
|
|
|
|
bool SymbolGroupNode::expandRunComplexDumpers(const SymbolGroupValueContext &ctx,
|
|
std::string *errorMessage)
|
|
{
|
|
if (isExpanded() || testFlags(ComplexDumperOk))
|
|
return true;
|
|
if (!expand(errorMessage))
|
|
return false;
|
|
// Run simple dumpers to obtain type and run complex dumpers
|
|
if (runSimpleDumpers(ctx) && testFlags(SimpleDumperOk))
|
|
runComplexDumpers(ctx);
|
|
return true;
|
|
}
|
|
|
|
bool SymbolGroupNode::typeCast(const std::string &desiredType, std::string *errorMessage)
|
|
{
|
|
const std::string fromType = type();
|
|
if (fromType == desiredType)
|
|
return true;
|
|
if (isExpanded()) {
|
|
*errorMessage = msgCannotCast(absoluteFullIName(), fromType, desiredType, "Already expanded");
|
|
return false;
|
|
}
|
|
HRESULT hr = m_symbolGroup->debugSymbolGroup()->OutputAsType(m_index, desiredType.c_str());
|
|
if (FAILED(hr)) {
|
|
*errorMessage = msgCannotCast(absoluteFullIName(), fromType, desiredType, msgDebugEngineComFailed("OutputAsType", hr));
|
|
return false;
|
|
}
|
|
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(m_index, 1, &m_parameters);
|
|
if (FAILED(hr)) { // Should never fail
|
|
*errorMessage = msgCannotCast(absoluteFullIName(), fromType, desiredType, msgDebugEngineComFailed("GetSymbolParameters", hr));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static inline std::string msgCannotAddSymbol(const std::string &name, const std::string &why)
|
|
{
|
|
std::ostringstream str;
|
|
str << "Cannot add symbol '" << name << "': " << why;
|
|
return str.str();
|
|
}
|
|
|
|
// For root nodes, only: Add a new symbol by name
|
|
SymbolGroupNode *SymbolGroupNode::addSymbolByName(const std::string &name,
|
|
const std::string &iname,
|
|
std::string *errorMessage)
|
|
{
|
|
ULONG index = DEBUG_ANY_ID; // Append
|
|
HRESULT hr = m_symbolGroup->debugSymbolGroup()->AddSymbol(name.c_str(), &index);
|
|
if (FAILED(hr)) {
|
|
*errorMessage = msgCannotAddSymbol(name, msgDebugEngineComFailed("AddSymbol", hr));
|
|
ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
|
|
return 0;
|
|
}
|
|
if (index == DEBUG_ANY_ID) { // Occasionally happens for unknown or 'complicated' types
|
|
*errorMessage = msgCannotAddSymbol(name, "DEBUG_ANY_ID was returned as symbol index by AddSymbol.");
|
|
ExtensionContext::instance().report('X', 0, 0, "Error", "%s", errorMessage->c_str());
|
|
return 0;
|
|
}
|
|
SymbolParameterVector parameters(1, DEBUG_SYMBOL_PARAMETERS());
|
|
hr = m_symbolGroup->debugSymbolGroup()->GetSymbolParameters(index, 1, &(*parameters.begin()));
|
|
if (FAILED(hr)) { // Should never fail
|
|
std::ostringstream str;
|
|
str << "Cannot retrieve 1 symbol parameter entry at " << index << ": "
|
|
<< msgDebugEngineComFailed("GetSymbolParameters", hr);
|
|
*errorMessage = msgCannotAddSymbol(name, str.str());
|
|
return 0;
|
|
}
|
|
// Paranoia: Check for cuckoo's eggs (which should not happen)
|
|
if (parameters.front().ParentSymbol != m_index) {
|
|
*errorMessage = msgCannotAddSymbol(name, "Parent id mismatch");
|
|
return 0;
|
|
}
|
|
SymbolGroupNode *node = new SymbolGroupNode(m_symbolGroup, index,
|
|
name, iname.empty() ? name : iname);
|
|
node->parseParameters(0, 0, parameters);
|
|
node->addFlags(AdditionalSymbol);
|
|
addChild(node);
|
|
return node;
|
|
}
|
|
|
|
// --------- ReferenceSymbolGroupNode
|
|
|
|
// Utility returning a pair ('[42]','42') as name/iname pair
|
|
// for a node representing an array index
|
|
typedef std::pair<std::string, std::string> StringStringPair;
|
|
|
|
static inline StringStringPair arrayIndexNameIname(int index)
|
|
{
|
|
StringStringPair rc(std::string(), toString(index));
|
|
rc.first = std::string(1, '[');
|
|
rc.first += rc.second;
|
|
rc.first.push_back(']');
|
|
return rc;
|
|
}
|
|
|
|
ReferenceSymbolGroupNode::ReferenceSymbolGroupNode(const std::string &name,
|
|
const std::string &iname,
|
|
SymbolGroupNode *referencedNode) :
|
|
AbstractSymbolGroupNode(name, iname), m_referencedNode(referencedNode)
|
|
{
|
|
}
|
|
|
|
// Convenience to create a node name name='[1]', iname='1' for arrays
|
|
ReferenceSymbolGroupNode *ReferenceSymbolGroupNode::createArrayNode(int index,
|
|
SymbolGroupNode *referencedNode)
|
|
{
|
|
const StringStringPair nameIname = arrayIndexNameIname(index);
|
|
return new ReferenceSymbolGroupNode(nameIname.first, nameIname.second, referencedNode);
|
|
}
|
|
|
|
void ReferenceSymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname,
|
|
const DumpParameters &p, const SymbolGroupValueContext &ctx)
|
|
{
|
|
// Let the referenced node dump with our iname/name
|
|
m_referencedNode->dumpNode(str, name(), visitingFullIname, p, ctx);
|
|
}
|
|
|
|
void ReferenceSymbolGroupNode::debug(std::ostream &str, const std::string &visitingFullIname,
|
|
unsigned verbosity, unsigned depth) const
|
|
{
|
|
indentStream(str, 2 * depth);
|
|
str << "Node " << name() << '/' << visitingFullIname << " referencing\n";
|
|
m_referencedNode->debug(str, visitingFullIname, verbosity, depth);
|
|
}
|
|
|
|
// ---------------- MapNodeSymbolGroupNode
|
|
MapNodeSymbolGroupNode::MapNodeSymbolGroupNode(const std::string &name,
|
|
const std::string &iname,
|
|
ULONG64 address,
|
|
const std::string &type,
|
|
AbstractSymbolGroupNode *key,
|
|
AbstractSymbolGroupNode *value) :
|
|
BaseSymbolGroupNode(name, iname), m_address(address), m_type(type)
|
|
{
|
|
addChild(key);
|
|
addChild(value);
|
|
}
|
|
|
|
MapNodeSymbolGroupNode
|
|
*MapNodeSymbolGroupNode::create(int index, ULONG64 address,
|
|
const std::string &type,
|
|
SymbolGroupNode *key, SymbolGroupNode *value)
|
|
{
|
|
const StringStringPair nameIname = arrayIndexNameIname(index);
|
|
const std::string keyName = "key";
|
|
ReferenceSymbolGroupNode *keyRN = new ReferenceSymbolGroupNode(keyName, keyName, key);
|
|
const std::string valueName = "value";
|
|
ReferenceSymbolGroupNode *valueRN = new ReferenceSymbolGroupNode(valueName, valueName, value);
|
|
return new MapNodeSymbolGroupNode(nameIname.first, nameIname.second, address, type, keyRN, valueRN);
|
|
}
|
|
|
|
void MapNodeSymbolGroupNode::dump(std::ostream &str, const std::string &visitingFullIname,
|
|
const DumpParameters &, const SymbolGroupValueContext &)
|
|
{
|
|
SymbolGroupNode::dumpBasicData(str, name(), visitingFullIname);
|
|
if (m_address)
|
|
str << ",addr=\"0x" << std::hex << m_address << '"';
|
|
str << ",type=\"" << m_type << "\",valueencoded=\"0\",value=\"\",valueenabled=\"false\""
|
|
",valueeditable=\"false\",numchild=\"2\"";
|
|
}
|
|
|
|
void MapNodeSymbolGroupNode::debug(std::ostream &os, const std::string &visitingFullIname,
|
|
unsigned /* verbosity */, unsigned depth) const
|
|
{
|
|
indentStream(os, 2 * depth);
|
|
os << "MapNode " << name() << '/' << visitingFullIname << '\n';
|
|
}
|
|
|
|
// --------- DebugSymbolGroupNodeVisitor
|
|
|
|
// "local.vi" -> "local"
|
|
std::string SymbolGroupNodeVisitor::parentIname(const std::string &iname)
|
|
{
|
|
const std::string::size_type lastSep = iname.rfind(SymbolGroupNodeVisitor::iNamePathSeparator);
|
|
return lastSep == std::string::npos ? std::string() : iname.substr(0, lastSep);
|
|
}
|
|
|
|
DebugSymbolGroupNodeVisitor::DebugSymbolGroupNodeVisitor(std::ostream &os, unsigned verbosity) :
|
|
m_os(os), m_verbosity(verbosity)
|
|
{
|
|
}
|
|
|
|
SymbolGroupNodeVisitor::VisitResult
|
|
DebugSymbolGroupNodeVisitor::visit(AbstractSymbolGroupNode *node,
|
|
const std::string &aFullIname,
|
|
unsigned /* child */, unsigned depth)
|
|
{
|
|
node->debug(m_os, aFullIname, m_verbosity, depth);
|
|
return VisitContinue;
|
|
}
|
|
|
|
DebugFilterSymbolGroupNodeVisitor::DebugFilterSymbolGroupNodeVisitor(std::ostream &os,
|
|
const std::string &filter,
|
|
const unsigned verbosity) :
|
|
DebugSymbolGroupNodeVisitor(os, verbosity), m_filter(filter)
|
|
{
|
|
}
|
|
|
|
SymbolGroupNodeVisitor::VisitResult
|
|
DebugFilterSymbolGroupNodeVisitor::visit(AbstractSymbolGroupNode *node,
|
|
const std::string &fullIname,
|
|
unsigned child, unsigned depth)
|
|
{
|
|
if (fullIname.find(m_filter) == std::string::npos
|
|
&& node->name().find(m_filter) == std::string::npos)
|
|
return SymbolGroupNodeVisitor::VisitContinue;
|
|
return DebugSymbolGroupNodeVisitor::visit(node, fullIname, child, depth);
|
|
}
|
|
|
|
// --------------------- DumpSymbolGroupNodeVisitor
|
|
DumpSymbolGroupNodeVisitor::DumpSymbolGroupNodeVisitor(std::ostream &os,
|
|
const SymbolGroupValueContext &context,
|
|
const DumpParameters ¶meters) :
|
|
m_os(os), m_context(context), m_parameters(parameters),
|
|
m_visitChildren(false),m_lastDepth(unsigned(-1))
|
|
{
|
|
}
|
|
|
|
SymbolGroupNodeVisitor::VisitResult
|
|
DumpSymbolGroupNodeVisitor::visit(AbstractSymbolGroupNode *node,
|
|
const std::string &fullIname,
|
|
unsigned /* child */, unsigned depth)
|
|
{
|
|
// Show container children only, no additional symbol below root.
|
|
const unsigned flags = node->flags();
|
|
if (flags & (SymbolGroupNode::Obscured|SymbolGroupNode::AdditionalSymbol))
|
|
return VisitSkipChildren;
|
|
// Recurse to children only if expanded by explicit watchmodel request
|
|
// and initialized.
|
|
m_visitChildren = true;
|
|
// Visit children of a SymbolGroupNode only if not expanded by its dumpers.
|
|
// Report only one level for Qt Creator.
|
|
if (SymbolGroupNode *snode = node->asSymbolGroupNode())
|
|
m_visitChildren = depth < 1 && snode->isExpanded()
|
|
&& (flags & (SymbolGroupNode::Uninitialized|SymbolGroupNode::ExpandedByDumper)) == 0;
|
|
// 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);
|
|
}
|
|
m_os << '{';
|
|
node->dump(m_os, fullIname, m_parameters, m_context);
|
|
if (m_visitChildren) { // open children array
|
|
m_os << ",children=[";
|
|
} else { // No children, close array.
|
|
m_os << '}';
|
|
}
|
|
if (m_parameters.humanReadable())
|
|
m_os << '\n';
|
|
return m_visitChildren ? VisitContinue : VisitSkipChildren;
|
|
}
|
|
|
|
void DumpSymbolGroupNodeVisitor::childrenVisited(const AbstractSymbolGroupNode *n, unsigned)
|
|
{
|
|
m_os << "]}"; // Close children array and self
|
|
if (m_parameters.humanReadable())
|
|
m_os << " /* end of '" << n->absoluteFullIName() << "' */\n";
|
|
}
|