diff --git a/src/plugins/debugger/cdb/cdbengine.cpp b/src/plugins/debugger/cdb/cdbengine.cpp index 9f2474abf08..8710b8e6b2a 100644 --- a/src/plugins/debugger/cdb/cdbengine.cpp +++ b/src/plugins/debugger/cdb/cdbengine.cpp @@ -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()); diff --git a/src/plugins/debugger/lldb/lldbengine.cpp b/src/plugins/debugger/lldb/lldbengine.cpp index 57025b50cf9..5523020d6c4 100644 --- a/src/plugins/debugger/lldb/lldbengine.cpp +++ b/src/plugins/debugger/lldb/lldbengine.cpp @@ -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) diff --git a/src/plugins/debugger/moduleshandler.cpp b/src/plugins/debugger/moduleshandler.cpp index 0c5b828e737..f1df561f71e 100644 --- a/src/plugins/debugger/moduleshandler.cpp +++ b/src/plugins/debugger/moduleshandler.cpp @@ -31,7 +31,9 @@ #include "moduleshandler.h" #include +#include +#include #include #include @@ -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; - } - 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("", "address"); - } - 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())); + 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; } - } catch(...) { - qWarning("%s: An exception occurred while reading module '%s'", - Q_FUNC_INFO, qPrintable(module.modulePath)); + 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("", "address"); + } + 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(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(); -} - -void ModulesHandler::removeModule(const QString &modulePath) -{ - m_model->removeModule(modulePath); -} - -void ModulesHandler::updateModule(const Module &module) -{ - m_model->updateModule(module); -} - -void ModulesHandler::setModules(const Modules &modules) -{ - m_model->setModules(modules); + m_model->removeAllSubItems(m_model->rootItem()); } Modules ModulesHandler::modules() const { - return m_model->m_modules; + Modules mods; + TreeItem *root = m_model->rootItem(); + for (int i = root->rowCount(); --i >= 0; ) + mods.append(static_cast(root->child(i))->module); + return mods; +} + +void ModulesHandler::removeModule(const QString &modulePath) +{ + if (ModuleItem *item = moduleFromPath(m_model->rootItem(), modulePath)) + m_model->removeItem(item); +} + +void ModulesHandler::updateModule(const Module &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::beginUpdateAll() +{ + TreeItem *root = m_model->rootItem(); + for (int i = root->rowCount(); --i >= 0; ) + static_cast(root->child(i))->updated = false; +} + +void ModulesHandler::endUpdateAll() +{ + TreeItem *root = m_model->rootItem(); + for (int i = root->rowCount(); --i >= 0; ) { + auto item = static_cast(root->child(i)); + if (!item->updated) + m_model->removeItem(item); + } } } // namespace Internal diff --git a/src/plugins/debugger/moduleshandler.h b/src/plugins/debugger/moduleshandler.h index 68975429542..b706a438002 100644 --- a/src/plugins/debugger/moduleshandler.h +++ b/src/plugins/debugger/moduleshandler.h @@ -32,6 +32,7 @@ #define DEBUGGER_MODULESHANDLER_H #include +#include QT_BEGIN_NAMESPACE class QAbstractItemModel; @@ -42,7 +43,6 @@ namespace Debugger { namespace Internal { class DebuggerEngine; -class ModulesModel; ////////////////////////////////////////////////////////////////// // @@ -109,7 +109,6 @@ public: typedef QVector 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; }; diff --git a/src/plugins/debugger/pdb/pdbengine.cpp b/src/plugins/debugger/pdb/pdbengine.cpp index 43ea8668f92..708b0d3f9be 100644 --- a/src/plugins/debugger/pdb/pdbengine.cpp +++ b/src/plugins/debugger/pdb/pdbengine.cpp @@ -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)