forked from qt-creator/qt-creator
Debugger: Merge debug mode and analyze mode
On the user-visible side, only the 'Analyze' mode button disappears, and instead a combobox to switch between different tools in appears in the Debug mode toolbar. Internally, that's quite some re-organzition: The centralized 'Analyze mode is busy' flag is gone, allowing us to run e.g. ClangStaticAnalyzer and MemCheck in parallel. Analyzer tools and debugger now share the same mechanism to generate/load/save dock widgets. Analyzer tools now create and handle their own start/stop button when appropriate. In general, Analyzer tools can create/handle more than one run control at a time. Further consolidation is possible, e.g. RunControl state handling could be merged into the base ProjectExplorer::RunControl to avoid the still existing duplication in ~15 instances. Change-Id: I91e5940ebc4211f98056d507cf2f7b5f8efe7f07 Reviewed-by: Christian Stenger <christian.stenger@theqtcompany.com>
This commit is contained in:
@@ -35,7 +35,7 @@
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
using namespace Analyzer;
|
||||
using namespace Debugger;
|
||||
using namespace Valgrind;
|
||||
using namespace Valgrind::Internal;
|
||||
|
||||
@@ -48,7 +48,7 @@ CallgrindRunControl::CallgrindRunControl(ProjectExplorer::RunConfiguration *runC
|
||||
connect(m_runner.parser(), &Callgrind::Parser::parserDataReady,
|
||||
this, &CallgrindRunControl::slotFinished);
|
||||
connect(&m_runner, &Callgrind::CallgrindRunner::statusMessage,
|
||||
this, &AnalyzerManager::showPermanentStatusMessage);
|
||||
this, &Debugger::showPermanentStatusMessage);
|
||||
}
|
||||
|
||||
QStringList CallgrindRunControl::toolArguments() const
|
||||
@@ -89,10 +89,10 @@ ValgrindRunner * CallgrindRunControl::runner()
|
||||
return &m_runner;
|
||||
}
|
||||
|
||||
bool CallgrindRunControl::startEngine()
|
||||
void CallgrindRunControl::start()
|
||||
{
|
||||
appendMessage(tr("Profiling %1").arg(executable()) + QLatin1Char('\n'), Utils::NormalMessageFormat);
|
||||
return ValgrindRunControl::startEngine();
|
||||
return ValgrindRunControl::start();
|
||||
}
|
||||
|
||||
void CallgrindRunControl::dump()
|
||||
|
||||
@@ -41,7 +41,7 @@ class CallgrindRunControl : public ValgrindRunControl
|
||||
public:
|
||||
CallgrindRunControl(ProjectExplorer::RunConfiguration *runConfiguration);
|
||||
|
||||
bool startEngine() override;
|
||||
void start() override;
|
||||
|
||||
Valgrind::Callgrind::ParseData *takeParserData();
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <valgrind/valgrindplugin.h>
|
||||
#include <valgrind/valgrindsettings.h>
|
||||
|
||||
#include <debugger/debuggerconstants.h>
|
||||
#include <debugger/analyzer/analyzerconstants.h>
|
||||
#include <debugger/analyzer/analyzericons.h>
|
||||
#include <debugger/analyzer/analyzermanager.h>
|
||||
@@ -90,11 +91,12 @@
|
||||
#include <QToolBar>
|
||||
#include <QToolButton>
|
||||
|
||||
using namespace Analyzer;
|
||||
using namespace Debugger;
|
||||
using namespace Core;
|
||||
using namespace Valgrind::Callgrind;
|
||||
using namespace TextEditor;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace Valgrind {
|
||||
namespace Internal {
|
||||
@@ -108,7 +110,6 @@ public:
|
||||
~CallgrindTool();
|
||||
|
||||
ValgrindRunControl *createRunControl(RunConfiguration *runConfiguration);
|
||||
void createWidgets();
|
||||
|
||||
void setParseData(ParseData *data);
|
||||
CostDelegate::CostFormat costFormat() const;
|
||||
@@ -164,6 +165,7 @@ public:
|
||||
|
||||
void editorOpened(IEditor *);
|
||||
void requestContextMenu(TextEditorWidget *widget, int line, QMenu *menu);
|
||||
void updateRunActions();
|
||||
|
||||
public:
|
||||
DataModel m_dataModel;
|
||||
@@ -200,15 +202,17 @@ public:
|
||||
|
||||
QVector<CallgrindTextMark *> m_textMarks;
|
||||
|
||||
QAction *m_startAction = 0;
|
||||
QAction *m_stopAction = 0;
|
||||
QAction *m_loadExternalLogFile;
|
||||
QAction *m_dumpAction = 0;
|
||||
QAction *m_resetAction = 0;
|
||||
QAction *m_pauseAction = 0;
|
||||
|
||||
QString m_toggleCollectFunction;
|
||||
bool m_toolBusy = false;
|
||||
};
|
||||
|
||||
|
||||
CallgrindTool::CallgrindTool(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
@@ -231,6 +235,9 @@ CallgrindTool::CallgrindTool(QObject *parent)
|
||||
connect(EditorManager::instance(), &EditorManager::editorOpened,
|
||||
this, &CallgrindTool::editorOpened);
|
||||
|
||||
m_startAction = Debugger::createStartAction();
|
||||
m_stopAction = Debugger::createStopAction();
|
||||
|
||||
ActionDescription desc;
|
||||
desc.setToolTip(tr("Valgrind Function Profile uses the "
|
||||
"Callgrind tool to record function calls when a program runs."));
|
||||
@@ -243,13 +250,13 @@ CallgrindTool::CallgrindTool(QObject *parent)
|
||||
});
|
||||
desc.setToolMode(OptimizedMode);
|
||||
desc.setRunMode(CALLGRIND_RUN_MODE);
|
||||
desc.setMenuGroup(Analyzer::Constants::G_ANALYZER_TOOLS);
|
||||
AnalyzerManager::registerAction(CallgrindLocalActionId, desc);
|
||||
desc.setMenuGroup(Debugger::Constants::G_ANALYZER_TOOLS);
|
||||
Debugger::registerAction(CallgrindLocalActionId, desc, m_startAction);
|
||||
}
|
||||
|
||||
desc.setText(tr("Valgrind Function Profiler (External Application)"));
|
||||
desc.setPerspectiveId(CallgrindPerspectiveId);
|
||||
desc.setCustomToolStarter([this](ProjectExplorer::RunConfiguration *runConfig) {
|
||||
desc.setCustomToolStarter([this](RunConfiguration *runConfig) {
|
||||
StartRemoteDialog dlg;
|
||||
if (dlg.exec() != QDialog::Accepted)
|
||||
return;
|
||||
@@ -263,17 +270,17 @@ CallgrindTool::CallgrindTool(QObject *parent)
|
||||
rc->setDisplayName(runnable.executable);
|
||||
ProjectExplorerPlugin::startRunControl(rc, CALLGRIND_RUN_MODE);
|
||||
});
|
||||
desc.setMenuGroup(Analyzer::Constants::G_ANALYZER_REMOTE_TOOLS);
|
||||
AnalyzerManager::registerAction(CallgrindRemoteActionId, desc);
|
||||
desc.setMenuGroup(Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
|
||||
Debugger::registerAction(CallgrindRemoteActionId, desc);
|
||||
|
||||
// If there is a CppEditor context menu add our own context menu actions.
|
||||
if (ActionContainer *editorContextMenu =
|
||||
ActionManager::actionContainer(CppEditor::Constants::M_CONTEXT)) {
|
||||
Context analyzerContext = Context(Analyzer::Constants::C_ANALYZEMODE);
|
||||
Context analyzerContext = Context(Debugger::Constants::C_DEBUGMODE);
|
||||
editorContextMenu->addSeparator(analyzerContext);
|
||||
|
||||
auto action = new QAction(tr("Profile Costs of This Function and Its Callees"), this);
|
||||
action->setIcon(Analyzer::Icons::ANALYZER_CONTROL_START.icon());
|
||||
action->setIcon(Debugger::Icons::ANALYZER_CONTROL_START.icon());
|
||||
connect(action, &QAction::triggered, this,
|
||||
&CallgrindTool::handleShowCostsOfFunction);
|
||||
Command *cmd = ActionManager::registerAction(action, "Analyzer.Callgrind.ShowCostsOfFunction",
|
||||
@@ -283,7 +290,214 @@ CallgrindTool::CallgrindTool(QObject *parent)
|
||||
cmd->setAttribute(Command::CA_NonConfigurable);
|
||||
}
|
||||
|
||||
createWidgets();
|
||||
QSettings *coreSettings = ICore::settings();
|
||||
|
||||
//
|
||||
// DockWidgets
|
||||
//
|
||||
m_visualization = new Visualisation;
|
||||
m_visualization->setFrameStyle(QFrame::NoFrame);
|
||||
m_visualization->setObjectName(QLatin1String("Valgrind.CallgrindTool.Visualisation"));
|
||||
m_visualization->setWindowTitle(tr("Visualization"));
|
||||
m_visualization->setModel(&m_dataModel);
|
||||
connect(m_visualization, &Visualisation::functionActivated,
|
||||
this, &CallgrindTool::visualisationFunctionSelected);
|
||||
|
||||
m_callersView = new CostView;
|
||||
m_callersView->setObjectName(QLatin1String("Valgrind.CallgrindTool.CallersView"));
|
||||
m_callersView->setWindowTitle(tr("Callers"));
|
||||
m_callersView->setSettings(coreSettings, "Valgrind.CallgrindTool.CallersView");
|
||||
m_callersView->sortByColumn(CallModel::CostColumn);
|
||||
m_callersView->setFrameStyle(QFrame::NoFrame);
|
||||
// enable sorting
|
||||
m_callersProxy.setSourceModel(&m_callersModel);
|
||||
m_callersView->setModel(&m_callersProxy);
|
||||
m_callersView->hideColumn(CallModel::CalleeColumn);
|
||||
connect(m_callersView, &QAbstractItemView::activated,
|
||||
this, &CallgrindTool::callerFunctionSelected);
|
||||
|
||||
m_calleesView = new CostView;
|
||||
m_calleesView->setObjectName(QLatin1String("Valgrind.CallgrindTool.CalleesView"));
|
||||
m_calleesView->setWindowTitle(tr("Callees"));
|
||||
m_calleesView->setSettings(coreSettings, "Valgrind.CallgrindTool.CalleesView");
|
||||
m_calleesView->sortByColumn(CallModel::CostColumn);
|
||||
m_calleesView->setFrameStyle(QFrame::NoFrame);
|
||||
// enable sorting
|
||||
m_calleesProxy.setSourceModel(&m_calleesModel);
|
||||
m_calleesView->setModel(&m_calleesProxy);
|
||||
m_calleesView->hideColumn(CallModel::CallerColumn);
|
||||
connect(m_calleesView, &QAbstractItemView::activated,
|
||||
this, &CallgrindTool::calleeFunctionSelected);
|
||||
|
||||
m_flatView = new CostView;
|
||||
m_flatView->setObjectName(QLatin1String("Valgrind.CallgrindTool.FlatView"));
|
||||
m_flatView->setWindowTitle(tr("Functions"));
|
||||
m_flatView->setSettings(coreSettings, "Valgrind.CallgrindTool.FlatView");
|
||||
m_flatView->sortByColumn(DataModel::SelfCostColumn);
|
||||
m_flatView->setFrameStyle(QFrame::NoFrame);
|
||||
m_flatView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
m_flatView->setModel(&m_proxyModel);
|
||||
connect(m_flatView, &QAbstractItemView::activated,
|
||||
this, &CallgrindTool::dataFunctionSelected);
|
||||
|
||||
updateCostFormat();
|
||||
|
||||
//
|
||||
// Control Widget
|
||||
//
|
||||
|
||||
// load external log file
|
||||
auto action = m_loadExternalLogFile = new QAction(this);
|
||||
action->setIcon(Core::Icons::OPENFILE.icon());
|
||||
action->setToolTip(tr("Load External Log File"));
|
||||
connect(action, &QAction::triggered, this, &CallgrindTool::loadExternalLogFile);
|
||||
|
||||
// dump action
|
||||
m_dumpAction = action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::REDO.icon());
|
||||
//action->setText(tr("Dump"));
|
||||
action->setToolTip(tr("Request the dumping of profile information. This will update the Callgrind visualization."));
|
||||
connect(action, &QAction::triggered, this, &CallgrindTool::slotRequestDump);
|
||||
|
||||
// reset action
|
||||
m_resetAction = action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::RELOAD.icon());
|
||||
//action->setText(tr("Reset"));
|
||||
action->setToolTip(tr("Reset all event counters."));
|
||||
connect(action, &QAction::triggered, this, &CallgrindTool::resetRequested);
|
||||
|
||||
// pause action
|
||||
m_pauseAction = action = new QAction(this);
|
||||
action->setCheckable(true);
|
||||
action->setIcon(ProjectExplorer::Icons::INTERRUPT_SMALL.icon());
|
||||
//action->setText(tr("Ignore"));
|
||||
action->setToolTip(tr("Pause event logging. No events are counted which will speed up program execution during profiling."));
|
||||
connect(action, &QAction::toggled, this, &CallgrindTool::pauseToggled);
|
||||
|
||||
// navigation
|
||||
// go back
|
||||
m_goBack = action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::PREV.icon());
|
||||
action->setToolTip(tr("Go back one step in history. This will select the previously selected item."));
|
||||
connect(action, &QAction::triggered, &m_stackBrowser, &StackBrowser::goBack);
|
||||
|
||||
// go forward
|
||||
m_goNext = action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::NEXT.icon());
|
||||
action->setToolTip(tr("Go forward one step in history."));
|
||||
connect(action, &QAction::triggered, &m_stackBrowser, &StackBrowser::goNext);
|
||||
|
||||
// event selection
|
||||
m_eventCombo = new QComboBox;
|
||||
m_eventCombo->setToolTip(tr("Selects which events from the profiling data are shown and visualized."));
|
||||
connect(m_eventCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &CallgrindTool::setCostEvent);
|
||||
updateEventCombo();
|
||||
|
||||
ToolbarDescription toolbar;
|
||||
toolbar.addAction(m_startAction);
|
||||
toolbar.addAction(m_stopAction);
|
||||
toolbar.addAction(m_loadExternalLogFile);
|
||||
toolbar.addAction(m_dumpAction);
|
||||
toolbar.addAction(m_resetAction);
|
||||
toolbar.addAction(m_pauseAction);
|
||||
toolbar.addAction(m_goBack);
|
||||
toolbar.addAction(m_goNext);
|
||||
toolbar.addWidget(new Utils::StyledSeparator);
|
||||
toolbar.addWidget(m_eventCombo);
|
||||
|
||||
// Cost formatting
|
||||
{
|
||||
auto menu = new QMenu;
|
||||
auto group = new QActionGroup(this);
|
||||
|
||||
// Show costs as absolute numbers
|
||||
m_costAbsolute = new QAction(tr("Absolute Costs"), this);
|
||||
m_costAbsolute->setToolTip(tr("Show costs as absolute numbers."));
|
||||
m_costAbsolute->setCheckable(true);
|
||||
m_costAbsolute->setChecked(true);
|
||||
connect(m_costAbsolute, &QAction::toggled, this, &CallgrindTool::updateCostFormat);
|
||||
group->addAction(m_costAbsolute);
|
||||
menu->addAction(m_costAbsolute);
|
||||
|
||||
// Show costs in percentages
|
||||
m_costRelative = new QAction(tr("Relative Costs"), this);
|
||||
m_costRelative->setToolTip(tr("Show costs relative to total inclusive cost."));
|
||||
m_costRelative->setCheckable(true);
|
||||
connect(m_costRelative, &QAction::toggled, this, &CallgrindTool::updateCostFormat);
|
||||
group->addAction(m_costRelative);
|
||||
menu->addAction(m_costRelative);
|
||||
|
||||
// Show costs relative to parent
|
||||
m_costRelativeToParent = new QAction(tr("Relative Costs to Parent"), this);
|
||||
m_costRelativeToParent->setToolTip(tr("Show costs relative to parent functions inclusive cost."));
|
||||
m_costRelativeToParent->setCheckable(true);
|
||||
connect(m_costRelativeToParent, &QAction::toggled, this, &CallgrindTool::updateCostFormat);
|
||||
group->addAction(m_costRelativeToParent);
|
||||
menu->addAction(m_costRelativeToParent);
|
||||
|
||||
auto button = new QToolButton;
|
||||
button->setMenu(menu);
|
||||
button->setPopupMode(QToolButton::InstantPopup);
|
||||
button->setText(QLatin1String("$"));
|
||||
button->setToolTip(tr("Cost Format"));
|
||||
toolbar.addWidget(button);
|
||||
}
|
||||
|
||||
ValgrindGlobalSettings *settings = ValgrindPlugin::globalSettings();
|
||||
|
||||
// Cycle detection
|
||||
//action = new QAction(QLatin1String("Cycle Detection"), this); ///FIXME: icon
|
||||
action = m_cycleDetection = new QAction(QLatin1String("O"), this); ///FIXME: icon
|
||||
action->setToolTip(tr("Enable cycle detection to properly handle recursive or circular function calls."));
|
||||
action->setCheckable(true);
|
||||
connect(action, &QAction::toggled, &m_dataModel, &DataModel::enableCycleDetection);
|
||||
connect(action, &QAction::toggled, settings, &ValgrindGlobalSettings::setDetectCycles);
|
||||
|
||||
// Shorter template signature
|
||||
action = m_shortenTemplates = new QAction(QLatin1String("<>"), this);
|
||||
action->setToolTip(tr("This removes template parameter lists when displaying function names."));
|
||||
action->setCheckable(true);
|
||||
connect(action, &QAction::toggled, &m_dataModel, &DataModel::setShortenTemplates);
|
||||
connect(action, &QAction::toggled, settings, &ValgrindGlobalSettings::setShortenTemplates);
|
||||
|
||||
// Filtering
|
||||
action = m_filterProjectCosts = new QAction(tr("Show Project Costs Only"), this);
|
||||
action->setIcon(Core::Icons::FILTER.icon());
|
||||
action->setToolTip(tr("Show only profiling info that originated from this project source."));
|
||||
action->setCheckable(true);
|
||||
connect(action, &QAction::toggled, this, &CallgrindTool::handleFilterProjectCosts);
|
||||
|
||||
// Filter
|
||||
///FIXME: find workaround for https://bugreports.qt.io/browse/QTCREATORBUG-3247
|
||||
m_searchFilter = new QLineEdit;
|
||||
m_searchFilter->setPlaceholderText(tr("Filter..."));
|
||||
connect(m_searchFilter, &QLineEdit::textChanged,
|
||||
&m_updateTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
|
||||
|
||||
setCostFormat(settings->costFormat());
|
||||
enableCycleDetection(settings->detectCycles());
|
||||
|
||||
toolbar.addAction(m_cycleDetection);
|
||||
toolbar.addAction(m_shortenTemplates);
|
||||
toolbar.addAction(m_filterProjectCosts);
|
||||
toolbar.addWidget(m_searchFilter);
|
||||
Debugger::registerToolbar(CallgrindPerspectiveId, toolbar);
|
||||
|
||||
Debugger::registerPerspective(CallgrindPerspectiveId, { tr("Callgrind"), {
|
||||
{ CallgrindFlatDockId, m_flatView, {}, Perspective::SplitVertical },
|
||||
{ CallgrindCalleesDockId, m_calleesView, {}, Perspective::SplitVertical },
|
||||
{ CallgrindCallersDockId, m_callersView, CallgrindCalleesDockId, Perspective::SplitHorizontal },
|
||||
{ CallgrindVisualizationDockId, m_visualization, {}, Perspective::SplitVertical,
|
||||
false, Qt::RightDockWidgetArea }
|
||||
}});
|
||||
|
||||
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions,
|
||||
this, &CallgrindTool::updateRunActions);
|
||||
}
|
||||
|
||||
CallgrindTool::~CallgrindTool()
|
||||
@@ -521,34 +735,28 @@ void CallgrindTool::updateEventCombo()
|
||||
m_eventCombo->addItem(ParseData::prettyStringForEvent(event));
|
||||
}
|
||||
|
||||
static QToolButton *createToolButton(QAction *action)
|
||||
{
|
||||
QToolButton *button = new QToolButton;
|
||||
button->setDefaultAction(action);
|
||||
//button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
|
||||
return button;
|
||||
}
|
||||
|
||||
ValgrindRunControl *CallgrindTool::createRunControl(RunConfiguration *runConfiguration)
|
||||
{
|
||||
auto rc = new CallgrindRunControl(runConfiguration);
|
||||
auto runControl = new CallgrindRunControl(runConfiguration);
|
||||
|
||||
connect(rc, &CallgrindRunControl::parserDataReady, this, &CallgrindTool::takeParserDataFromRunControl);
|
||||
connect(rc, &AnalyzerRunControl::starting, this, &CallgrindTool::engineStarting);
|
||||
connect(rc, &RunControl::finished, this, &CallgrindTool::engineFinished);
|
||||
connect(runControl, &CallgrindRunControl::parserDataReady, this, &CallgrindTool::takeParserDataFromRunControl);
|
||||
connect(runControl, &AnalyzerRunControl::starting, this, &CallgrindTool::engineStarting);
|
||||
connect(runControl, &RunControl::finished, this, &CallgrindTool::engineFinished);
|
||||
|
||||
connect(this, &CallgrindTool::dumpRequested, rc, &CallgrindRunControl::dump);
|
||||
connect(this, &CallgrindTool::resetRequested, rc, &CallgrindRunControl::reset);
|
||||
connect(this, &CallgrindTool::pauseToggled, rc, &CallgrindRunControl::setPaused);
|
||||
connect(this, &CallgrindTool::dumpRequested, runControl, &CallgrindRunControl::dump);
|
||||
connect(this, &CallgrindTool::resetRequested, runControl, &CallgrindRunControl::reset);
|
||||
connect(this, &CallgrindTool::pauseToggled, runControl, &CallgrindRunControl::setPaused);
|
||||
|
||||
connect(m_stopAction, &QAction::triggered, runControl, [runControl] { runControl->stop(); });
|
||||
|
||||
// initialize run control
|
||||
rc->setPaused(m_pauseAction->isChecked());
|
||||
runControl->setPaused(m_pauseAction->isChecked());
|
||||
|
||||
// we may want to toggle collect for one function only in this run
|
||||
rc->setToggleCollectFunction(m_toggleCollectFunction);
|
||||
runControl->setToggleCollectFunction(m_toggleCollectFunction);
|
||||
m_toggleCollectFunction.clear();
|
||||
|
||||
QTC_ASSERT(m_visualization, return rc);
|
||||
QTC_ASSERT(m_visualization, return runControl);
|
||||
|
||||
// apply project settings
|
||||
if (runConfiguration) {
|
||||
@@ -560,241 +768,32 @@ ValgrindRunControl *CallgrindTool::createRunControl(RunConfiguration *runConfigu
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
||||
m_toolBusy = true;
|
||||
updateRunActions();
|
||||
|
||||
return runControl;
|
||||
}
|
||||
|
||||
void CallgrindTool::createWidgets()
|
||||
void CallgrindTool::updateRunActions()
|
||||
{
|
||||
QTC_ASSERT(!m_visualization, return);
|
||||
|
||||
QSettings *coreSettings = ICore::settings();
|
||||
|
||||
//
|
||||
// DockWidgets
|
||||
//
|
||||
m_visualization = new Visualisation;
|
||||
m_visualization->setFrameStyle(QFrame::NoFrame);
|
||||
m_visualization->setObjectName(QLatin1String("Valgrind.CallgrindTool.Visualisation"));
|
||||
m_visualization->setWindowTitle(tr("Visualization"));
|
||||
m_visualization->setModel(&m_dataModel);
|
||||
connect(m_visualization, &Visualisation::functionActivated,
|
||||
this, &CallgrindTool::visualisationFunctionSelected);
|
||||
|
||||
m_callersView = new CostView;
|
||||
m_callersView->setObjectName(QLatin1String("Valgrind.CallgrindTool.CallersView"));
|
||||
m_callersView->setWindowTitle(tr("Callers"));
|
||||
m_callersView->setSettings(coreSettings, "Valgrind.CallgrindTool.CallersView");
|
||||
m_callersView->sortByColumn(CallModel::CostColumn);
|
||||
m_callersView->setFrameStyle(QFrame::NoFrame);
|
||||
// enable sorting
|
||||
m_callersProxy.setSourceModel(&m_callersModel);
|
||||
m_callersView->setModel(&m_callersProxy);
|
||||
m_callersView->hideColumn(CallModel::CalleeColumn);
|
||||
connect(m_callersView, &QAbstractItemView::activated,
|
||||
this, &CallgrindTool::callerFunctionSelected);
|
||||
|
||||
m_calleesView = new CostView;
|
||||
m_calleesView->setObjectName(QLatin1String("Valgrind.CallgrindTool.CalleesView"));
|
||||
m_calleesView->setWindowTitle(tr("Callees"));
|
||||
m_calleesView->setSettings(coreSettings, "Valgrind.CallgrindTool.CalleesView");
|
||||
m_calleesView->sortByColumn(CallModel::CostColumn);
|
||||
m_calleesView->setFrameStyle(QFrame::NoFrame);
|
||||
// enable sorting
|
||||
m_calleesProxy.setSourceModel(&m_calleesModel);
|
||||
m_calleesView->setModel(&m_calleesProxy);
|
||||
m_calleesView->hideColumn(CallModel::CallerColumn);
|
||||
connect(m_calleesView, &QAbstractItemView::activated,
|
||||
this, &CallgrindTool::calleeFunctionSelected);
|
||||
|
||||
m_flatView = new CostView;
|
||||
m_flatView->setObjectName(QLatin1String("Valgrind.CallgrindTool.FlatView"));
|
||||
m_flatView->setWindowTitle(tr("Functions"));
|
||||
m_flatView->setSettings(coreSettings, "Valgrind.CallgrindTool.FlatView");
|
||||
m_flatView->sortByColumn(DataModel::SelfCostColumn);
|
||||
m_flatView->setFrameStyle(QFrame::NoFrame);
|
||||
m_flatView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
m_flatView->setModel(&m_proxyModel);
|
||||
connect(m_flatView, &QAbstractItemView::activated,
|
||||
this, &CallgrindTool::dataFunctionSelected);
|
||||
|
||||
updateCostFormat();
|
||||
|
||||
//
|
||||
// Control Widget
|
||||
//
|
||||
auto layout = new QHBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
auto widget = new QWidget;
|
||||
widget->setLayout(layout);
|
||||
|
||||
// load external log file
|
||||
auto action = new QAction(this);
|
||||
action->setIcon(Core::Icons::OPENFILE.icon());
|
||||
action->setToolTip(tr("Load External Log File"));
|
||||
connect(action, &QAction::triggered, this, &CallgrindTool::loadExternalLogFile);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_loadExternalLogFile = action;
|
||||
|
||||
// dump action
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::REDO.icon());
|
||||
//action->setText(tr("Dump"));
|
||||
action->setToolTip(tr("Request the dumping of profile information. This will update the Callgrind visualization."));
|
||||
connect(action, &QAction::triggered, this, &CallgrindTool::slotRequestDump);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_dumpAction = action;
|
||||
|
||||
// reset action
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::RELOAD.icon());
|
||||
//action->setText(tr("Reset"));
|
||||
action->setToolTip(tr("Reset all event counters."));
|
||||
connect(action, &QAction::triggered, this, &CallgrindTool::resetRequested);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_resetAction = action;
|
||||
|
||||
// pause action
|
||||
action = new QAction(this);
|
||||
action->setCheckable(true);
|
||||
action->setIcon(ProjectExplorer::Icons::INTERRUPT_SMALL.icon());
|
||||
//action->setText(tr("Ignore"));
|
||||
action->setToolTip(tr("Pause event logging. No events are counted which will speed up program execution during profiling."));
|
||||
connect(action, &QAction::toggled, this, &CallgrindTool::pauseToggled);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_pauseAction = action;
|
||||
|
||||
// navigation
|
||||
// go back
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::PREV.icon());
|
||||
action->setToolTip(tr("Go back one step in history. This will select the previously selected item."));
|
||||
connect(action, &QAction::triggered, &m_stackBrowser, &StackBrowser::goBack);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_goBack = action;
|
||||
|
||||
// go forward
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::NEXT.icon());
|
||||
action->setToolTip(tr("Go forward one step in history."));
|
||||
connect(action, &QAction::triggered, &m_stackBrowser, &StackBrowser::goNext);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_goNext = action;
|
||||
|
||||
layout->addWidget(new Utils::StyledSeparator);
|
||||
|
||||
// event selection
|
||||
m_eventCombo = new QComboBox;
|
||||
m_eventCombo->setToolTip(tr("Selects which events from the profiling data are shown and visualized."));
|
||||
connect(m_eventCombo, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &CallgrindTool::setCostEvent);
|
||||
updateEventCombo();
|
||||
layout->addWidget(m_eventCombo);
|
||||
|
||||
// Cost formatting
|
||||
{
|
||||
auto menu = new QMenu(layout->parentWidget());
|
||||
auto group = new QActionGroup(this);
|
||||
|
||||
// Show costs as absolute numbers
|
||||
m_costAbsolute = new QAction(tr("Absolute Costs"), this);
|
||||
m_costAbsolute->setToolTip(tr("Show costs as absolute numbers."));
|
||||
m_costAbsolute->setCheckable(true);
|
||||
m_costAbsolute->setChecked(true);
|
||||
connect(m_costAbsolute, &QAction::toggled, this, &CallgrindTool::updateCostFormat);
|
||||
group->addAction(m_costAbsolute);
|
||||
menu->addAction(m_costAbsolute);
|
||||
|
||||
// Show costs in percentages
|
||||
m_costRelative = new QAction(tr("Relative Costs"), this);
|
||||
m_costRelative->setToolTip(tr("Show costs relative to total inclusive cost."));
|
||||
m_costRelative->setCheckable(true);
|
||||
connect(m_costRelative, &QAction::toggled, this, &CallgrindTool::updateCostFormat);
|
||||
group->addAction(m_costRelative);
|
||||
menu->addAction(m_costRelative);
|
||||
|
||||
// Show costs relative to parent
|
||||
m_costRelativeToParent = new QAction(tr("Relative Costs to Parent"), this);
|
||||
m_costRelativeToParent->setToolTip(tr("Show costs relative to parent functions inclusive cost."));
|
||||
m_costRelativeToParent->setCheckable(true);
|
||||
connect(m_costRelativeToParent, &QAction::toggled, this, &CallgrindTool::updateCostFormat);
|
||||
group->addAction(m_costRelativeToParent);
|
||||
menu->addAction(m_costRelativeToParent);
|
||||
|
||||
auto button = new QToolButton;
|
||||
button->setMenu(menu);
|
||||
button->setPopupMode(QToolButton::InstantPopup);
|
||||
button->setText(QLatin1String("$"));
|
||||
button->setToolTip(tr("Cost Format"));
|
||||
layout->addWidget(button);
|
||||
if (m_toolBusy) {
|
||||
m_startAction->setEnabled(false);
|
||||
m_startAction->setToolTip(tr("A Valgrind Callgrind analysis is still in progress."));
|
||||
m_stopAction->setEnabled(true);
|
||||
} else {
|
||||
const bool projectUsable = SessionManager::startupProject() != 0;
|
||||
if (projectUsable) {
|
||||
m_startAction->setEnabled(true);
|
||||
m_startAction->setToolTip(tr("Start a Valgrind Callgrind analysis."));
|
||||
m_stopAction->setEnabled(false);
|
||||
} else {
|
||||
m_startAction->setEnabled(false);
|
||||
m_startAction->setToolTip(tr("Start a Valgrind Callgrind analysis."));
|
||||
m_stopAction->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
ValgrindGlobalSettings *settings = ValgrindPlugin::globalSettings();
|
||||
|
||||
// Cycle detection
|
||||
//action = new QAction(QLatin1String("Cycle Detection"), this); ///FIXME: icon
|
||||
action = new QAction(QLatin1String("O"), this); ///FIXME: icon
|
||||
action->setToolTip(tr("Enable cycle detection to properly handle recursive or circular function calls."));
|
||||
action->setCheckable(true);
|
||||
connect(action, &QAction::toggled, &m_dataModel, &DataModel::enableCycleDetection);
|
||||
connect(action, &QAction::toggled, settings, &ValgrindGlobalSettings::setDetectCycles);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_cycleDetection = action;
|
||||
|
||||
// Shorter template signature
|
||||
action = new QAction(QLatin1String("<>"), this);
|
||||
action->setToolTip(tr("This removes template parameter lists when displaying function names."));
|
||||
action->setCheckable(true);
|
||||
connect(action, &QAction::toggled, &m_dataModel, &DataModel::setShortenTemplates);
|
||||
connect(action, &QAction::toggled, settings, &ValgrindGlobalSettings::setShortenTemplates);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_shortenTemplates = action;
|
||||
|
||||
// Filtering
|
||||
action = new QAction(tr("Show Project Costs Only"), this);
|
||||
action->setIcon(Core::Icons::FILTER.icon());
|
||||
action->setToolTip(tr("Show only profiling info that originated from this project source."));
|
||||
action->setCheckable(true);
|
||||
connect(action, &QAction::toggled, this, &CallgrindTool::handleFilterProjectCosts);
|
||||
layout->addWidget(createToolButton(action));
|
||||
m_filterProjectCosts = action;
|
||||
|
||||
// Filter
|
||||
///FIXME: find workaround for https://bugreports.qt.io/browse/QTCREATORBUG-3247
|
||||
auto filter = new QLineEdit;
|
||||
filter->setPlaceholderText(tr("Filter..."));
|
||||
connect(filter, &QLineEdit::textChanged,
|
||||
&m_updateTimer, static_cast<void(QTimer::*)()>(&QTimer::start));
|
||||
layout->addWidget(filter);
|
||||
m_searchFilter = filter;
|
||||
|
||||
setCostFormat(settings->costFormat());
|
||||
enableCycleDetection(settings->detectCycles());
|
||||
|
||||
layout->addWidget(new Utils::StyledSeparator);
|
||||
layout->addStretch();
|
||||
|
||||
AnalyzerManager::registerDockWidget(CallgrindCallersDockId, m_callersView);
|
||||
AnalyzerManager::registerDockWidget(CallgrindFlatDockId, m_flatView);
|
||||
AnalyzerManager::registerDockWidget(CallgrindCalleesDockId, m_calleesView);
|
||||
AnalyzerManager::registerDockWidget(CallgrindVisualizationDockId, m_visualization);
|
||||
|
||||
AnalyzerManager::registerPerspective(CallgrindPerspectiveId, {
|
||||
{ CallgrindFlatDockId, Id(), Perspective::SplitVertical },
|
||||
{ CallgrindCalleesDockId, Id(), Perspective::SplitVertical },
|
||||
{ CallgrindCallersDockId, CallgrindCalleesDockId, Perspective::SplitHorizontal },
|
||||
{ CallgrindVisualizationDockId, Id(), Perspective::SplitVertical,
|
||||
false, Qt::RightDockWidgetArea }
|
||||
});
|
||||
|
||||
AnalyzerManager::registerToolbar(CallgrindPerspectiveId, widget);
|
||||
}
|
||||
|
||||
void CallgrindTool::clearTextMarks()
|
||||
{
|
||||
qDeleteAll(m_textMarks);
|
||||
@@ -813,6 +812,9 @@ void CallgrindTool::engineStarting()
|
||||
|
||||
void CallgrindTool::engineFinished()
|
||||
{
|
||||
m_toolBusy = false;
|
||||
updateRunActions();
|
||||
|
||||
// Enable/disable actions
|
||||
m_resetAction->setEnabled(false);
|
||||
m_dumpAction->setEnabled(false);
|
||||
@@ -822,7 +824,7 @@ void CallgrindTool::engineFinished()
|
||||
if (data)
|
||||
showParserResults(data);
|
||||
else
|
||||
AnalyzerManager::showPermanentStatusMessage(tr("Profiling aborted."));
|
||||
Debugger::showPermanentStatusMessage(tr("Profiling aborted."));
|
||||
|
||||
setBusyCursor(false);
|
||||
}
|
||||
@@ -841,7 +843,7 @@ void CallgrindTool::showParserResults(const ParseData *data)
|
||||
} else {
|
||||
msg = tr("Parsing failed.");
|
||||
}
|
||||
AnalyzerManager::showPermanentStatusMessage(msg);
|
||||
Debugger::showPermanentStatusMessage(msg);
|
||||
}
|
||||
|
||||
void CallgrindTool::editorOpened(IEditor *editor)
|
||||
@@ -878,8 +880,7 @@ void CallgrindTool::handleShowCostsOfFunction()
|
||||
const QString qualifiedFunctionName = view.prettyName(CPlusPlus::LookupContext::fullyQualifiedName(symbol));
|
||||
|
||||
m_toggleCollectFunction = qualifiedFunctionName + QLatin1String("()");
|
||||
|
||||
AnalyzerManager::selectAction(CallgrindLocalActionId, /* alsoRunIt = */ true);
|
||||
m_startAction->trigger();
|
||||
}
|
||||
|
||||
void CallgrindTool::slotRequestDump()
|
||||
@@ -906,7 +907,7 @@ void CallgrindTool::loadExternalLogFile()
|
||||
return;
|
||||
}
|
||||
|
||||
AnalyzerManager::showPermanentStatusMessage(tr("Parsing Profile Data..."));
|
||||
Debugger::showPermanentStatusMessage(tr("Parsing Profile Data..."));
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
Parser parser;
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
using namespace Analyzer;
|
||||
using namespace Debugger;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Valgrind::XmlProtocol;
|
||||
|
||||
@@ -72,20 +72,20 @@ ValgrindRunner *MemcheckRunControl::runner()
|
||||
return &m_runner;
|
||||
}
|
||||
|
||||
bool MemcheckRunControl::startEngine()
|
||||
void MemcheckRunControl::start()
|
||||
{
|
||||
m_runner.setParser(&m_parser);
|
||||
|
||||
appendMessage(tr("Analyzing memory of %1").arg(executable()) + QLatin1Char('\n'),
|
||||
Utils::NormalMessageFormat);
|
||||
return ValgrindRunControl::startEngine();
|
||||
ValgrindRunControl::start();
|
||||
}
|
||||
|
||||
void MemcheckRunControl::stopEngine()
|
||||
RunControl::StopResult MemcheckRunControl::stop()
|
||||
{
|
||||
disconnect(&m_parser, &ThreadedParser::internalError,
|
||||
this, &MemcheckRunControl::internalParserError);
|
||||
ValgrindRunControl::stopEngine();
|
||||
return ValgrindRunControl::stop();
|
||||
}
|
||||
|
||||
QStringList MemcheckRunControl::toolArguments() const
|
||||
|
||||
@@ -43,8 +43,8 @@ public:
|
||||
MemcheckRunControl(ProjectExplorer::RunConfiguration *runConfiguration,
|
||||
Core::Id runMode);
|
||||
|
||||
bool startEngine() override;
|
||||
void stopEngine() override;
|
||||
void start() override;
|
||||
StopResult stop() override;
|
||||
|
||||
QStringList suppressionFiles() const;
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ namespace Valgrind {
|
||||
namespace Internal {
|
||||
|
||||
MemcheckErrorView::MemcheckErrorView(QWidget *parent)
|
||||
: Analyzer::DetailedErrorView(parent),
|
||||
: Debugger::DetailedErrorView(parent),
|
||||
m_settings(0)
|
||||
{
|
||||
m_suppressAction = new QAction(this);
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Internal {
|
||||
|
||||
class ValgrindBaseSettings;
|
||||
|
||||
class MemcheckErrorView : public Analyzer::DetailedErrorView
|
||||
class MemcheckErrorView : public Debugger::DetailedErrorView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
||||
@@ -85,14 +85,60 @@
|
||||
#include <QString>
|
||||
#include <QToolButton>
|
||||
|
||||
using namespace Analyzer;
|
||||
using namespace Debugger;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
using namespace Valgrind::XmlProtocol;
|
||||
|
||||
namespace Valgrind {
|
||||
namespace Internal {
|
||||
|
||||
class FrameFinder;
|
||||
class FrameFinder : public ErrorListModel::RelevantFrameFinder
|
||||
{
|
||||
public:
|
||||
Frame findRelevant(const Error &error) const
|
||||
{
|
||||
const QVector<Stack> stacks = error.stacks();
|
||||
if (stacks.isEmpty())
|
||||
return Frame();
|
||||
const Stack &stack = stacks[0];
|
||||
const QVector<Frame> frames = stack.frames();
|
||||
if (frames.isEmpty())
|
||||
return Frame();
|
||||
|
||||
//find the first frame belonging to the project
|
||||
if (!m_projectFiles.isEmpty()) {
|
||||
foreach (const Frame &frame, frames) {
|
||||
if (frame.directory().isEmpty() || frame.fileName().isEmpty())
|
||||
continue;
|
||||
|
||||
//filepaths can contain "..", clean them:
|
||||
const QString f = QFileInfo(frame.filePath()).absoluteFilePath();
|
||||
if (m_projectFiles.contains(f))
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
||||
//if no frame belonging to the project was found, return the first one that is not malloc/new
|
||||
foreach (const Frame &frame, frames) {
|
||||
if (!frame.functionName().isEmpty() && frame.functionName() != QLatin1String("malloc")
|
||||
&& !frame.functionName().startsWith(QLatin1String("operator new(")))
|
||||
{
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
||||
//else fallback to the first frame
|
||||
return frames.first();
|
||||
}
|
||||
void setFiles(const QStringList &files)
|
||||
{
|
||||
m_projectFiles = files;
|
||||
}
|
||||
private:
|
||||
QStringList m_projectFiles;
|
||||
};
|
||||
|
||||
|
||||
class MemcheckErrorFilterProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
@@ -203,12 +249,13 @@ class MemcheckTool : public QObject
|
||||
public:
|
||||
MemcheckTool(QObject *parent);
|
||||
|
||||
QWidget *createWidgets();
|
||||
void createWidgets();
|
||||
|
||||
MemcheckRunControl *createRunControl(ProjectExplorer::RunConfiguration *runConfiguration,
|
||||
Core::Id runMode);
|
||||
|
||||
private:
|
||||
void updateRunActions();
|
||||
void settingsDestroyed(QObject *settings);
|
||||
void maybeActiveRunConfigurationChanged();
|
||||
|
||||
@@ -240,10 +287,14 @@ private:
|
||||
QList<QAction *> m_errorFilterActions;
|
||||
QAction *m_filterProjectAction;
|
||||
QList<QAction *> m_suppressionActions;
|
||||
QAction *m_startAction;
|
||||
QAction *m_startWithGdbAction;
|
||||
QAction *m_stopAction;
|
||||
QAction *m_suppressionSeparator;
|
||||
QAction *m_loadExternalLogFile;
|
||||
QAction *m_goBack;
|
||||
QAction *m_goNext;
|
||||
bool m_toolBusy = false;
|
||||
};
|
||||
|
||||
MemcheckTool::MemcheckTool(QObject *parent)
|
||||
@@ -288,7 +339,79 @@ MemcheckTool::MemcheckTool(QObject *parent)
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
AnalyzerManager::registerToolbar(MemcheckPerspectiveId, createWidgets());
|
||||
QTC_ASSERT(!m_errorView, return);
|
||||
|
||||
m_errorView = new MemcheckErrorView;
|
||||
m_errorView->setObjectName(QLatin1String("MemcheckErrorView"));
|
||||
m_errorView->setFrameStyle(QFrame::NoFrame);
|
||||
m_errorView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
m_errorModel = new ErrorListModel(m_errorView);
|
||||
m_frameFinder = new Internal::FrameFinder;
|
||||
m_errorModel->setRelevantFrameFinder(QSharedPointer<Internal::FrameFinder>(m_frameFinder));
|
||||
m_errorProxyModel = new MemcheckErrorFilterProxyModel(m_errorView);
|
||||
m_errorProxyModel->setSourceModel(m_errorModel);
|
||||
m_errorProxyModel->setDynamicSortFilter(true);
|
||||
m_errorView->setModel(m_errorProxyModel);
|
||||
m_errorView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
// make m_errorView->selectionModel()->selectedRows() return something
|
||||
m_errorView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_errorView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
m_errorView->setAutoScroll(false);
|
||||
m_errorView->setObjectName(QLatin1String("Valgrind.MemcheckTool.ErrorView"));
|
||||
m_errorView->setWindowTitle(tr("Memory Issues"));
|
||||
|
||||
Debugger::registerPerspective(MemcheckPerspectiveId, { tr("Memcheck"), {
|
||||
{ MemcheckErrorDockId, m_errorView, {}, Perspective::SplitVertical }
|
||||
}});
|
||||
|
||||
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions,
|
||||
this, &MemcheckTool::maybeActiveRunConfigurationChanged);
|
||||
|
||||
//
|
||||
// The Control Widget.
|
||||
//
|
||||
|
||||
m_startAction = Debugger::createStartAction();
|
||||
m_startWithGdbAction = Debugger::createStartAction();
|
||||
m_stopAction = Debugger::createStopAction();
|
||||
|
||||
// Load external XML log file
|
||||
auto action = new QAction(this);
|
||||
action->setIcon(Core::Icons::OPENFILE.icon());
|
||||
action->setToolTip(tr("Load External XML Log File"));
|
||||
connect(action, &QAction::triggered, this, &MemcheckTool::loadExternalXmlLogFile);
|
||||
m_loadExternalLogFile = action;
|
||||
|
||||
// Go to previous leak.
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::PREV.icon());
|
||||
action->setToolTip(tr("Go to previous leak."));
|
||||
connect(action, &QAction::triggered, m_errorView, &MemcheckErrorView::goBack);
|
||||
m_goBack = action;
|
||||
|
||||
// Go to next leak.
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::NEXT.icon());
|
||||
action->setToolTip(tr("Go to next leak."));
|
||||
connect(action, &QAction::triggered, m_errorView, &MemcheckErrorView::goNext);
|
||||
m_goNext = action;
|
||||
|
||||
auto filterButton = new QToolButton;
|
||||
filterButton->setIcon(Core::Icons::FILTER.icon());
|
||||
filterButton->setText(tr("Error Filter"));
|
||||
filterButton->setPopupMode(QToolButton::InstantPopup);
|
||||
filterButton->setProperty("noArrow", true);
|
||||
|
||||
m_filterMenu = new QMenu(filterButton);
|
||||
foreach (QAction *filterAction, m_errorFilterActions)
|
||||
m_filterMenu->addAction(filterAction);
|
||||
m_filterMenu->addSeparator();
|
||||
m_filterMenu->addAction(m_filterProjectAction);
|
||||
m_filterMenu->addAction(m_suppressionSeparator);
|
||||
connect(m_filterMenu, &QMenu::triggered, this, &MemcheckTool::updateErrorFilter);
|
||||
filterButton->setMenu(m_filterMenu);
|
||||
|
||||
ActionDescription desc;
|
||||
desc.setToolTip(tr("Valgrind Analyze Memory uses the "
|
||||
@@ -300,8 +423,8 @@ MemcheckTool::MemcheckTool(QObject *parent)
|
||||
desc.setRunControlCreator(std::bind(&MemcheckTool::createRunControl, this, _1, _2));
|
||||
desc.setToolMode(DebugMode);
|
||||
desc.setRunMode(MEMCHECK_RUN_MODE);
|
||||
desc.setMenuGroup(Analyzer::Constants::G_ANALYZER_TOOLS);
|
||||
AnalyzerManager::registerAction("Memcheck.Local", desc);
|
||||
desc.setMenuGroup(Debugger::Constants::G_ANALYZER_TOOLS);
|
||||
Debugger::registerAction("Memcheck.Local", desc, m_startAction);
|
||||
|
||||
desc.setText(tr("Valgrind Memory Analyzer with GDB"));
|
||||
desc.setToolTip(tr("Valgrind Analyze Memory with GDB uses the "
|
||||
@@ -311,8 +434,8 @@ MemcheckTool::MemcheckTool(QObject *parent)
|
||||
desc.setRunControlCreator(std::bind(&MemcheckTool::createRunControl, this, _1, _2));
|
||||
desc.setToolMode(DebugMode);
|
||||
desc.setRunMode(MEMCHECK_WITH_GDB_RUN_MODE);
|
||||
desc.setMenuGroup(Analyzer::Constants::G_ANALYZER_TOOLS);
|
||||
AnalyzerManager::registerAction("MemcheckWithGdb.Local", desc);
|
||||
desc.setMenuGroup(Debugger::Constants::G_ANALYZER_TOOLS);
|
||||
Debugger::registerAction("MemcheckWithGdb.Local", desc, m_startWithGdbAction);
|
||||
}
|
||||
|
||||
desc.setText(tr("Valgrind Memory Analyzer (External Application)"));
|
||||
@@ -331,8 +454,42 @@ MemcheckTool::MemcheckTool(QObject *parent)
|
||||
rc->setDisplayName(runnable.executable);
|
||||
ProjectExplorerPlugin::startRunControl(rc, MEMCHECK_RUN_MODE);
|
||||
});
|
||||
desc.setMenuGroup(Analyzer::Constants::G_ANALYZER_REMOTE_TOOLS);
|
||||
AnalyzerManager::registerAction("Memcheck.Remote", desc);
|
||||
desc.setMenuGroup(Debugger::Constants::G_ANALYZER_REMOTE_TOOLS);
|
||||
Debugger::registerAction("Memcheck.Remote", desc);
|
||||
|
||||
ToolbarDescription toolbar;
|
||||
toolbar.addAction(m_startAction);
|
||||
//toolbar.addAction(m_startWithGdbAction);
|
||||
toolbar.addAction(m_stopAction);
|
||||
toolbar.addAction(m_loadExternalLogFile);
|
||||
toolbar.addAction(m_goBack);
|
||||
toolbar.addAction(m_goNext);
|
||||
toolbar.addWidget(filterButton);
|
||||
Debugger::registerToolbar(MemcheckPerspectiveId, toolbar);
|
||||
}
|
||||
|
||||
void MemcheckTool::updateRunActions()
|
||||
{
|
||||
if (m_toolBusy) {
|
||||
m_startAction->setEnabled(false);
|
||||
m_startAction->setToolTip(tr("A Valgrind Memcheck analysis is still in progress."));
|
||||
m_startWithGdbAction->setEnabled(false);
|
||||
m_startWithGdbAction->setToolTip(tr("A Valgrind Memcheck analysis is still in progress."));
|
||||
m_stopAction->setEnabled(true);
|
||||
} else {
|
||||
const bool projectUsable = SessionManager::startupProject() != 0;
|
||||
m_startAction->setToolTip(tr("Start a Valgrind Memcheck analysis."));
|
||||
m_startWithGdbAction->setToolTip(tr("Start a Valgrind Memcheck with GDB analysis."));
|
||||
if (projectUsable) {
|
||||
m_startAction->setEnabled(true);
|
||||
m_startWithGdbAction->setEnabled(true);
|
||||
m_stopAction->setEnabled(false);
|
||||
} else {
|
||||
m_startAction->setEnabled(false);
|
||||
m_startWithGdbAction->setEnabled(false);
|
||||
m_stopAction->setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemcheckTool::settingsDestroyed(QObject *settings)
|
||||
@@ -368,6 +525,8 @@ void MemcheckTool::updateFromSettings()
|
||||
|
||||
void MemcheckTool::maybeActiveRunConfigurationChanged()
|
||||
{
|
||||
updateRunActions();
|
||||
|
||||
ValgrindBaseSettings *settings = 0;
|
||||
if (Project *project = SessionManager::startupProject())
|
||||
if (Target *target = project->activeTarget())
|
||||
@@ -395,150 +554,6 @@ void MemcheckTool::maybeActiveRunConfigurationChanged()
|
||||
updateFromSettings();
|
||||
}
|
||||
|
||||
class FrameFinder : public ErrorListModel::RelevantFrameFinder
|
||||
{
|
||||
public:
|
||||
Frame findRelevant(const Error &error) const
|
||||
{
|
||||
const QVector<Stack> stacks = error.stacks();
|
||||
if (stacks.isEmpty())
|
||||
return Frame();
|
||||
const Stack &stack = stacks[0];
|
||||
const QVector<Frame> frames = stack.frames();
|
||||
if (frames.isEmpty())
|
||||
return Frame();
|
||||
|
||||
//find the first frame belonging to the project
|
||||
if (!m_projectFiles.isEmpty()) {
|
||||
foreach (const Frame &frame, frames) {
|
||||
if (frame.directory().isEmpty() || frame.fileName().isEmpty())
|
||||
continue;
|
||||
|
||||
//filepaths can contain "..", clean them:
|
||||
const QString f = QFileInfo(frame.filePath()).absoluteFilePath();
|
||||
if (m_projectFiles.contains(f))
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
||||
//if no frame belonging to the project was found, return the first one that is not malloc/new
|
||||
foreach (const Frame &frame, frames) {
|
||||
if (!frame.functionName().isEmpty() && frame.functionName() != QLatin1String("malloc")
|
||||
&& !frame.functionName().startsWith(QLatin1String("operator new(")))
|
||||
{
|
||||
return frame;
|
||||
}
|
||||
}
|
||||
|
||||
//else fallback to the first frame
|
||||
return frames.first();
|
||||
}
|
||||
void setFiles(const QStringList &files)
|
||||
{
|
||||
m_projectFiles = files;
|
||||
}
|
||||
private:
|
||||
QStringList m_projectFiles;
|
||||
};
|
||||
|
||||
|
||||
QWidget *MemcheckTool::createWidgets()
|
||||
{
|
||||
QTC_ASSERT(!m_errorView, return 0);
|
||||
|
||||
m_errorView = new MemcheckErrorView;
|
||||
m_errorView->setObjectName(QLatin1String("MemcheckErrorView"));
|
||||
m_errorView->setFrameStyle(QFrame::NoFrame);
|
||||
m_errorView->setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
m_errorModel = new ErrorListModel(m_errorView);
|
||||
m_frameFinder = new Internal::FrameFinder;
|
||||
m_errorModel->setRelevantFrameFinder(QSharedPointer<Internal::FrameFinder>(m_frameFinder));
|
||||
m_errorProxyModel = new MemcheckErrorFilterProxyModel(m_errorView);
|
||||
m_errorProxyModel->setSourceModel(m_errorModel);
|
||||
m_errorProxyModel->setDynamicSortFilter(true);
|
||||
m_errorView->setModel(m_errorProxyModel);
|
||||
m_errorView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
// make m_errorView->selectionModel()->selectedRows() return something
|
||||
m_errorView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
m_errorView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
m_errorView->setAutoScroll(false);
|
||||
m_errorView->setObjectName(QLatin1String("Valgrind.MemcheckTool.ErrorView"));
|
||||
m_errorView->setWindowTitle(tr("Memory Issues"));
|
||||
|
||||
AnalyzerManager::registerDockWidget(MemcheckErrorDockId, m_errorView);
|
||||
|
||||
AnalyzerManager::registerPerspective(MemcheckPerspectiveId, {
|
||||
{ MemcheckErrorDockId, Core::Id(), Perspective::SplitVertical }
|
||||
});
|
||||
|
||||
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions,
|
||||
this, &MemcheckTool::maybeActiveRunConfigurationChanged);
|
||||
|
||||
//
|
||||
// The Control Widget.
|
||||
//
|
||||
QAction *action = 0;
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
QToolButton *button = 0;
|
||||
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
// Load external XML log file
|
||||
action = new QAction(this);
|
||||
action->setIcon(Core::Icons::OPENFILE.icon());
|
||||
action->setToolTip(tr("Load External XML Log File"));
|
||||
connect(action, &QAction::triggered, this, &MemcheckTool::loadExternalXmlLogFile);
|
||||
button = new QToolButton;
|
||||
button->setDefaultAction(action);
|
||||
layout->addWidget(button);
|
||||
m_loadExternalLogFile = action;
|
||||
|
||||
// Go to previous leak.
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::PREV.icon());
|
||||
action->setToolTip(tr("Go to previous leak."));
|
||||
connect(action, &QAction::triggered, m_errorView, &MemcheckErrorView::goBack);
|
||||
button = new QToolButton;
|
||||
button->setDefaultAction(action);
|
||||
layout->addWidget(button);
|
||||
m_goBack = action;
|
||||
|
||||
// Go to next leak.
|
||||
action = new QAction(this);
|
||||
action->setDisabled(true);
|
||||
action->setIcon(Core::Icons::NEXT.icon());
|
||||
action->setToolTip(tr("Go to next leak."));
|
||||
connect(action, &QAction::triggered, m_errorView, &MemcheckErrorView::goNext);
|
||||
button = new QToolButton;
|
||||
button->setDefaultAction(action);
|
||||
layout->addWidget(button);
|
||||
m_goNext = action;
|
||||
|
||||
QToolButton *filterButton = new QToolButton;
|
||||
filterButton->setIcon(Core::Icons::FILTER.icon());
|
||||
filterButton->setText(tr("Error Filter"));
|
||||
filterButton->setPopupMode(QToolButton::InstantPopup);
|
||||
filterButton->setProperty("noArrow", true);
|
||||
|
||||
m_filterMenu = new QMenu(filterButton);
|
||||
foreach (QAction *filterAction, m_errorFilterActions)
|
||||
m_filterMenu->addAction(filterAction);
|
||||
m_filterMenu->addSeparator();
|
||||
m_filterMenu->addAction(m_filterProjectAction);
|
||||
m_filterMenu->addAction(m_suppressionSeparator);
|
||||
connect(m_filterMenu, &QMenu::triggered, this, &MemcheckTool::updateErrorFilter);
|
||||
filterButton->setMenu(m_filterMenu);
|
||||
layout->addWidget(filterButton);
|
||||
|
||||
layout->addStretch();
|
||||
QWidget *widget = new QWidget;
|
||||
widget->setObjectName(QLatin1String("MemCheckToolBarWidget"));
|
||||
widget->setLayout(layout);
|
||||
return widget;
|
||||
}
|
||||
|
||||
MemcheckRunControl *MemcheckTool::createRunControl(RunConfiguration *runConfiguration,
|
||||
Core::Id runMode)
|
||||
{
|
||||
@@ -555,6 +570,12 @@ MemcheckRunControl *MemcheckTool::createRunControl(RunConfiguration *runConfigur
|
||||
connect(runControl, &MemcheckRunControl::parserError, this, &MemcheckTool::parserError);
|
||||
connect(runControl, &MemcheckRunControl::internalParserError, this, &MemcheckTool::internalParserError);
|
||||
connect(runControl, &MemcheckRunControl::finished, this, &MemcheckTool::engineFinished);
|
||||
|
||||
connect(m_stopAction, &QAction::triggered, runControl, [runControl] { runControl->stop(); });
|
||||
|
||||
m_toolBusy = true;
|
||||
updateRunActions();
|
||||
|
||||
return runControl;
|
||||
}
|
||||
|
||||
@@ -673,18 +694,19 @@ int MemcheckTool::updateUiAfterFinishedHelper()
|
||||
|
||||
void MemcheckTool::engineFinished()
|
||||
{
|
||||
m_toolBusy = false;
|
||||
updateRunActions();
|
||||
|
||||
const int issuesFound = updateUiAfterFinishedHelper();
|
||||
AnalyzerManager::showPermanentStatusMessage(issuesFound > 0
|
||||
? AnalyzerManager::tr("Memory Analyzer Tool finished, %n issues were found.", 0, issuesFound)
|
||||
: AnalyzerManager::tr("Memory Analyzer Tool finished, no issues were found."));
|
||||
Debugger::showPermanentStatusMessage(
|
||||
tr("Memory Analyzer Tool finished, %n issues were found.", 0, issuesFound));
|
||||
}
|
||||
|
||||
void MemcheckTool::loadingExternalXmlLogFileFinished()
|
||||
{
|
||||
const int issuesFound = updateUiAfterFinishedHelper();
|
||||
AnalyzerManager::showPermanentStatusMessage(issuesFound > 0
|
||||
? AnalyzerManager::tr("Log file processed, %n issues were found.", 0, issuesFound)
|
||||
: AnalyzerManager::tr("Log file processed, no issues were found."));
|
||||
Debugger::showPermanentStatusMessage(
|
||||
tr("Log file processed, %n issues were found.", 0, issuesFound));
|
||||
}
|
||||
|
||||
void MemcheckTool::setBusyCursor(bool busy)
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
#define VALGRIND_DEBUG_OUTPUT 0
|
||||
|
||||
using namespace Analyzer;
|
||||
using namespace Debugger;
|
||||
using namespace Core;
|
||||
using namespace Utils;
|
||||
using namespace ProjectExplorer;
|
||||
@@ -65,10 +65,8 @@ ValgrindRunControl::ValgrindRunControl(RunConfiguration *runConfiguration, Core:
|
||||
m_settings = ValgrindPlugin::globalSettings();
|
||||
}
|
||||
|
||||
bool ValgrindRunControl::startEngine()
|
||||
void ValgrindRunControl::start()
|
||||
{
|
||||
emit starting();
|
||||
|
||||
FutureProgress *fp = ProgressManager::addTimedTask(m_progress, progressTitle(), "valgrind", 100);
|
||||
fp->setKeepOnFinish(FutureProgress::HideOnFinish);
|
||||
connect(fp, &FutureProgress::canceled,
|
||||
@@ -86,7 +84,6 @@ bool ValgrindRunControl::startEngine()
|
||||
ValgrindRunner *run = runner();
|
||||
run->setValgrindExecutable(m_settings->valgrindExecutable());
|
||||
run->setValgrindArguments(genericToolArguments() + toolArguments());
|
||||
QTC_ASSERT(!device().isNull(), return false);
|
||||
run->setDevice(device());
|
||||
run->setDebuggee(runnable().as<StandardRunnable>());
|
||||
|
||||
@@ -99,15 +96,24 @@ bool ValgrindRunControl::startEngine()
|
||||
|
||||
if (!run->start()) {
|
||||
m_progress.cancel();
|
||||
return false;
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
return true;
|
||||
|
||||
m_isRunning = true;
|
||||
emit started();
|
||||
}
|
||||
|
||||
void ValgrindRunControl::stopEngine()
|
||||
RunControl::StopResult ValgrindRunControl::stop()
|
||||
{
|
||||
m_isStopping = true;
|
||||
runner()->stop();
|
||||
return AsynchronousStop;
|
||||
}
|
||||
|
||||
bool ValgrindRunControl::isRunning() const
|
||||
{
|
||||
return m_isRunning;
|
||||
}
|
||||
|
||||
QString ValgrindRunControl::executable() const
|
||||
@@ -139,7 +145,6 @@ QStringList ValgrindRunControl::genericToolArguments() const
|
||||
|
||||
void ValgrindRunControl::handleProgressCanceled()
|
||||
{
|
||||
AnalyzerManager::stopTool();
|
||||
m_progress.reportCanceled();
|
||||
m_progress.reportFinished();
|
||||
}
|
||||
@@ -151,6 +156,8 @@ void ValgrindRunControl::handleProgressFinished()
|
||||
|
||||
void ValgrindRunControl::runnerFinished()
|
||||
{
|
||||
m_isRunning = false;
|
||||
|
||||
appendMessage(tr("Analyzing finished.") + QLatin1Char('\n'), NormalMessageFormat);
|
||||
emit finished();
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
namespace Valgrind {
|
||||
namespace Internal {
|
||||
|
||||
class ValgrindRunControl : public Analyzer::AnalyzerRunControl
|
||||
class ValgrindRunControl : public Debugger::AnalyzerRunControl
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
@@ -46,8 +46,9 @@ public:
|
||||
ValgrindRunControl(ProjectExplorer::RunConfiguration *runConfiguration,
|
||||
Core::Id runMode);
|
||||
|
||||
bool startEngine() override;
|
||||
void stopEngine() override;
|
||||
void start() override;
|
||||
StopResult stop() override;
|
||||
bool isRunning() const override;
|
||||
|
||||
QString executable() const;
|
||||
|
||||
@@ -70,6 +71,7 @@ private:
|
||||
QStringList genericToolArguments() const;
|
||||
|
||||
private:
|
||||
bool m_isRunning = false;
|
||||
bool m_isStopping = false;
|
||||
};
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
using namespace Analyzer;
|
||||
using namespace Debugger;
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace Valgrind {
|
||||
@@ -56,7 +56,7 @@ bool ValgrindRunControlFactory::canRun(RunConfiguration *runConfiguration, Core:
|
||||
RunControl *ValgrindRunControlFactory::create(RunConfiguration *runConfiguration, Core::Id mode, QString *errorMessage)
|
||||
{
|
||||
Q_UNUSED(errorMessage);
|
||||
return AnalyzerManager::createRunControl(runConfiguration, mode);
|
||||
return Debugger::createAnalyzerRunControl(runConfiguration, mode);
|
||||
}
|
||||
|
||||
class ValgrindRunConfigurationAspect : public IRunConfigurationAspect
|
||||
|
||||
@@ -188,8 +188,8 @@ QString ValgrindRunner::errorString() const
|
||||
|
||||
void ValgrindRunner::stop()
|
||||
{
|
||||
if (d->process)
|
||||
d->process->close();
|
||||
QTC_ASSERT(d->process, finished(); return);
|
||||
d->process->close();
|
||||
}
|
||||
|
||||
ValgrindProcess *ValgrindRunner::valgrindProcess() const
|
||||
|
||||
@@ -191,8 +191,8 @@ ErrorItem::ErrorItem(const ErrorListModelPrivate *modelPrivate, const Error &err
|
||||
static QVariant location(const Frame &frame, int role)
|
||||
{
|
||||
switch (role) {
|
||||
case Analyzer::DetailedErrorView::LocationRole:
|
||||
return QVariant::fromValue(Analyzer::DiagnosticLocation(frame.filePath(), frame.line(), 0));
|
||||
case Debugger::DetailedErrorView::LocationRole:
|
||||
return QVariant::fromValue(Debugger::DiagnosticLocation(frame.filePath(), frame.line(), 0));
|
||||
case Qt::ToolTipRole:
|
||||
return frame.filePath().isEmpty() ? QVariant() : QVariant(frame.filePath());
|
||||
default:
|
||||
@@ -202,14 +202,14 @@ static QVariant location(const Frame &frame, int role)
|
||||
|
||||
QVariant ErrorItem::data(int column, int role) const
|
||||
{
|
||||
if (column == Analyzer::DetailedErrorView::LocationColumn) {
|
||||
if (column == Debugger::DetailedErrorView::LocationColumn) {
|
||||
const Frame frame = m_modelPrivate->findRelevantFrame(m_error);
|
||||
return location(frame, role);
|
||||
}
|
||||
|
||||
// DiagnosticColumn
|
||||
switch (role) {
|
||||
case Analyzer::DetailedErrorView::FullTextRole: {
|
||||
case Debugger::DetailedErrorView::FullTextRole: {
|
||||
QString content;
|
||||
QTextStream stream(&content);
|
||||
|
||||
@@ -257,7 +257,7 @@ StackItem::StackItem(const Stack &stack) : m_stack(stack)
|
||||
QVariant StackItem::data(int column, int role) const
|
||||
{
|
||||
const ErrorItem * const errorItem = getErrorItem();
|
||||
if (column == Analyzer::DetailedErrorView::LocationColumn)
|
||||
if (column == Debugger::DetailedErrorView::LocationColumn)
|
||||
return location(errorItem->modelPrivate()->findRelevantFrame(errorItem->error()), role);
|
||||
|
||||
// DiagnosticColumn
|
||||
@@ -285,7 +285,7 @@ FrameItem::FrameItem(const Frame &frame) : m_frame(frame)
|
||||
|
||||
QVariant FrameItem::data(int column, int role) const
|
||||
{
|
||||
if (column == Analyzer::DetailedErrorView::LocationColumn)
|
||||
if (column == Debugger::DetailedErrorView::LocationColumn)
|
||||
return location(m_frame, role);
|
||||
|
||||
// DiagnosticColumn
|
||||
|
||||
@@ -45,7 +45,7 @@ class ErrorListModel : public Utils::TreeModel
|
||||
|
||||
public:
|
||||
enum Role {
|
||||
ErrorRole = Analyzer::DetailedErrorView::FullTextRole + 1,
|
||||
ErrorRole = Debugger::DetailedErrorView::FullTextRole + 1,
|
||||
};
|
||||
|
||||
class RelevantFrameFinder
|
||||
|
||||
Reference in New Issue
Block a user