Debugger: Introduce a tree level above stack frames

This level is meant to take the role of current thread handler
in the long run, allowing per-thread stackviews in each engine.

For now, the additional level holds just a single, invisible
dummy item playing the role of a "current thread".

Change-Id: Ief6131500fc1aa8902f2313038a65840b80b495b
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2019-06-24 14:54:47 +02:00
parent c39f2866c7
commit 32bbf2a3b3
8 changed files with 117 additions and 76 deletions

View File

@@ -439,8 +439,6 @@ public:
m_lookupRequests.clear();
m_locationTimer.stop();
m_locationMark.reset();
m_stackHandler.resetLocation();
m_watchHandler.resetLocation();
m_disassemblerAgent.resetLocation();
m_toolTipManager.resetLocation();
}
@@ -1889,7 +1887,7 @@ void DebuggerEngine::operateByInstructionTriggered(bool on)
{
// Go to source only if we have the file.
// if (DebuggerEngine *cppEngine = m_engine->cppEngine()) {
d->m_stackHandler.resetModel();
d->m_stackHandler.rootItem()->updateAll();
if (d->m_stackHandler.currentIndex() >= 0) {
const StackFrame frame = d->m_stackHandler.currentFrame();
if (on || frame.isUsable())

View File

@@ -2581,7 +2581,7 @@ void GdbEngine::loadSymbolsForStack()
{
bool needUpdate = false;
const Modules &modules = modulesHandler()->modules();
stackHandler()->forItemsAtLevel<1>([modules, &needUpdate, this](StackFrameItem *frameItem) {
stackHandler()->forItemsAtLevel<2>([modules, &needUpdate, this](StackFrameItem *frameItem) {
const StackFrame &frame = frameItem->frame;
if (frame.function == "??") {
//qDebug() << "LOAD FOR " << frame.address;

View File

@@ -663,9 +663,8 @@ void QmlEngine::activateFrame(int index)
return;
stackHandler()->setCurrentIndex(index);
auto frameItem = stackHandler()->rootItem()->childAt(index);
QTC_ASSERT(frameItem, return);
gotoLocation(frameItem->frame);
const StackFrame &frame = stackHandler()->frameAt(index);
gotoLocation(frame);
d->updateLocals();
}

View File

@@ -74,32 +74,30 @@ StackHandler::StackHandler(DebuggerEngine *engine)
this, &StackHandler::reloadFullStack);
connect(action(MaximalStackDepth), &QAction::triggered,
this, &StackHandler::reloadFullStack);
// For now there's always only "the" current thread.
rootItem()->appendChild(new ThreadDummyItem);
}
StackHandler::~StackHandler() = default;
QVariant StackHandler::data(const QModelIndex &index, int role) const
QVariant SpecialStackItem::data(int column, int role) const
{
if (!index.isValid() || index.row() > rowCount())
return QVariant();
if (isSpecialFrame(index.row())) {
if (role == Qt::DisplayRole && index.column() == StackLevelColumn)
return tr("...");
if (role == Qt::DisplayRole && index.column() == StackFunctionNameColumn)
return tr("<More>");
if (role == Qt::DecorationRole && index.column() == StackLevelColumn)
if (role == Qt::DisplayRole && column == StackLevelColumn)
return StackHandler::tr("...");
if (role == Qt::DisplayRole && column == StackFunctionNameColumn)
return StackHandler::tr("<More>");
if (role == Qt::DecorationRole && column == StackLevelColumn)
return Icons::EMPTY.icon();
return QVariant();
}
const StackFrameItem *frameItem = static_cast<StackFrameItem *>(itemForIndex(index));
const StackFrame &frame = frameItem->frame;
QVariant StackFrameItem::data(int column, int role) const
{
if (role == Qt::DisplayRole) {
switch (index.column()) {
switch (column) {
case StackLevelColumn:
return QString::number(index.row() + 1);
return row >= 0 ? QString::number(row + 1) : QString();
case StackFunctionNameColumn:
return simplifyType(frame.function);
case StackFileNameColumn:
@@ -114,11 +112,8 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
return QVariant();
}
if (role == Qt::DecorationRole && index.column() == StackLevelColumn) {
// Return icon that indicates whether this is the active stack frame
return (m_contentsValid && index.row() == m_currentIndex)
? Icons::LOCATION.icon() : Icons::EMPTY.icon();
}
if (role == Qt::DecorationRole && column == StackLevelColumn)
return handler->iconForRow(row);
if (role == Qt::ToolTipRole && boolSetting(UseToolTipsInStackView))
return frame.toToolTip();
@@ -126,16 +121,18 @@ QVariant StackHandler::data(const QModelIndex &index, int role) const
return QVariant();
}
Qt::ItemFlags StackHandler::flags(const QModelIndex &index) const
Qt::ItemFlags StackFrameItem::flags(int column) const
{
if (index.row() >= rowCount())
return {};
if (index.row() == rowCount() - 1 && m_canExpand)
return StackHandlerModel::flags(index);
const StackFrame &frame = rootItem()->childAt(index.row())->frame;
const bool isValid = frame.isUsable() || m_engine->operatesByInstruction();
return isValid && m_contentsValid
? StackHandlerModel::flags(index) : Qt::ItemFlags();
const bool isValid = frame.isUsable() || handler->operatesByInstruction();
return isValid && handler->isContentsValid()
? TreeItem::flags(column) : Qt::ItemFlags();
}
QIcon StackHandler::iconForRow(int row) const
{
// Return icon that indicates whether this is the active stack frame
return (m_contentsValid && row == m_currentIndex)
? Icons::LOCATION.icon() : Icons::EMPTY.icon();
}
bool StackHandler::setData(const QModelIndex &idx, const QVariant &data, int role)
@@ -154,6 +151,12 @@ bool StackHandler::setData(const QModelIndex &idx, const QVariant &data, int rol
return false;
}
ThreadDummyItem *StackHandler::dummyThreadItem() const
{
QTC_ASSERT(rootItem()->childCount() == 1, return nullptr);
return rootItem()->childAt(0);
}
StackFrame StackHandler::currentFrame() const
{
if (m_currentIndex == -1)
@@ -164,20 +167,22 @@ StackFrame StackHandler::currentFrame() const
StackFrame StackHandler::frameAt(int index) const
{
auto frameItem = static_cast<StackFrameItem *>(m_root->childAt(index));
auto threadItem = dummyThreadItem();
QTC_ASSERT(threadItem, return {});
StackFrameItem *frameItem = threadItem->childAt(index);
QTC_ASSERT(frameItem, return {});
return frameItem->frame;
}
int StackHandler::stackSize() const
{
return rowCount() - m_canExpand;
return stackRowCount() - m_canExpand;
}
quint64 StackHandler::topAddress() const
{
QTC_ASSERT(rowCount() > 0, return 0);
return rootItem()->childAt(0)->frame.address;
QTC_ASSERT(stackRowCount() > 0, return 0);
return frameAt(0).address;
}
void StackHandler::setCurrentIndex(int level)
@@ -199,28 +204,39 @@ void StackHandler::setCurrentIndex(int level)
void StackHandler::removeAll()
{
clear();
auto threadItem = dummyThreadItem();
QTC_ASSERT(threadItem, return);
threadItem->removeChildren();
setCurrentIndex(-1);
}
bool StackHandler::operatesByInstruction() const
{
return m_engine->operatesByInstruction();
}
void StackHandler::setFrames(const StackFrames &frames, bool canExpand)
{
clear();
auto threadItem = dummyThreadItem();
QTC_ASSERT(threadItem, return);
threadItem->removeChildren();
m_resetLocationScheduled = false;
m_contentsValid = true;
m_canExpand = canExpand;
int row = 0;
for (const StackFrame &frame : frames)
rootItem()->appendChild(new StackFrameItem(frame));
threadItem->appendChild(new StackFrameItem(this, frame, row++));
if (canExpand)
rootItem()->appendChild(new StackFrameItem(StackFrame()));
threadItem->appendChild(new SpecialStackItem(this));
if (rowCount() >= 0)
setCurrentIndex(0);
else
if (frames.isEmpty())
m_currentIndex = -1;
else
setCurrentIndex(0);
emit stackChanged();
}
@@ -268,9 +284,11 @@ void StackHandler::prependFrames(const StackFrames &frames)
{
if (frames.isEmpty())
return;
auto threadItem = dummyThreadItem();
QTC_ASSERT(threadItem, return);
const int count = frames.size();
for (int i = count - 1; i >= 0; --i)
rootItem()->prependChild(new StackFrameItem(frames.at(i)));
threadItem->prependChild(new StackFrameItem(this, frames.at(i)));
if (m_currentIndex >= 0)
setCurrentIndex(m_currentIndex + count);
emit stackChanged();
@@ -278,14 +296,14 @@ void StackHandler::prependFrames(const StackFrames &frames)
bool StackHandler::isSpecialFrame(int index) const
{
return m_canExpand && index + 1 == rowCount();
return m_canExpand && index + 1 == stackRowCount();
}
int StackHandler::firstUsableIndex() const
{
if (!m_engine->operatesByInstruction()) {
for (int i = 0, n = rowCount(); i != n; ++i)
if (rootItem()->childAt(i)->frame.isUsable())
for (int i = 0, n = stackRowCount(); i != n; ++i)
if (frameAt(i).isUsable())
return i;
}
return 0;
@@ -293,17 +311,15 @@ int StackHandler::firstUsableIndex() const
void StackHandler::scheduleResetLocation()
{
m_resetLocationScheduled = true;
m_contentsValid = false;
}
void StackHandler::resetLocation()
int StackHandler::stackRowCount() const
{
if (m_resetLocationScheduled) {
beginResetModel();
m_resetLocationScheduled = false;
endResetModel();
}
// Only one "thread" for now.
auto threadItem = dummyThreadItem();
QTC_ASSERT(threadItem, return 0);
return threadItem->childCount();
}
// Input a function to be disassembled. Accept CDB syntax
@@ -353,7 +369,7 @@ void StackHandler::saveTaskFile()
}
QTextStream str(&file);
forItemsAtLevel<1>([&str](StackFrameItem *item) {
forItemsAtLevel<2>([&str](StackFrameItem *item) {
const StackFrame &frame = item->frame;
if (frame.isUsable())
str << frame.file << '\t' << frame.line << "\tstack\tFrame #" << frame.level << '\n';

View File

@@ -34,6 +34,7 @@ namespace Debugger {
namespace Internal {
class DebuggerEngine;
class StackHandler;
enum StackColumns
{
@@ -48,14 +49,41 @@ enum StackColumns
class StackFrameItem : public Utils::TreeItem
{
public:
StackFrameItem();
explicit StackFrameItem(const StackFrame &frame) : frame(frame) {}
StackFrameItem(StackHandler *handler, const StackFrame &frame, int row = -1)
: handler(handler), frame(frame), row(row)
{}
QVariant data(int column, int role) const override;
Qt::ItemFlags flags(int column) const override;
public:
StackHandler *handler = nullptr;
StackFrame frame;
int row = -1;
};
using StackHandlerModel = Utils::TreeModel<Utils::TypedTreeItem<StackFrameItem>, StackFrameItem>;
class SpecialStackItem : public StackFrameItem
{
public:
SpecialStackItem(StackHandler *handler)
: StackFrameItem(handler, StackFrame{})
{}
QVariant data(int column, int role) const override;
};
// FIXME: Move ThreadItem over here.
class ThreadDummyItem : public Utils::TypedTreeItem<StackFrameItem>
{
public:
};
using StackHandlerModel = Utils::TreeModel<
Utils::TypedTreeItem<ThreadDummyItem>,
Utils::TypedTreeItem<StackFrameItem>,
StackFrameItem
>;
class StackHandler : public StackHandlerModel
{
@@ -82,18 +110,20 @@ public:
void removeAll();
QAbstractItemModel *model() { return this; }
bool isContentsValid() const { return m_contentsValid; }
bool operatesByInstruction() const;
void scheduleResetLocation();
void resetLocation();
void resetModel() { beginResetModel(); endResetModel(); }
QIcon iconForRow(int row) const;
signals:
void stackChanged();
void currentIndexChanged();
private:
QVariant data(const QModelIndex &index, int role) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
int stackRowCount() const; // Including the <more...> "frame"
bool setData(const QModelIndex &idx, const QVariant &data, int role) override;
ThreadDummyItem *dummyThreadItem() const;
bool contextMenuEvent(const Utils::ItemViewEvent &event);
void reloadFullStack();
@@ -103,7 +133,6 @@ private:
DebuggerEngine *m_engine;
int m_currentIndex = -1;
bool m_canExpand = false;
bool m_resetLocationScheduled = false;
bool m_contentsValid = false;
};

View File

@@ -49,6 +49,10 @@ StackTreeView::StackTreeView(QWidget *parent)
void StackTreeView::setModel(QAbstractItemModel *model)
{
BaseTreeView::setModel(model);
if (model)
setRootIndex(model->index(0, 0, QModelIndex()));
connect(static_cast<StackHandler*>(model), &StackHandler::stackChanged,
this, [this]() {
if (!m_contentsAdjusted)

View File

@@ -2640,10 +2640,6 @@ void WatchHandler::scheduleResetLocation()
m_model->m_contentsValid = false;
}
void WatchHandler::resetLocation()
{
}
void WatchHandler::setCurrentItem(const QString &iname)
{
if (WatchItem *item = m_model->findItem(iname)) {

View File

@@ -101,7 +101,6 @@ public:
QString watcherName(const QString &exp);
void scheduleResetLocation();
void resetLocation();
void setCurrentItem(const QString &iname);
void updateLocalsWindow();