Files
qt-creator/src/plugins/debugger/moduleshandler.cpp
Lucie Gérard a7956df3ca Use SPDX license identifiers
Replace the current license disclaimer in files by
a SPDX-License-Identifier.

Task-number: QTBUG-67283
Change-Id: I708fd1f9f2b73d60f57cc3568646929117825813
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
2022-08-26 12:27:18 +00:00

311 lines
10 KiB
C++

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "moduleshandler.h"
#include "debuggeractions.h"
#include "debuggerconstants.h"
#include "debuggercore.h"
#include "debuggerengine.h"
#include "debuggertr.h"
#include <utils/basetreeview.h>
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/treemodel.h>
#include <QCoreApplication>
#include <QDebug>
#include <QMenu>
#include <QSortFilterProxyModel>
#include <functional>
using namespace Utils;
namespace Debugger::Internal {
class ModuleItem : public TreeItem
{
public:
QVariant data(int column, int role) const override;
public:
Module module;
bool updated;
};
QVariant ModuleItem::data(int column, int role) const
{
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 Tr::tr("Unknown");
case Module::ReadFailed: return Tr::tr("No");
case Module::ReadOk: return Tr::tr("Yes");
}
break;
case 3:
if (role == Qt::DisplayRole)
switch (module.elfData.symbolsType) {
case UnknownSymbols: return Tr::tr("Unknown");
case NoSymbols: return Tr::tr("None");
case PlainSymbols: return Tr::tr("Plain");
case FastSymbols: return Tr::tr("Fast");
case LinkedSymbols: return Tr::tr("debuglnk");
case BuildIdSymbols: return Tr::tr("buildid");
}
else if (role == Qt::ToolTipRole)
switch (module.elfData.symbolsType) {
case UnknownSymbols:
return Tr::tr("It is unknown whether this module contains debug "
"information.\nUse \"Examine Symbols\" from the "
"context menu to initiate a check.");
case NoSymbols:
return Tr::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:
case FastSymbols:
return Tr::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 Tr::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("0x" + QString::number(module.startAddress, 16));
break;
case 5:
if (role == Qt::DisplayRole) {
if (module.endAddress)
return QString("0x" + QString::number(module.endAddress, 16));
//: End address of loaded module
return Tr::tr("<unknown>", "address");
}
break;
}
return QVariant();
}
//////////////////////////////////////////////////////////////////
//
// ModulesModel
//
//////////////////////////////////////////////////////////////////
class ModulesModel : public TreeModel<TypedTreeItem<ModuleItem>, ModuleItem>
{
public:
bool setData(const QModelIndex &idx, const QVariant &data, int role) override
{
if (role == BaseTreeView::ItemViewEventRole) {
ItemViewEvent ev = data.value<ItemViewEvent>();
if (ev.type() == QEvent::ContextMenu)
return contextMenuEvent(ev);
}
return TreeModel::setData(idx, data, role);
}
bool contextMenuEvent(const ItemViewEvent &ev);
DebuggerEngine *engine;
};
bool ModulesModel::contextMenuEvent(const ItemViewEvent &ev)
{
ModuleItem *item = itemForIndexAtLevel<1>(ev.sourceModelIndex());
const bool enabled = engine->debuggerActionsEnabled();
const bool canReload = engine->hasCapability(ReloadModuleCapability);
const bool canLoadSymbols = engine->hasCapability(ReloadModuleSymbolsCapability);
const bool canShowSymbols = engine->hasCapability(ShowModuleSymbolsCapability);
const bool moduleNameValid = item && !item->module.moduleName.isEmpty();
const QString moduleName = item ? item->module.moduleName : QString();
const QString modulePath = item ? item->module.modulePath : QString();
auto menu = new QMenu;
addAction(this, menu, Tr::tr("Update Module List"),
enabled && canReload,
[this] { engine->reloadModules(); });
addAction(this, menu, Tr::tr("Show Source Files for Module \"%1\"").arg(moduleName),
Tr::tr("Show Source Files for Module"),
moduleNameValid && enabled && canReload,
[this, modulePath] { engine->loadSymbols(modulePath); });
// FIXME: Dependencies only available on Windows, when "depends" is installed.
addAction(this, menu, Tr::tr("Show Dependencies of \"%1\"").arg(moduleName),
Tr::tr("Show Dependencies"),
moduleNameValid && !moduleName.isEmpty() && HostOsInfo::isWindowsHost(),
[modulePath] { QtcProcess::startDetached({{"depends"}, {modulePath}}); });
addAction(this, menu, Tr::tr("Load Symbols for All Modules"),
enabled && canLoadSymbols,
[this] { engine->loadAllSymbols(); });
addAction(this, menu, Tr::tr("Examine All Modules"),
enabled && canLoadSymbols,
[this] { engine->examineModules(); });
addAction(this, menu, Tr::tr("Load Symbols for Module \"%1\"").arg(moduleName),
Tr::tr("Load Symbols for Module"),
moduleNameValid && canLoadSymbols,
[this, modulePath] { engine->loadSymbols(modulePath); });
addAction(this, menu, Tr::tr("Edit File \"%1\"").arg(moduleName),
Tr::tr("Edit File"),
moduleNameValid,
[this, modulePath] { engine->gotoLocation(FilePath::fromString(modulePath)); });
addAction(this, menu, Tr::tr("Show Symbols in File \"%1\"").arg(moduleName),
Tr::tr("Show Symbols"),
canShowSymbols && moduleNameValid,
[this, modulePath] { engine->requestModuleSymbols(modulePath); });
addAction(this, menu, Tr::tr("Show Sections in File \"%1\"").arg(moduleName),
Tr::tr("Show Sections"),
canShowSymbols && moduleNameValid,
[this, modulePath] { engine->requestModuleSections(modulePath); });
menu->addAction(debuggerSettings()->settingsDialog.action());
connect(menu, &QMenu::aboutToHide, menu, &QObject::deleteLater);
menu->popup(ev.globalPos());
return true;
}
//////////////////////////////////////////////////////////////////
//
// ModulesHandler
//
//////////////////////////////////////////////////////////////////
ModulesHandler::ModulesHandler(DebuggerEngine *engine)
{
QString pad = " ";
m_model = new ModulesModel;
m_model->engine = engine;
m_model->setObjectName("ModulesModel");
m_model->setHeader(QStringList({
Tr::tr("Module Name") + pad,
Tr::tr("Module Path") + pad,
Tr::tr("Symbols Read") + pad,
Tr::tr("Symbols Type") + pad,
Tr::tr("Start Address") + pad,
Tr::tr("End Address") + pad}));
m_proxyModel = new QSortFilterProxyModel(this);
m_proxyModel->setObjectName("ModulesProxyModel");
m_proxyModel->setSourceModel(m_model);
}
ModulesHandler::~ModulesHandler()
{
delete m_model;
}
QAbstractItemModel *ModulesHandler::model() const
{
return m_proxyModel;
}
ModuleItem *ModulesHandler::moduleFromPath(const QString &modulePath) const
{
// Recent modules are more likely to be unloaded first.
return m_model->findItemAtLevel<1>([modulePath](ModuleItem *item) {
return item->module.modulePath == modulePath;
});
}
void ModulesHandler::removeAll()
{
m_model->clear();
}
const Modules ModulesHandler::modules() const
{
Modules mods;
m_model->forItemsAtLevel<1>([&mods](ModuleItem *item) { mods.append(item->module); });
return mods;
}
void ModulesHandler::removeModule(const QString &modulePath)
{
if (ModuleItem *item = moduleFromPath(modulePath))
m_model->destroyItem(item);
}
void ModulesHandler::updateModule(const Module &module)
{
const QString path = module.modulePath;
if (path.isEmpty())
return;
ModuleItem *item = moduleFromPath(path);
if (item) {
item->module = module;
} else {
item = new ModuleItem;
item->module = module;
m_model->rootItem()->appendChild(item);
}
try { // MinGW occasionallly throws std::bad_alloc.
ElfReader reader(FilePath::fromString(path));
item->module.elfData = reader.readHeaders();
item->update();
} catch(...) {
qWarning("%s: An exception occurred while reading module '%s'",
Q_FUNC_INFO, qPrintable(module.modulePath));
}
item->updated = true;
}
void ModulesHandler::beginUpdateAll()
{
m_model->forItemsAtLevel<1>([](ModuleItem *item) { item->updated = false; });
}
void ModulesHandler::endUpdateAll()
{
QList<TreeItem *> toDestroy;
m_model->forItemsAtLevel<1>([&toDestroy](ModuleItem *item) {
if (!item->updated)
toDestroy.append(item);
});
for (TreeItem *item : toDestroy)
m_model->destroyItem(item);
}
} // namespace Debugger::Internal