Commit a8ea8d38 authored by hjk's avatar hjk
Browse files

Debugger: Use TreeModel for ModulesHandler



Change-Id: I831f71e7441330e2a6a7f3ddcf89a29517b3b91b
Reviewed-by: default avatarhjk <hjk@theqtcompany.com>
parent 54fda55b
......@@ -1858,8 +1858,8 @@ void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
GdbMi value;
value.fromString(reply->reply);
if (value.type() == GdbMi::List) {
Modules modules;
modules.reserve(value.childCount());
ModulesHandler *handler = modulesHandler();
handler->beginUpdateAll();
foreach (const GdbMi &gdbmiModule, value.children()) {
Module module;
module.moduleName = QString::fromLatin1(gdbmiModule["name"].data());
......@@ -1868,9 +1868,9 @@ void CdbEngine::handleModules(const CdbExtensionCommandPtr &reply)
module.endAddress = gdbmiModule["end"].data().toULongLong(0, 0);
if (gdbmiModule["deferred"].type() == GdbMi::Invalid)
module.symbolsRead = Module::ReadOk;
modules.push_back(module);
handler->updateModule(module);
}
modulesHandler()->setModules(modules);
handler->endUpdateAll();
} else {
showMessage(QString::fromLatin1("Parse error in modules response."), LogError);
qWarning("Parse error in modules response:\n%s", reply->reply.constData());
......
......@@ -771,7 +771,8 @@ void LldbEngine::reloadModules()
void LldbEngine::refreshModules(const GdbMi &modules)
{
Modules mods;
ModulesHandler *handler = modulesHandler();
handler->beginUpdateAll();
foreach (const GdbMi &item, modules.children()) {
Module module;
module.modulePath = item["file"].toUtf8();
......@@ -779,9 +780,9 @@ void LldbEngine::refreshModules(const GdbMi &modules)
module.symbolsRead = Module::UnknownReadState;
module.startAddress = item["loaded_addr"].toAddress();
module.endAddress = 0; // FIXME: End address not easily available.
mods.append(module);
handler->updateModule(module);
}
modulesHandler()->setModules(mods);
handler->endUpdateAll();
}
void LldbEngine::requestModuleSymbols(const QString &moduleName)
......
......@@ -31,7 +31,9 @@
#include "moduleshandler.h"
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <QCoreApplication>
#include <QDebug>
#include <QSortFilterProxyModel>
......@@ -46,211 +48,109 @@ using namespace Utils;
namespace Debugger {
namespace Internal {
class ModulesModel : public QAbstractItemModel
class ModuleItem : public TreeItem
{
public:
explicit ModulesModel(QObject *parent)
: QAbstractItemModel(parent)
{}
QVariant data(int column, int role) const;
int columnCount(const QModelIndex &parent) const
{ return parent.isValid() ? 0 : 5; }
int rowCount(const QModelIndex &parent) const
{ return parent.isValid() ? 0 : m_modules.size(); }
QModelIndex parent(const QModelIndex &) const { return QModelIndex(); }
QModelIndex index(int row, int column, const QModelIndex &) const
{ return createIndex(row, column); }
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QVariant data(const QModelIndex &index, int role) const;
void clearModel();
void removeModule(const QString &modulePath);
void setModules(const Modules &modules);
void updateModule(const Module &module);
int indexOfModule(const QString &modulePath) const;
Modules m_modules;
public:
Module module;
bool updated;
};
QVariant ModulesModel::headerData(int section,
Qt::Orientation orientation, int role) const
QVariant ModuleItem::data(int column, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
static QString headers[] = {
ModulesHandler::tr("Module Name") + QLatin1String(" "),
ModulesHandler::tr("Module Path") + QLatin1String(" "),
ModulesHandler::tr("Symbols Read") + QLatin1String(" "),
ModulesHandler::tr("Symbols Type") + QLatin1String(" "),
ModulesHandler::tr("Start Address") + QLatin1String(" "),
ModulesHandler::tr("End Address") + QLatin1String(" ")
};
return headers[section];
}
return QVariant();
}
QVariant ModulesModel::data(const QModelIndex &index, int role) const
{
int row = index.row();
if (row < 0 || row >= m_modules.size())
return QVariant();
const Module &module = m_modules.at(row);
switch (index.column()) {
case 0:
if (role == Qt::DisplayRole)
return module.moduleName;
// FIXME: add icons
//if (role == Qt::DecorationRole)
// return module.symbolsRead ? icon2 : icon;
break;
case 1:
if (role == Qt::DisplayRole)
return module.modulePath;
if (role == Qt::ToolTipRole) {
QString msg;
if (!module.elfData.buildId.isEmpty())
msg += QString::fromLatin1("Build Id: " + module.elfData.buildId);
if (!module.elfData.debugLink.isEmpty())
msg += QString::fromLatin1("Debug Link: " + module.elfData.debugLink);
return msg;
switch (column) {
case 0:
if (role == Qt::DisplayRole)
return module.moduleName;
// FIXME: add icons
//if (role == Qt::DecorationRole)
// return module.symbolsRead ? icon2 : icon;
break;
case 1:
if (role == Qt::DisplayRole)
return module.modulePath;
if (role == Qt::ToolTipRole) {
QString msg;
if (!module.elfData.buildId.isEmpty())
msg += QString::fromLatin1("Build Id: " + module.elfData.buildId);
if (!module.elfData.debugLink.isEmpty())
msg += QString::fromLatin1("Debug Link: " + module.elfData.debugLink);
return msg;
}
break;
case 2:
if (role == Qt::DisplayRole)
switch (module.symbolsRead) {
case Module::UnknownReadState: return ModulesHandler::tr("Unknown");
case Module::ReadFailed: return ModulesHandler::tr("No");
case Module::ReadOk: return ModulesHandler::tr("Yes");
}
break;
case 2:
if (role == Qt::DisplayRole)
switch (module.symbolsRead) {
case Module::UnknownReadState: return ModulesHandler::tr("Unknown");
case Module::ReadFailed: return ModulesHandler::tr("No");
case Module::ReadOk: return ModulesHandler::tr("Yes");
}
break;
case 3:
if (role == Qt::DisplayRole)
switch (module.elfData.symbolsType) {
case UnknownSymbols:
return ModulesHandler::tr("Unknown");
case NoSymbols:
return ModulesHandler::tr("None");
case PlainSymbols:
return ModulesHandler::tr("Plain");
case FastSymbols:
return ModulesHandler::tr("Fast");
case LinkedSymbols:
return ModulesHandler::tr("debuglnk");
case BuildIdSymbols:
return ModulesHandler::tr("buildid");
}
else if (role == Qt::ToolTipRole)
switch (module.elfData.symbolsType) {
case UnknownSymbols:
return ModulesHandler::tr(
"It is unknown whether this module contains debug "
"information.\nUse \"Examine Symbols\" from the "
"context menu to initiate a check.");
case NoSymbols:
return ModulesHandler::tr(
"This module neither contains nor references debug "
"information.\nStepping into the module or setting "
"breakpoints by file and line will not work.");
case PlainSymbols:
return ModulesHandler::tr(
"This module contains debug information.\nStepping "
"into the module or setting breakpoints by file and "
"line is expected to work.");
case FastSymbols:
return ModulesHandler::tr(
"This module contains debug information.\nStepping "
"into the module or setting breakpoints by file and "
"line is expected to work.");
case LinkedSymbols:
case BuildIdSymbols:
return ModulesHandler::tr(
"This module does not contain debug information "
"itself, but contains a reference to external "
"debug information.");
}
break;
case 4:
if (role == Qt::DisplayRole)
if (module.startAddress)
return QString(QLatin1String("0x")
+ QString::number(module.startAddress, 16));
break;
case 5:
if (role == Qt::DisplayRole) {
if (module.endAddress)
return QString(QLatin1String("0x")
+ QString::number(module.endAddress, 16));
//: End address of loaded module
return ModulesHandler::tr("<unknown>", "address");
break;
case 3:
if (role == Qt::DisplayRole)
switch (module.elfData.symbolsType) {
case UnknownSymbols:
return ModulesHandler::tr("Unknown");
case NoSymbols:
return ModulesHandler::tr("None");
case PlainSymbols:
return ModulesHandler::tr("Plain");
case FastSymbols:
return ModulesHandler::tr("Fast");
case LinkedSymbols:
return ModulesHandler::tr("debuglnk");
case BuildIdSymbols:
return ModulesHandler::tr("buildid");
}
break;
}
return QVariant();
}
void ModulesModel::setModules(const Modules &m)
{
beginResetModel();
m_modules = m;
endResetModel();
}
void ModulesModel::clearModel()
{
if (!m_modules.isEmpty()) {
beginResetModel();
m_modules.clear();
endResetModel();
}
}
int ModulesModel::indexOfModule(const QString &modulePath) const
{
// Recent modules are more likely to be unloaded first.
for (int i = m_modules.size() - 1; i >= 0; i--)
if (m_modules.at(i).modulePath == modulePath)
return i;
return -1;
}
void ModulesModel::removeModule(const QString &modulePath)
{
const int row = indexOfModule(modulePath);
QTC_ASSERT(row != -1, return);
beginRemoveRows(QModelIndex(), row, row);
m_modules.remove(row);
endRemoveRows();
}
void ModulesModel::updateModule(const Module &module)
{
const int row = indexOfModule(module.modulePath);
const QString path = module.modulePath;
if (path.isEmpty())
return;
try { // MinGW occasionallly throws std::bad_alloc.
ElfReader reader(path);
ElfData elfData = reader.readHeaders();
if (row == -1) {
const int n = m_modules.size();
beginInsertRows(QModelIndex(), n, n);
m_modules.push_back(module);
m_modules.back().elfData = elfData;
endInsertRows();
} else {
m_modules[row] = module;
m_modules[row].elfData = elfData;
dataChanged(index(row, 0, QModelIndex()), index(row, 4, QModelIndex()));
else if (role == Qt::ToolTipRole)
switch (module.elfData.symbolsType) {
case UnknownSymbols:
return ModulesHandler::tr(
"It is unknown whether this module contains debug "
"information.\nUse \"Examine Symbols\" from the "
"context menu to initiate a check.");
case NoSymbols:
return ModulesHandler::tr(
"This module neither contains nor references debug "
"information.\nStepping into the module or setting "
"breakpoints by file and line will not work.");
case PlainSymbols:
return ModulesHandler::tr(
"This module contains debug information.\nStepping "
"into the module or setting breakpoints by file and "
"line is expected to work.");
case FastSymbols:
return ModulesHandler::tr(
"This module contains debug information.\nStepping "
"into the module or setting breakpoints by file and "
"line is expected to work.");
case LinkedSymbols:
case BuildIdSymbols:
return ModulesHandler::tr(
"This module does not contain debug information "
"itself, but contains a reference to external "
"debug information.");
}
break;
case 4:
if (role == Qt::DisplayRole)
if (module.startAddress)
return QString(QLatin1String("0x")
+ QString::number(module.startAddress, 16));
break;
case 5:
if (role == Qt::DisplayRole) {
if (module.endAddress)
return QString(QLatin1String("0x")
+ QString::number(module.endAddress, 16));
//: End address of loaded module
return ModulesHandler::tr("<unknown>", "address");
}
} catch(...) {
qWarning("%s: An exception occurred while reading module '%s'",
Q_FUNC_INFO, qPrintable(module.modulePath));
break;
}
return QVariant();
}
//////////////////////////////////////////////////////////////////
......@@ -259,11 +159,33 @@ void ModulesModel::updateModule(const Module &module)
//
//////////////////////////////////////////////////////////////////
static ModuleItem *moduleFromPath(TreeItem *root, const QString &modulePath)
{
// Recent modules are more likely to be unloaded first.
for (int i = root->rowCount(); --i >= 0; ) {
auto item = static_cast<ModuleItem *>(root->child(i));
if (item->module.modulePath == modulePath)
return item;
}
return 0;
}
ModulesHandler::ModulesHandler(DebuggerEngine *engine)
{
m_engine = engine;
m_model = new ModulesModel(this);
QString pad = QLatin1String(" ");
m_model = new TreeModel(this);
m_model->setObjectName(QLatin1String("ModulesModel"));
auto root = new TreeItem(QStringList()
<< ModulesHandler::tr("Module Name") + pad
<< ModulesHandler::tr("Module Path") + pad
<< ModulesHandler::tr("Symbols Read") + pad
<< ModulesHandler::tr("Symbols Type") + pad
<< ModulesHandler::tr("Start Address") + pad
<< ModulesHandler::tr("End Address") + pad);
m_model->setRootItem(root);
m_proxyModel = new QSortFilterProxyModel(this);
m_proxyModel->setObjectName(QLatin1String("ModulesProxyModel"));
m_proxyModel->setSourceModel(m_model);
......@@ -276,27 +198,65 @@ QAbstractItemModel *ModulesHandler::model() const
void ModulesHandler::removeAll()
{
m_model->clearModel();
m_model->removeAllSubItems(m_model->rootItem());
}
Modules ModulesHandler::modules() const
{
Modules mods;
TreeItem *root = m_model->rootItem();
for (int i = root->rowCount(); --i >= 0; )
mods.append(static_cast<ModuleItem *>(root->child(i))->module);
return mods;
}
void ModulesHandler::removeModule(const QString &modulePath)
{
m_model->removeModule(modulePath);
if (ModuleItem *item = moduleFromPath(m_model->rootItem(), modulePath))
m_model->removeItem(item);
}
void ModulesHandler::updateModule(const Module &module)
{
m_model->updateModule(module);
const QString path = module.modulePath;
if (path.isEmpty())
return;
ModuleItem *item = moduleFromPath(m_model->rootItem(), path);
if (item) {
item->module = module;
} else {
item = new ModuleItem;
item->module = module;
m_model->appendItem(m_model->rootItem(), item);
}
try { // MinGW occasionallly throws std::bad_alloc.
ElfReader reader(path);
item->module.elfData = reader.readHeaders();
m_model->updateItem(item);
} catch(...) {
qWarning("%s: An exception occurred while reading module '%s'",
Q_FUNC_INFO, qPrintable(module.modulePath));
}
item->updated = true;
}
void ModulesHandler::setModules(const Modules &modules)
void ModulesHandler::beginUpdateAll()
{
m_model->setModules(modules);
TreeItem *root = m_model->rootItem();
for (int i = root->rowCount(); --i >= 0; )
static_cast<ModuleItem *>(root->child(i))->updated = false;
}
Modules ModulesHandler::modules() const
void ModulesHandler::endUpdateAll()
{
return m_model->m_modules;
TreeItem *root = m_model->rootItem();
for (int i = root->rowCount(); --i >= 0; ) {
auto item = static_cast<ModuleItem *>(root->child(i));
if (!item->updated)
m_model->removeItem(item);
}
}
} // namespace Internal
......
......@@ -32,6 +32,7 @@
#define DEBUGGER_MODULESHANDLER_H
#include <utils/elfreader.h>
#include <utils/treemodel.h>
QT_BEGIN_NAMESPACE
class QAbstractItemModel;
......@@ -42,7 +43,6 @@ namespace Debugger {
namespace Internal {
class DebuggerEngine;
class ModulesModel;
//////////////////////////////////////////////////////////////////
//
......@@ -109,7 +109,6 @@ public:
typedef QVector<Module> Modules;
//////////////////////////////////////////////////////////////////
//
// ModulesHandler
......@@ -125,16 +124,18 @@ public:
QAbstractItemModel *model() const;
void setModules(const Modules &modules);
void removeModule(const QString &modulePath);
void updateModule(const Module &module);
Modules modules() const;
void beginUpdateAll();
void endUpdateAll();
void removeAll();
Modules modules() const;
private:
DebuggerEngine *m_engine;
ModulesModel *m_model;
Utils::TreeModel *m_model;
QSortFilterProxyModel *m_proxyModel;
};
......
......@@ -402,7 +402,8 @@ void PdbEngine::handleListModules(const PdbResponse &response)
{
GdbMi out;
out.fromString(response.data.trimmed());
Modules modules;
ModulesHandler *handler = modulesHandler();
handler->beginUpdateAll();
foreach (const GdbMi &item, out.children()) {
Module module;
module.moduleName = _(item["name"].data());
......@@ -417,9 +418,9 @@ void PdbEngine::handleListModules(const PdbResponse &response)
path = _("(builtin)");
}
module.modulePath = path;
modules.append(module);
handler->updateModule(module);
}
modulesHandler()->setModules(modules);
handler->endUpdateAll();
}
void PdbEngine::requestModuleSymbols(const QString &moduleName)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment