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

Add expanding of symbols.

parent 1f869cb5
......@@ -145,6 +145,7 @@ CdbDebugEnginePrivate::CdbDebugEnginePrivate(DebuggerManager *parent, CdbDebugEn
m_debuggerManager(parent),
m_debuggerManagerAccess(parent->engineInterface()),
m_currentStackTrace(0),
m_firstActivatedFrame(true),
m_mode(AttachCore)
{
}
......@@ -428,22 +429,15 @@ void CdbDebugEngine::exitDebugger()
killWatchTimer();
}
class ModelBuildIterator {
public:
explicit ModelBuildIterator(WatchHandler *wh) : m_wh(wh) {}
ModelBuildIterator & operator*() { return *this; }
ModelBuildIterator &operator=(const WatchData &wd);
ModelBuildIterator &operator++() { return *this; }
private:
WatchHandler *m_wh;
};
ModelBuildIterator &ModelBuildIterator::operator=(const WatchData &wd)
CdbSymbolGroupContext *CdbDebugEnginePrivate::getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const
{
m_wh->insertData(wd);
return *this;
if (!m_currentStackTrace) {
*errorMessage = QLatin1String(msgNoStackTraceC);
return 0;
}
if (CdbSymbolGroupContext *sg = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage))
return sg;
return 0;
}
bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
......@@ -452,36 +446,29 @@ bool CdbDebugEnginePrivate::updateLocals(int frameIndex,
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << frameIndex;
bool success = false;
wh->cleanup();
do {
if (!m_currentStackTrace) {
*errorMessage = QLatin1String(msgNoStackTraceC);
break;
}
CdbSymbolGroupContext *sgc = m_currentStackTrace->symbolGroupContextAt(frameIndex, errorMessage);
if (!sgc) {
break;
}
ModelBuildIterator it(wh);
sgc->getSymbols(sgc->prefix(), it);
success = true;
} while (false);
wh->rebuildModel();
bool success = false;
if (CdbSymbolGroupContext *sgc = getStackFrameSymbolGroupContext(frameIndex, errorMessage))
success = CdbSymbolGroupContext::populateModelInitially(sgc, wh, errorMessage);
wh->rebuildModel();
return success;
}
void CdbDebugEngine::updateWatchModel()
{
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
const QList<WatchData> incomplete = watchHandler->takeCurrentIncompletes();
const int frameIndex = m_d->m_debuggerManagerAccess->stackHandler()->currentIndex();
if (debugCDB)
qDebug() << Q_FUNC_INFO << incomplete.size();
foreach (const WatchData& wd, incomplete)
qDebug() << Q_FUNC_INFO << wd.toString();
qDebug() << Q_FUNC_INFO << "fi=" << frameIndex;
bool success = false;
QString errorMessage;
if (CdbSymbolGroupContext *sg = m_d->m_currentStackTrace->symbolGroupContextAt(frameIndex, &errorMessage))
success = CdbSymbolGroupContext::completeModel(sg, m_d->m_debuggerManagerAccess->watchHandler(), &errorMessage);
if (!success)
qWarning("%s : %s", Q_FUNC_INFO, qPrintable(errorMessage));
}
void CdbDebugEngine::stepExec()
......@@ -655,17 +642,18 @@ void CdbDebugEngine::activateFrame(int frameIndex)
bool success = false;
do {
StackHandler *stackHandler = m_d->m_debuggerManagerAccess->stackHandler();
WatchHandler *watchHandler = m_d->m_debuggerManagerAccess->watchHandler();
const int oldIndex = stackHandler->currentIndex();
if (frameIndex >= stackHandler->stackSize()) {
errorMessage = msgStackIndexOutOfRange(frameIndex, stackHandler->stackSize());
break;
}
if (oldIndex != frameIndex) {
if (oldIndex != frameIndex)
stackHandler->setCurrentIndex(frameIndex);
if (!m_d->updateLocals(frameIndex, m_d->m_debuggerManagerAccess->watchHandler(), &errorMessage))
if (oldIndex != frameIndex || m_d->m_firstActivatedFrame)
if (!m_d->updateLocals(frameIndex, watchHandler, &errorMessage))
break;
}
const StackFrame &frame = stackHandler->currentFrame();
if (!frame.isUsable()) {
......@@ -678,6 +666,7 @@ void CdbDebugEngine::activateFrame(int frameIndex)
} while (false);
if (!success)
qWarning("%s", qPrintable(errorMessage));
m_d->m_firstActivatedFrame = false;
}
void CdbDebugEngine::selectThread(int index)
......@@ -917,6 +906,7 @@ void CdbDebugEnginePrivate::updateStackTrace()
m_debuggerManager->gotoLocation(stackFrames.at(current).file,
stackFrames.at(current).line, true);
}
m_firstActivatedFrame = true;
}
void CdbDebugEnginePrivate::handleDebugOutput(const char *szOutputString)
......
......@@ -79,6 +79,7 @@ struct CdbDebugEnginePrivate
void handleDebugOutput(const char* szOutputString);
void handleBreakpointEvent(PDEBUG_BREAKPOINT pBP);
void cleanStackTrace();
CdbSymbolGroupContext *getStackFrameSymbolGroupContext(int frameIndex, QString *errorMessage) const;
HANDLE m_hDebuggeeProcess;
HANDLE m_hDebuggeeThread;
......@@ -98,6 +99,7 @@ struct CdbDebugEnginePrivate
DebuggerManager *m_debuggerManager;
IDebuggerManagerAccessForEngines *m_debuggerManagerAccess;
CdbStackTraceContext *m_currentStackTrace;
bool m_firstActivatedFrame;
DebuggerStartMode m_mode;
Core::Utils::ConsoleProcess m_consoleStubProc;
......
......@@ -127,7 +127,9 @@ CdbSymbolGroupContext *CdbStackTraceContext::symbolGroupContextAt(int index, QSt
IDebugSymbolGroup2 *sg = createSymbolGroup(index, errorMessage);
if (!sg)
return 0;
CdbSymbolGroupContext *sc = new CdbSymbolGroupContext(QLatin1String("local"), sg);
CdbSymbolGroupContext *sc = CdbSymbolGroupContext::create(QLatin1String("local"), sg, errorMessage);
if (!sc)
return 0; \
m_symbolContexts[index] = sc;
return sc;
}
......
......@@ -30,6 +30,37 @@
#include "cdbsymbolgroupcontext.h"
#include "cdbdebugengine_p.h"
#include "watchhandler.h"
#include "watchutils.h"
#include <QtCore/QTextStream>
static inline void debugSymbolFlags(unsigned long f, QTextStream &str)
{
if (f & DEBUG_SYMBOL_EXPANDED)
str << "DEBUG_SYMBOL_EXPANDED";
if (f & DEBUG_SYMBOL_READ_ONLY)
str << "|DEBUG_SYMBOL_READ_ONLY";
if (f & DEBUG_SYMBOL_IS_ARRAY)
str << "|DEBUG_SYMBOL_IS_ARRAY";
if (f & DEBUG_SYMBOL_IS_FLOAT)
str << "|DEBUG_SYMBOL_IS_FLOAT";
if (f & DEBUG_SYMBOL_IS_ARGUMENT)
str << "|DEBUG_SYMBOL_IS_ARGUMENT";
if (f & DEBUG_SYMBOL_IS_LOCAL)
str << "|DEBUG_SYMBOL_IS_LOCAL";
}
QTextStream &operator<<(QTextStream &str, const DEBUG_SYMBOL_PARAMETERS& p)
{
str << " Type=" << p.TypeId << " parent=";
if (p.ParentSymbol == DEBUG_ANY_ID) {
str << "<ROOT>";
} else {
str << p.ParentSymbol;
}
str << " Subs=" << p.SubElements << " flags=" << p.Flags << '/';
debugSymbolFlags(p.Flags, str);
return str;
}
// A helper function to extract a string value from a member function of
// IDebugSymbolGroup2 taking the symbol index and a character buffer.
......@@ -68,84 +99,182 @@ CdbSymbolGroupContext::~CdbSymbolGroupContext()
m_symbolGroup->Release();
}
CdbSymbolGroupContext::Range
CdbSymbolGroupContext::getSymbolRange(const QString &prefix)
CdbSymbolGroupContext *CdbSymbolGroupContext::create(const QString &prefix,
IDebugSymbolGroup2 *symbolGroup,
QString *errorMessage)
{
CdbSymbolGroupContext *rc= new CdbSymbolGroupContext(prefix, symbolGroup);
if (!rc->init(errorMessage)) {
delete rc;
return 0;
}
return rc;
}
bool CdbSymbolGroupContext::init(QString *errorMessage)
{
// retrieve the root symbols
ULONG count;
HRESULT hr = m_symbolGroup->GetNumberSymbols(&count);
if (FAILED(hr)) {
*errorMessage = msgComFailed("GetNumberSymbols", hr);
return false;
}
m_symbolParameters.reserve(3u * count);
m_symbolParameters.resize(count);
hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters());
if (FAILED(hr)) {
*errorMessage = msgComFailed("GetSymbolParameters", hr);
return false;
}
populateINameIndexMap(m_prefix, 0, count);
return true;
}
void CdbSymbolGroupContext::populateINameIndexMap(const QString &prefix, unsigned long start, unsigned long count)
{
const QString symbolPrefix = prefix + m_nameDelimiter;
const unsigned long end = start + count;
for (unsigned long i = start; i < end; i++)
if (isSymbolDisplayable(m_symbolParameters.at(i))) {
const QString name = symbolPrefix + getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i);
m_inameIndexMap.insert(name, i);
}
}
QString CdbSymbolGroupContext::toString() const
{
QString rc;
QTextStream str(&rc);
const int count = m_symbolParameters.size();
for (int i = 0; i < count; i++) {
str << i << ' ';
const DEBUG_SYMBOL_PARAMETERS &p = m_symbolParameters.at(i);
if (p.ParentSymbol != DEBUG_ANY_ID)
str << " ";
str << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i);
if (p.Flags & DEBUG_SYMBOL_IS_LOCAL)
str << " '" << getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, i);
str << p << '\n';
}
return rc;
}
bool CdbSymbolGroupContext::isSymbolDisplayable(const DEBUG_SYMBOL_PARAMETERS &p)
{
return p.Flags & (DEBUG_SYMBOL_IS_LOCAL|DEBUG_SYMBOL_IS_ARGUMENT);
}
static inline bool isSymbolExpanded(const DEBUG_SYMBOL_PARAMETERS &p) { return p.Flags & DEBUG_SYMBOL_EXPANDED; }
bool CdbSymbolGroupContext::isExpanded(unsigned long index) const
{
return isSymbolExpanded(m_symbolParameters.at(index));
}
/* Retrieve children and get the position. */
bool CdbSymbolGroupContext::getChildSymbolsPosition(const QString &prefix,
unsigned long *start,
unsigned long *parentId,
QString *errorMessage)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << prefix;
const ChildRangeMap::const_iterator it = m_childRanges.constFind(prefix);
if (it != m_childRanges.constEnd())
return it.value();
const Range r = prefix == m_prefix ? allocateRootSymbols() : allocateChildSymbols(prefix);
m_childRanges.insert(prefix, r);
return r;
}
CdbSymbolGroupContext::Range
CdbSymbolGroupContext::allocateChildSymbols(const QString &prefix)
{
unsigned long startPos = 0;
unsigned long count = 0;
bool success = false;
QString errorMessage;
do {
const int parentIndex = m_symbolINames.indexOf(prefix);
if (parentIndex == -1) {
errorMessage = QString::fromLatin1("Prefix not found '%1'").arg(prefix);
break;
}
qDebug() << Q_FUNC_INFO << '\n'<< prefix;
success = true;
} while (false);
if (!success) {
qWarning("%s\n", qPrintable(errorMessage));
*start = *parentId = 0;
// Root item?
if (prefix == m_prefix) {
*start = 0;
*parentId = DEBUG_ANY_ID;
if (debugCDB)
qDebug() << '<' << prefix << "at" << *start << '\n' << toString();
return true;
}
// Get parent index, make sure it is expanded
NameIndexMap::const_iterator nit = m_inameIndexMap.constFind(prefix);
if (nit == m_inameIndexMap.constEnd()) {
*errorMessage = QString::fromLatin1("'%1' not found.").arg(prefix);
return false;
}
return Range(startPos, count);
*parentId = nit.value();
*start = nit.value() + 1;
if (!expandSymbol(prefix, *parentId, errorMessage))
return false;
if (debugCDB)
qDebug() << '<' << prefix << "at" << *start << '\n' << toString();
return true;
}
CdbSymbolGroupContext::Range
CdbSymbolGroupContext::allocateRootSymbols()
// Expand a symbol using the symbol group interface.
bool CdbSymbolGroupContext::expandSymbol(const QString &prefix, unsigned long index, QString *errorMessage)
{
unsigned long startPos = 0;
unsigned long count = 0;
bool success = false;
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << prefix << index;
QString errorMessage;
do {
HRESULT hr = m_symbolGroup->GetNumberSymbols(&count);
if (FAILED(hr)) {
errorMessage = msgComFailed("GetNumberSymbols", hr);
break;
}
if (isExpanded(index))
return true;
m_symbolParameters.reserve(3u * count);
m_symbolParameters.resize(count);
HRESULT hr = m_symbolGroup->ExpandSymbol(index, TRUE);
if (FAILED(hr)) {
*errorMessage = QString::fromLatin1("Unable to expand '%1' %2: %3").
arg(prefix).arg(index).arg(msgComFailed("ExpandSymbol", hr));
return false;
}
// Hopefully, this will never fail, else data structure will be foobar.
const ULONG oldSize = m_symbolParameters.size();
ULONG newSize;
hr = m_symbolGroup->GetNumberSymbols(&newSize);
if (FAILED(hr)) {
*errorMessage = msgComFailed("GetNumberSymbols", hr);
return false;
}
hr = m_symbolGroup->GetSymbolParameters(0, count, symbolParameters());
if (FAILED(hr)) {
errorMessage = msgComFailed("GetSymbolParameters", hr);
break;
}
const QString symbolPrefix = m_prefix + m_nameDelimiter;
for (unsigned long i = 0; i < count; i++)
m_symbolINames.push_back(symbolPrefix + getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolNameWide, i));
// Retrieve the new parameter structs which will be inserted
// after the parents, offsetting consecutive indexes.
m_symbolParameters.resize(newSize);
success = true;
} while (false);
if (!success) {
clear();
count = 0;
qWarning("%s\n", qPrintable(errorMessage));
hr = m_symbolGroup->GetSymbolParameters(0, newSize, symbolParameters());
if (FAILED(hr)) {
*errorMessage = msgComFailed("GetSymbolParameters", hr);
return false;
}
return Range(startPos, count);
// The new symbols are inserted after the parent symbol.
// We need to correct the following values in the name->index map
const unsigned long newSymbolCount = newSize - oldSize;
const NameIndexMap::iterator nend = m_inameIndexMap.end();
for (NameIndexMap::iterator it = m_inameIndexMap.begin(); it != nend; ++it)
if (it.value() > index)
it.value() += newSymbolCount;
// insert the new symbols
populateINameIndexMap(prefix, index + 1, newSymbolCount);
return true;
}
void CdbSymbolGroupContext::clear()
{
m_symbolParameters.clear();
m_childRanges.clear();
m_symbolINames.clear();
m_inameIndexMap.clear();
}
int CdbSymbolGroupContext::getDisplayableChildCount(unsigned long index) const
{
if (!isExpanded(index))
return 0;
int rc = 0;
// Skip over expanded children, count displayable ones
const unsigned long childCount = m_symbolParameters.at(index).SubElements;
unsigned long seenChildren = 0;
for (unsigned long c = index + 1; seenChildren < childCount; c++) {
const DEBUG_SYMBOL_PARAMETERS &params = m_symbolParameters.at(c);
if (params.ParentSymbol == index) {
seenChildren++;
if (isSymbolDisplayable(params))
rc++;
}
}
return rc;
}
WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const
......@@ -154,23 +283,131 @@ WatchData CdbSymbolGroupContext::symbolAt(unsigned long index) const
qDebug() << Q_FUNC_INFO << index;
WatchData wd;
wd.iname = m_symbolINames.at(index);
wd.iname = m_inameIndexMap.key(index);
const int lastDelimiterPos = wd.iname.lastIndexOf(m_nameDelimiter);
wd.name = lastDelimiterPos == -1 ? wd.iname : wd.iname.mid(lastDelimiterPos + 1);
wd.type = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, index);
wd.value = getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index);
wd.setType(getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolTypeNameWide, index));
wd.setValue(getSymbolString(m_symbolGroup, &IDebugSymbolGroup2::GetSymbolValueTextWide, index).toUtf8());
wd.setChildrenNeeded(); // compensate side effects of above setters
wd.setChildCountNeeded();
// Figure out children. The SubElement is only a guess unless the symbol,
// is expanded, so, we leave this as a guess for later updates.
const DEBUG_SYMBOL_PARAMETERS &params = m_symbolParameters.at(index);
if (params.SubElements) {
wd.setTypeUnneeded();
wd.setValueUnneeded();
wd.setChildCount(1);
if (isSymbolExpanded(params))
wd.setChildCount(getDisplayableChildCount(index));
wd.setChildrenUnneeded();
} else {
wd.setAllUnneeded();
wd.setChildCount(0);
}
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << wd.toString();
return wd;
}
class WatchDataBackInserter {
public:
explicit WatchDataBackInserter(QList<WatchData> &wh) : m_wh(wh) {}
WatchDataBackInserter & operator*() { return *this; }
WatchDataBackInserter &operator=(const WatchData &wd) {
m_wh.push_back(wd);
return *this;
}
WatchDataBackInserter &operator++() { return *this; }
private:
QList<WatchData> &m_wh;
};
static bool insertChildrenRecursion(const QString &iname,
CdbSymbolGroupContext *sg,
WatchHandler *watchHandler,
int level,
QString *errorMessage,
int *childCount = 0);
// Insert a symbol and its children recursively if
// they are known.
static bool insertSymbolRecursion(const WatchData wd,
CdbSymbolGroupContext *sg,
WatchHandler *watchHandler,
int level,
QString *errorMessage
)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << wd.iname << level;
watchHandler->insertData(wd);
if (wd.childCount && wd.isChildCountKnown())
return insertChildrenRecursion(wd.iname, sg, watchHandler, level + 1, errorMessage);
return true;
}
// Insert the children of prefix.
static bool insertChildrenRecursion(const QString &iname,
CdbSymbolGroupContext *sg,
WatchHandler *watchHandler,
int level,
QString *errorMessage,
int *childCount)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO << '\n' << iname << level;
QList<WatchData> watchList;
if (!sg->getChildSymbols(iname, WatchDataBackInserter(watchList), errorMessage))
return false;
if (childCount)
*childCount = watchList.size();
foreach(const WatchData &wd, watchList)
if (!insertSymbolRecursion(wd, sg, watchHandler, level + 1, errorMessage))
return false;
return true;
}
bool CdbSymbolGroupContext::populateModelInitially(CdbSymbolGroupContext *sg,
WatchHandler *watchHandler,
QString *errorMessage)
{
if (debugCDB)
qDebug() << Q_FUNC_INFO;
// Insert root items and known children.
QList<WatchData> watchList;
if (!sg->getChildSymbols(sg->prefix(), WatchDataBackInserter(watchList), errorMessage))
return false;
foreach(const WatchData &wd, watchList)
if (!insertSymbolRecursion(wd, sg, watchHandler, 0, errorMessage))
return false;
return true;
}
bool CdbSymbolGroupContext::completeModel(CdbSymbolGroupContext *sg,
WatchHandler *watchHandler,
QString *errorMessage)
{
const QList<WatchData> incomplete = watchHandler->takeCurrentIncompletes();
if (debugCDB) {
qDebug() << Q_FUNC_INFO << wd.toString();
QDebug nsp = qDebug().nospace();
nsp << Q_FUNC_INFO << '\n' << incomplete.size();
foreach(const WatchData& wd, incomplete)
nsp << ' ' << wd.iname;
nsp << '\n';
}
return wd;
// At this point, it should be nodes with unknown children.
int childCount;
foreach(WatchData wd, incomplete) {
if (insertChildrenRecursion(wd.iname, sg, watchHandler, 0, errorMessage, &childCount)) {
wd.setChildCount(childCount);
watchHandler->insertData(wd);
} else {
return false;
}
}
return true;
}
}
......
......@@ -44,65 +44,85 @@ namespace Debugger {
namespace Internal {
class WatchData;
class WatchHandler;
/* A thin wrapper around the IDebugSymbolGroup2 interface which represents
* a flat list of symbols using an index (for example, belonging to a stack frame).
* It uses the hierarchical naming convention of WatchHandler as:
* a flat list of symbols using an index (for example, belonging to a stack
* frame). It uses the hierarchical naming convention of WatchHandler as in: