aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/debugger/debuggerengine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/debugger/debuggerengine.cpp')
-rw-r--r--src/plugins/debugger/debuggerengine.cpp1943
1 files changed, 1402 insertions, 541 deletions
diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp
index c0618ee818..028fa92211 100644
--- a/src/plugins/debugger/debuggerengine.cpp
+++ b/src/plugins/debugger/debuggerengine.cpp
@@ -28,25 +28,34 @@
#include "debuggerinternalconstants.h"
#include "debuggeractions.h"
#include "debuggercore.h"
+#include "debuggerdialogs.h"
#include "debuggericons.h"
#include "debuggerruncontrol.h"
#include "debuggertooltipmanager.h"
+#include "analyzer/analyzermanager.h"
#include "breakhandler.h"
#include "disassembleragent.h"
+#include "localsandexpressionswindow.h"
#include "logwindow.h"
+#include "debuggermainwindow.h"
#include "memoryagent.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "sourcefileshandler.h"
#include "sourceutils.h"
#include "stackhandler.h"
+#include "stackwindow.h"
+#include "snapshothandler.h"
#include "terminal.h"
#include "threadshandler.h"
#include "watchhandler.h"
+#include "watchutils.h"
+#include "watchwindow.h"
#include "debugger/shared/peutils.h"
#include "console/console.h"
+#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/icore.h>
@@ -59,18 +68,29 @@
#include <projectexplorer/taskhub.h>
#include <texteditor/texteditor.h>
+#include <texteditor/texteditorsettings.h>
+#include <texteditor/fontsettings.h>
+#include <utils/basetreeview.h>
#include <utils/fileinprojectfinder.h>
#include <utils/macroexpander.h>
#include <utils/processhandle.h>
#include <utils/qtcassert.h>
#include <utils/qtcprocess.h>
#include <utils/savedaction.h>
+#include <utils/styledbar.h>
+#include <utils/utilsicons.h>
+#include <QApplication>
+#include <QComboBox>
#include <QDebug>
-#include <QTimer>
-#include <QFileInfo>
#include <QDir>
+#include <QDockWidget>
+#include <QFileInfo>
+#include <QHeaderView>
+#include <QTextBlock>
+#include <QTimer>
+#include <QToolButton>
#include <QJsonArray>
#include <QJsonDocument>
@@ -113,6 +133,33 @@ QDebug operator<<(QDebug str, const DebuggerRunParameters &sp)
namespace Internal {
+static bool debuggerActionsEnabledHelper(DebuggerState state)
+{
+ switch (state) {
+ case InferiorRunOk:
+ case InferiorUnrunnable:
+ case InferiorStopOk:
+ return true;
+ case InferiorStopRequested:
+ case InferiorRunRequested:
+ case InferiorRunFailed:
+ case DebuggerNotReady:
+ case EngineSetupRequested:
+ case EngineSetupOk:
+ case EngineSetupFailed:
+ case EngineRunRequested:
+ case EngineRunFailed:
+ case InferiorStopFailed:
+ case InferiorShutdownRequested:
+ case InferiorShutdownFinished:
+ case EngineShutdownRequested:
+ case EngineShutdownFinished:
+ case DebuggerFinished:
+ return false;
+ }
+ return false;
+}
+
Location::Location(const StackFrame &frame, bool marker)
{
m_fileName = frame.file;
@@ -128,13 +175,22 @@ Location::Location(const StackFrame &frame, bool marker)
LocationMark::LocationMark(DebuggerEngine *engine, const FileName &file, int line)
: TextMark(file, line, Constants::TEXT_MARK_CATEGORY_LOCATION), m_engine(engine)
{
- setIcon(Icons::LOCATION.icon());
setPriority(TextMark::HighPriority);
+ updateIcon();
+}
+
+void LocationMark::updateIcon()
+{
+ const Icon *icon = &Icons::WATCHPOINT;
+ if (m_engine && EngineManager::currentEngine() == m_engine)
+ icon = m_engine->isReverseDebugging() ? &Icons::REVERSE_LOCATION : &Icons::LOCATION;
+ setIcon(icon->icon());
+ updateMarker();
}
bool LocationMark::isDraggable() const
{
- return m_engine->hasCapability(JumpToLineCapability);
+ return m_engine && m_engine->hasCapability(JumpToLineCapability);
}
void LocationMark::dragToLine(int line)
@@ -213,41 +269,108 @@ class DebuggerEnginePrivate : public QObject
public:
DebuggerEnginePrivate(DebuggerEngine *engine)
- : m_engine(engine),
- m_modulesHandler(engine),
- m_registerHandler(engine),
- m_sourceFilesHandler(engine),
- m_stackHandler(engine),
- m_threadsHandler(engine),
- m_watchHandler(engine),
- m_disassemblerAgent(engine)
+ : m_engine(engine),
+ m_breakHandler(engine),
+ m_modulesHandler(engine),
+ m_registerHandler(engine),
+ m_sourceFilesHandler(engine),
+ m_stackHandler(engine),
+ m_threadsHandler(engine),
+ m_watchHandler(engine),
+ m_disassemblerAgent(engine)
+ {
+ m_logWindow = new LogWindow(m_engine); // Needed before start()
+ m_logWindow->setObjectName(QLatin1String(DOCKWIDGET_OUTPUT));
+ m_debuggerName = DebuggerEngine::tr("Debugger");
+
+ connect(action(EnableReverseDebugging), &SavedAction::valueChanged,
+ this, [this] { updateState(true); });
+ }
+
+
+ ~DebuggerEnginePrivate()
{
- connect(&m_locationTimer, &QTimer::timeout,
- this, &DebuggerEnginePrivate::resetLocation);
+ destroyPerspective();
+
+ delete m_logWindow;
+ delete m_breakWindow;
+ delete m_returnWindow;
+ delete m_localsWindow;
+ delete m_watchersWindow;
+ delete m_inspectorWindow;
+ delete m_registerWindow;
+ delete m_modulesWindow;
+ delete m_sourceFilesWindow;
+ delete m_stackWindow;
+ delete m_threadsWindow;
+
+ delete m_breakView;
+ delete m_returnView;
+ delete m_localsView;
+ delete m_watchersView;
+ delete m_inspectorView;
+ delete m_registerView;
+ delete m_modulesView;
+ delete m_sourceFilesView;
+ delete m_stackView;
+ delete m_threadsView;
}
- void doShutdownEngine();
- void doShutdownInferior();
+ void setupViews();
+
+ void destroyPerspective()
+ {
+ if (!m_perspective)
+ return;
+
+ EngineManager::unregisterEngine(m_engine);
+
+ // Give up ownership on claimed breakpoints.
+ m_breakHandler.releaseAllBreakpoints();
+ DebuggerToolTipManager::deregisterEngine(m_engine);
+ m_memoryAgents.handleDebuggerFinished();
+
+ m_perspective->destroy();
+ m_perspective = nullptr;
+
+ setBusyCursor(false);
+ }
+
+ void updateReturnViewHeader(int section, int, int newSize)
+ {
+ if (m_perspective && m_returnView && m_returnView->header())
+ m_returnView->header()->resizeSection(section, newSize);
+ }
+
+ void doShutdownEngine()
+ {
+ m_engine->setState(EngineShutdownRequested);
+ m_engine->startDying();
+ m_engine->showMessage("CALL: SHUTDOWN ENGINE");
+ m_engine->shutdownEngine();
+ }
+
+ void doShutdownInferior()
+ {
+ m_engine->setState(InferiorShutdownRequested);
+ //QTC_ASSERT(isMasterEngine(), return);
+ resetLocation();
+ m_engine->showMessage("CALL: SHUTDOWN INFERIOR");
+ m_engine->shutdownInferior();
+ }
void doFinishDebugger()
{
- QTC_ASSERT(state() == EngineShutdownFinished, qDebug() << state());
- m_engine->setState(DebuggerFinished);
+ QTC_ASSERT(m_state == EngineShutdownFinished, qDebug() << m_state);
resetLocation();
- if (isMasterEngine()) {
- if (m_runTool) {
- m_progress.setProgressValue(1000);
- m_progress.reportFinished();
- m_modulesHandler.removeAll();
- m_stackHandler.removeAll();
- m_threadsHandler.removeAll();
- m_watchHandler.cleanup();
- Internal::runControlFinished(m_runTool);
- m_runTool->reportStopped();
- m_runTool->appendMessage(tr("Debugging has finished"), NormalMessageFormat);
- m_runTool.clear();
- }
- }
+ m_progress.setProgressValue(1000);
+ m_progress.reportFinished();
+ m_modulesHandler.removeAll();
+ m_stackHandler.removeAll();
+ m_threadsHandler.removeAll();
+ m_watchHandler.cleanup();
+ m_engine->showMessage(tr("Debugger finished."), StatusBar);
+ m_engine->setState(DebuggerFinished); // Also destroys views.
}
void scheduleResetLocation()
@@ -272,13 +395,45 @@ public:
DebuggerToolTipManager::resetLocation();
}
+ void selectThread(int index)
+ {
+ ThreadId id = m_engine->threadsHandler()->threadAt(index);
+ m_engine->selectThread(id);
+ }
+
+ void handleOperateByInstructionTriggered(bool on)
+ {
+ // Go to source only if we have the file.
+ // if (DebuggerEngine *cppEngine = m_engine->cppEngine()) {
+ if (m_stackHandler.currentIndex() >= 0) {
+ const StackFrame frame = m_stackHandler.currentFrame();
+ if (on || frame.isUsable())
+ m_engine->gotoLocation(Location(frame, true));
+ }
+ // }
+ }
+
+ bool operatesByInstruction() const
+ {
+ return m_operateByInstructionAction.isChecked();
+ }
+
public:
- DebuggerState state() const { return m_state; }
- bool isMasterEngine() const { return m_engine->isMasterEngine(); }
+ void setInitialActionStates();
+ void setBusyCursor(bool on);
+ void cleanupViews();
+ void updateState(bool alsoUpdateCompanion);
+ void updateReverseActions();
DebuggerEngine *m_engine = nullptr; // Not owned.
- DebuggerEngine *m_masterEngine = nullptr; // Not owned
- QPointer<DebuggerRunTool> m_runTool; // Not owned.
+ QPointer<RunConfiguration> m_runConfiguration; // Not owned.
+ QString m_debuggerName;
+ Perspective *m_perspective = nullptr;
+ DebuggerRunParameters m_runParameters;
+ IDevice::ConstPtr m_device;
+
+ QPointer<DebuggerEngine> m_companionEngine;
+ bool m_isPrimaryEngine = true;
// The current state.
DebuggerState m_state = DebuggerNotReady;
@@ -286,6 +441,7 @@ public:
// Terminal m_terminal;
ProcessHandle m_inferiorPid;
+ BreakHandler m_breakHandler;
ModulesHandler m_modulesHandler;
RegisterHandler m_registerHandler;
SourceFilesHandler m_sourceFilesHandler;
@@ -305,8 +461,330 @@ public:
// Safety net to avoid infinite lookups.
QSet<QString> m_lookupRequests; // FIXME: Integrate properly.
QPointer<QWidget> m_alertBox;
+
+ QPointer<BaseTreeView> m_breakView;
+ QPointer<BaseTreeView> m_returnView;
+ QPointer<BaseTreeView> m_localsView;
+ QPointer<BaseTreeView> m_watchersView;
+ QPointer<WatchTreeView> m_inspectorView;
+ QPointer<BaseTreeView> m_registerView;
+ QPointer<BaseTreeView> m_modulesView;
+ QPointer<BaseTreeView> m_sourceFilesView;
+ QPointer<BaseTreeView> m_stackView;
+ QPointer<BaseTreeView> m_threadsView;
+ QPointer<QWidget> m_breakWindow;
+ QPointer<QWidget> m_returnWindow;
+ QPointer<QWidget> m_localsWindow;
+ QPointer<QWidget> m_watchersWindow;
+ QPointer<QWidget> m_inspectorWindow;
+ QPointer<QWidget> m_registerWindow;
+ QPointer<QWidget> m_modulesWindow;
+ QPointer<QWidget> m_sourceFilesWindow;
+ QPointer<QWidget> m_stackWindow;
+ QPointer<QWidget> m_threadsWindow;
+ QPointer<LogWindow> m_logWindow;
+ QPointer<LocalsAndInspectorWindow> m_localsAndInspectorWindow;
+
+ QPointer<QLabel> m_threadLabel;
+ QPointer<QComboBox> m_threadBox;
+
+ bool m_busy = false;
+ bool m_isDying = false;
+
+ QAction m_debugWithoutDeployAction;
+ QAction m_attachToQmlPortAction;
+ QAction m_attachToRemoteServerAction;
+ QAction m_startRemoteCdbAction;
+ QAction m_attachToCoreAction;
+ QAction m_detachAction;
+ OptionalAction m_continueAction{tr("Continue")};
+ QAction m_exitAction{tr("Stop Debugger")}; // On application output button if "Stop" is possible
+ OptionalAction m_interruptAction{tr("Interrupt")}; // On the fat debug button if "Pause" is possible
+ QAction m_abortAction{tr("Abort Debugging")};
+ QAction m_stepIntoAction{tr("Step Into")};
+ QAction m_stepOutAction{tr("Step Out")};
+ QAction m_runToLineAction{tr("Run to Line")}; // In the debug menu
+ QAction m_runToSelectedFunctionAction{tr("Run to Selected Function")};
+ QAction m_jumpToLineAction{tr("Jump to Line")};
+ // In the Debug menu.
+ QAction m_returnFromFunctionAction{tr("Immediately Return From Inner Function")};
+ QAction m_stepOverAction{tr("Step Over")};
+ QAction m_watchAction{tr("Add Expression Evaluator")};
+ QAction m_breakAction{tr("Toggle Breakpoint")};
+ QAction m_resetAction{tr("Restart Debugging")};
+ OptionalAction m_operateByInstructionAction{tr("Operate by Instruction")};
+ QAction m_recordForReverseOperationAction{tr("Record information to allpow reversal of Direction")};
+ OptionalAction m_operateInReverseDirectionAction{tr("Reverse Direction")};
+ OptionalAction m_snapshotAction{tr("Take Snapshot of Process State")};
+
+ QPointer<TerminalRunner> m_terminalRunner;
};
+void DebuggerEnginePrivate::setupViews()
+{
+ const DebuggerRunParameters &rp = m_engine->runParameters();
+
+ QTC_CHECK(!m_perspective);
+ const QString id = Constants::PERSPECTIVE_ID + m_debuggerName + rp.displayName;
+ m_perspective = new Perspective(id, m_engine->displayName());
+
+ m_progress.setProgressRange(0, 1000);
+ FutureProgress *fp = ProgressManager::addTask(m_progress.future(),
+ tr("Launching Debugger"), "Debugger.Launcher");
+ connect(fp, &FutureProgress::canceled, m_engine, &DebuggerEngine::quitDebugger);
+ fp->setKeepOnFinish(FutureProgress::HideOnFinish);
+ m_progress.reportStarted();
+
+ m_inferiorPid = rp.attachPID.isValid() ? rp.attachPID : ProcessHandle();
+// if (m_inferiorPid.isValid())
+// m_runControl->setApplicationProcessHandle(m_inferiorPid);
+
+ m_operateByInstructionAction.setEnabled(true);
+ m_operateByInstructionAction.setVisible(m_engine->hasCapability(DisassemblerCapability));
+ m_operateByInstructionAction.setIcon(Debugger::Icons::SINGLE_INSTRUCTION_MODE.icon());
+ m_operateByInstructionAction.setCheckable(true);
+ m_operateByInstructionAction.setChecked(boolSetting(OperateByInstruction));
+ connect(&m_operateByInstructionAction, &QAction::triggered,
+ this, &DebuggerEnginePrivate::handleOperateByInstructionTriggered);
+
+ QTC_ASSERT(m_state == DebuggerNotReady || m_state == DebuggerFinished, qDebug() << m_state);
+ m_progress.setProgressValue(200);
+
+// m_terminal.setup();
+// if (m_terminal.isUsable()) {
+// connect(&m_terminal, &Terminal::stdOutReady, [this](const QString &msg) {
+// m_engine->showMessage(msg, Utils::StdOutFormatSameLine);
+// });
+// connect(&m_terminal, &Terminal::stdErrReady, [this](const QString &msg) {
+// m_engine->showMessage(msg, Utils::StdErrFormatSameLine);
+// });
+// connect(&m_terminal, &Terminal::error, [this](const QString &msg) {
+// m_engine->showMessage(msg, Utils::ErrorMessageFormat);
+// });
+// }
+
+ connect(&m_locationTimer, &QTimer::timeout,
+ this, &DebuggerEnginePrivate::resetLocation);
+
+ QSettings *settings = ICore::settings();
+ const QString perspectiveId = m_perspective->id();
+
+ m_modulesView = new BaseTreeView;
+ m_modulesView->setModel(m_modulesHandler.model());
+ m_modulesView->setSortingEnabled(true);
+ m_modulesView->setSettings(settings, "Debugger.ModulesView");
+ connect(m_modulesView, &BaseTreeView::aboutToShow,
+ m_engine, &DebuggerEngine::reloadModules,
+ Qt::QueuedConnection);
+ m_modulesWindow = addSearch(m_modulesView);
+ m_modulesWindow->setObjectName(DOCKWIDGET_MODULES + perspectiveId);
+ m_modulesWindow->setWindowTitle(tr("&Modules"));
+
+ m_registerView = new BaseTreeView;
+ m_registerView->setModel(m_registerHandler.model());
+ m_registerView->setRootIsDecorated(true);
+ m_registerView->setSettings(settings, "Debugger.RegisterView");
+ connect(m_registerView, &BaseTreeView::aboutToShow,
+ m_engine, &DebuggerEngine::reloadRegisters,
+ Qt::QueuedConnection);
+ m_registerWindow = addSearch(m_registerView);
+ m_registerWindow->setObjectName(DOCKWIDGET_REGISTER + m_perspective->id());
+ m_registerWindow->setWindowTitle(tr("Reg&isters"));
+
+ m_stackView = new BaseTreeView;
+ m_stackView->setModel(m_stackHandler.model());
+ m_stackView->setSettings(settings, "Debugger.StackView");
+ m_stackView->setIconSize(QSize(10, 10));
+ m_stackWindow = addSearch(m_stackView);
+ m_stackWindow->setObjectName(DOCKWIDGET_STACK + m_perspective->id());
+ m_stackWindow->setWindowTitle(tr("&Stack"));
+
+ m_sourceFilesView = new BaseTreeView;
+ m_sourceFilesView->setModel(m_sourceFilesHandler.model());
+ m_sourceFilesView->setSortingEnabled(true);
+ m_sourceFilesView->setSettings(settings, "Debugger.SourceFilesView");
+ connect(m_sourceFilesView, &BaseTreeView::aboutToShow,
+ m_engine, &DebuggerEngine::reloadSourceFiles,
+ Qt::QueuedConnection);
+ m_sourceFilesWindow = addSearch(m_sourceFilesView);
+ m_sourceFilesWindow->setObjectName(DOCKWIDGET_SOURCE_FILES + m_perspective->id());
+ m_sourceFilesWindow->setWindowTitle(tr("Source Files"));
+
+ m_threadsView = new BaseTreeView;
+ m_threadsView->setModel(m_threadsHandler.model());
+ m_threadsView->setSortingEnabled(true);
+ m_threadsView->setSettings(settings, "Debugger.ThreadsView");
+ m_threadsView->setIconSize(QSize(10, 10));
+ m_threadsWindow = addSearch(m_threadsView);
+ m_threadsWindow->setObjectName(DOCKWIDGET_THREADS + m_perspective->id());
+ m_threadsWindow->setWindowTitle(tr("&Threads"));
+
+ m_returnView = new WatchTreeView{ReturnType};
+ m_returnView->setModel(m_watchHandler.model());
+ m_returnWindow = addSearch(m_returnView);
+ m_returnWindow->setObjectName("CppDebugReturn" + m_perspective->id());
+ m_returnWindow->setWindowTitle(tr("Locals"));
+ m_returnWindow->setVisible(false);
+
+ m_localsView = new WatchTreeView{LocalsType};
+ m_localsView->setModel(m_watchHandler.model());
+ m_localsView->setSettings(settings, "Debugger.LocalsView");
+ m_localsWindow = addSearch(m_localsView);
+ m_localsWindow->setObjectName("CppDebugLocals" + m_perspective->id());
+ m_localsWindow->setWindowTitle(tr("Locals"));
+
+ m_inspectorView = new WatchTreeView{InspectType};
+ m_inspectorView->setModel(m_watchHandler.model());
+ m_inspectorView->setSettings(settings, "Debugger.LocalsView"); // sic! same as locals view.
+ m_inspectorWindow = addSearch(m_inspectorView);
+ m_inspectorWindow->setObjectName("Inspector" + m_perspective->id());
+ m_inspectorWindow->setWindowTitle(tr("Locals"));
+
+ m_watchersView = new WatchTreeView{WatchersType};
+ m_watchersView->setModel(m_watchHandler.model());
+ m_watchersView->setSettings(settings, "Debugger.WatchersView");
+ m_watchersWindow = addSearch(m_watchersView);
+ m_watchersWindow->setObjectName("CppDebugWatchers" + m_perspective->id());
+ m_watchersWindow->setWindowTitle(tr("&Expressions"));
+
+ m_localsAndInspectorWindow = new LocalsAndInspectorWindow(
+ m_localsWindow, m_inspectorWindow, m_returnWindow);
+ m_localsAndInspectorWindow->setObjectName(DOCKWIDGET_LOCALS_AND_INSPECTOR + m_perspective->id());
+ m_localsAndInspectorWindow->setWindowTitle(m_localsWindow->windowTitle());
+
+ // Locals
+ connect(m_localsView->header(), &QHeaderView::sectionResized,
+ this, &DebuggerEnginePrivate::updateReturnViewHeader, Qt::QueuedConnection);
+
+ m_breakView = new BaseTreeView;
+ m_breakView->setIconSize(QSize(10, 10));
+ m_breakView->setWindowIcon(Icons::BREAKPOINTS.icon());
+ m_breakView->setSelectionMode(QAbstractItemView::ExtendedSelection);
+ connect(action(UseAddressInBreakpointsView), &QAction::toggled,
+ this, [this](bool on) { m_breakView->setColumnHidden(BreakpointAddressColumn, !on); });
+ m_breakView->setSettings(settings, "Debugger.BreakWindow");
+ m_breakView->setModel(m_breakHandler.model());
+ m_breakView->setRootIsDecorated(true);
+ m_breakWindow = addSearch(m_breakView);
+ m_breakWindow->setObjectName(DOCKWIDGET_BREAK + m_perspective->id());
+ m_breakWindow->setWindowTitle(tr("&Breakpoints"));
+
+ m_perspective->addToolBarWidget(EngineManager::engineChooser());
+
+ m_perspective->addToolBarAction(&m_continueAction);
+ m_perspective->addToolBarAction(&m_interruptAction);
+
+ m_perspective->addToolBarAction(&m_exitAction);
+ m_perspective->addToolBarAction(&m_stepOverAction);
+ m_perspective->addToolBarAction(&m_stepIntoAction);
+ m_perspective->addToolBarAction(&m_stepOutAction);
+ m_perspective->addToolBarAction(&m_resetAction);
+ m_perspective->addToolBarAction(&m_operateByInstructionAction);
+
+ m_continueAction.setIcon(Icons::DEBUG_CONTINUE_SMALL_TOOLBAR.icon());
+ connect(&m_continueAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecContinue);
+
+ m_exitAction.setIcon(Icons::DEBUG_EXIT_SMALL.icon());
+ connect(&m_exitAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::requestRunControlStop);
+
+ m_interruptAction.setIcon(Icons::DEBUG_INTERRUPT_SMALL_TOOLBAR.icon());
+ connect(&m_interruptAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecInterrupt);
+
+ m_abortAction.setToolTip(tr("Aborts debugging and resets the debugger to the initial state."));
+ connect(&m_abortAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::abortDebugger);
+
+ m_resetAction.setToolTip(tr("Restart the debugging session."));
+ m_resetAction.setIcon(Icons::RESTART_TOOLBAR.icon());
+ connect(&m_resetAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleReset);
+
+ m_stepOverAction.setIcon(Icons::STEP_OVER_TOOLBAR.icon());
+ connect(&m_stepOverAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecNext);
+
+ m_stepIntoAction.setIcon(Icons::STEP_INTO_TOOLBAR.icon());
+ connect(&m_stepIntoAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecStep);
+
+ m_stepOutAction.setIcon(Icons::STEP_OUT_TOOLBAR.icon());
+ connect(&m_stepOutAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecStepOut);
+
+ connect(&m_runToLineAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecRunToLine);
+
+ connect(&m_runToSelectedFunctionAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecRunToSelectedFunction);
+
+ connect(&m_returnFromFunctionAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecReturn);
+
+ connect(&m_jumpToLineAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleExecJumpToLine);
+
+ m_perspective->addToolBarAction(&m_recordForReverseOperationAction);
+ connect(&m_recordForReverseOperationAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleRecordReverse);
+
+ m_perspective->addToolBarAction(&m_operateInReverseDirectionAction);
+ connect(&m_operateInReverseDirectionAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::handleReverseDirection);
+
+ m_perspective->addToolBarAction(&m_snapshotAction);
+ connect(&m_snapshotAction, &QAction::triggered,
+ m_engine, &DebuggerEngine::createSnapshot);
+
+ m_perspective->addToolbarSeparator();
+
+ m_threadLabel = new QLabel(tr("Threads:"));
+ m_perspective->addToolBarWidget(m_threadLabel);
+
+ m_threadBox = new QComboBox;
+ m_threadBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
+ connect(m_threadBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::activated),
+ this, &DebuggerEnginePrivate::selectThread);
+
+ m_perspective->addToolBarWidget(m_threadBox);
+
+ connect(TextEditorSettings::instance(), &TextEditorSettings::fontSettingsChanged,
+ this, [this](const FontSettings &settings) {
+ if (!boolSetting(FontSizeFollowsEditor))
+ return;
+ const qreal size = settings.fontZoom() * settings.fontSize() / 100.;
+ QFont font = m_breakWindow->font();
+ font.setPointSizeF(size);
+ m_breakWindow->setFont(font);
+ m_logWindow->setFont(font);
+ m_localsWindow->setFont(font);
+ m_modulesWindow->setFont(font);
+ //m_consoleWindow->setFont(font);
+ m_registerWindow->setFont(font);
+ m_returnWindow->setFont(font);
+ m_sourceFilesWindow->setFont(font);
+ m_stackWindow->setFont(font);
+ m_threadsWindow->setFont(font);
+ m_watchersWindow->setFont(font);
+ m_inspectorWindow->setFont(font);
+ });
+
+ m_perspective->setParentPerspective(Debugger::Constants::PRESET_PERSPRECTIVE_ID);
+ m_perspective->addWindow(m_stackWindow, Perspective::SplitVertical, nullptr);
+ m_perspective->addWindow(m_breakWindow, Perspective::SplitHorizontal, m_stackWindow);
+ m_perspective->addWindow(m_threadsWindow, Perspective::AddToTab, m_breakWindow,false);
+ m_perspective->addWindow(m_modulesWindow, Perspective::AddToTab, m_threadsWindow, false);
+ m_perspective->addWindow(m_sourceFilesWindow, Perspective::AddToTab, m_modulesWindow, false);
+ m_perspective->addWindow(m_localsAndInspectorWindow, Perspective::AddToTab, nullptr, true, Qt::RightDockWidgetArea);
+ m_perspective->addWindow(m_watchersWindow, Perspective::AddToTab, m_localsAndInspectorWindow, true, Qt::RightDockWidgetArea);
+ m_perspective->addWindow(m_registerWindow, Perspective::AddToTab, m_watchersWindow, true, Qt::RightDockWidgetArea);
+ m_perspective->addWindow(m_logWindow, Perspective::AddToTab, nullptr, false, Qt::TopDockWidgetArea);
+
+ Debugger::registerPerspective(m_perspective);
+ m_perspective->select();
+}
//////////////////////////////////////////////////////////////////////
//
@@ -317,14 +795,20 @@ public:
DebuggerEngine::DebuggerEngine()
: d(new DebuggerEnginePrivate(this))
{
+ updateState(false);
}
DebuggerEngine::~DebuggerEngine()
{
- disconnect();
+// EngineManager::unregisterEngine(this);
delete d;
}
+void DebuggerEngine::setDebuggerName(const QString &name)
+{
+ d->m_debuggerName = name;
+}
+
QString DebuggerEngine::stateName(int s)
{
# define SN(x) case x: return QLatin1String(#x);
@@ -357,6 +841,31 @@ void DebuggerEngine::showStatusMessage(const QString &msg, int timeout) const
showMessage(msg, StatusBar, timeout);
}
+void DebuggerEngine::updateLocalsWindow(bool showReturn)
+{
+ d->m_returnWindow->setVisible(showReturn);
+ d->m_localsView->resizeColumns();
+}
+
+bool DebuggerEngine::isRegistersWindowVisible() const
+{
+ return d->m_registerWindow->isVisible();
+}
+
+bool DebuggerEngine::isModulesWindowVisible() const
+{
+ return d->m_modulesWindow->isVisible();
+}
+
+void DebuggerEngine::setThreadBoxContents(const QStringList &list, int index)
+{
+ QSignalBlocker blocker(d->m_threadBox);
+ d->m_threadBox->clear();
+ for (const QString &item : list)
+ d->m_threadBox->addItem(item);
+ d->m_threadBox->setCurrentIndex(index);
+}
+
void DebuggerEngine::frameUp()
{
int currentIndex = stackHandler()->currentIndex();
@@ -375,74 +884,47 @@ void DebuggerEngine::doUpdateLocals(const UpdateParameters &)
ModulesHandler *DebuggerEngine::modulesHandler() const
{
- return d->m_masterEngine
- ? d->m_masterEngine->modulesHandler()
- : &d->m_modulesHandler;
+ return &d->m_modulesHandler;
}
RegisterHandler *DebuggerEngine::registerHandler() const
{
- return d->m_masterEngine
- ? d->m_masterEngine->registerHandler()
- : &d->m_registerHandler;
+ return &d->m_registerHandler;
}
StackHandler *DebuggerEngine::stackHandler() const
{
- return d->m_masterEngine
- ? d->m_masterEngine->stackHandler()
- : &d->m_stackHandler;
+ return &d->m_stackHandler;
}
ThreadsHandler *DebuggerEngine::threadsHandler() const
{
- return d->m_masterEngine
- ? d->m_masterEngine->threadsHandler()
- : &d->m_threadsHandler;
+ return &d->m_threadsHandler;
}
WatchHandler *DebuggerEngine::watchHandler() const
{
- return d->m_masterEngine
- ? d->m_masterEngine->watchHandler()
- : &d->m_watchHandler;
+ return &d->m_watchHandler;
}
SourceFilesHandler *DebuggerEngine::sourceFilesHandler() const
{
- return d->m_masterEngine
- ? d->m_masterEngine->sourceFilesHandler()
- : &d->m_sourceFilesHandler;
-}
-
-QAbstractItemModel *DebuggerEngine::modulesModel() const
-{
- return modulesHandler()->model();
-}
-
-QAbstractItemModel *DebuggerEngine::registerModel() const
-{
- return registerHandler()->model();
+ return &d->m_sourceFilesHandler;
}
-QAbstractItemModel *DebuggerEngine::stackModel() const
-{
- return stackHandler()->model();
-}
-
-QAbstractItemModel *DebuggerEngine::threadsModel() const
+BreakHandler *DebuggerEngine::breakHandler() const
{
- return threadsHandler()->model();
+ return &d->m_breakHandler;
}
-QAbstractItemModel *DebuggerEngine::watchModel() const
+LogWindow *DebuggerEngine::logWindow() const
{
- return watchHandler()->model();
+ return d->m_logWindow;
}
-QAbstractItemModel *DebuggerEngine::sourceFilesModel() const
+DisassemblerAgent *DebuggerEngine::disassemblerAgent() const
{
- return sourceFilesHandler()->model();
+ return &d->m_disassemblerAgent;
}
void DebuggerEngine::fetchMemory(MemoryAgent *, quint64 addr, quint64 length)
@@ -463,48 +945,30 @@ void DebuggerEngine::setRegisterValue(const QString &name, const QString &value)
Q_UNUSED(value);
}
-void DebuggerEngine::setRunTool(DebuggerRunTool *runTool)
+void DebuggerEngine::setRunParameters(const DebuggerRunParameters &runParameters)
{
- QTC_ASSERT(!d->m_runTool, notifyEngineSetupFailed(); return);
- d->m_runTool = runTool;
+ d->m_runParameters = runParameters;
}
-void DebuggerEngine::start()
+void DebuggerEngine::setRunTool(DebuggerRunTool *runTool)
{
- QTC_ASSERT(d->m_runTool, notifyEngineSetupFailed(); return);
-
- d->m_progress.setProgressRange(0, 1000);
- FutureProgress *fp = ProgressManager::addTask(d->m_progress.future(),
- tr("Launching Debugger"), "Debugger.Launcher");
- connect(fp, &FutureProgress::canceled, this, &DebuggerEngine::quitDebugger);
- fp->setKeepOnFinish(FutureProgress::HideOnFinish);
- d->m_progress.reportStarted();
+ RunControl *runControl = runTool->runControl();
+ d->m_runConfiguration = runControl->runConfiguration();
+ d->m_device = runControl->device();
+ if (!d->m_device)
+ d->m_device = d->m_runParameters.inferior.device;
+ d->m_terminalRunner = runTool->terminalRunner();
- const DebuggerRunParameters &rp = runParameters();
- d->m_inferiorPid = rp.attachPID.isValid() ? rp.attachPID : ProcessHandle();
- if (d->m_inferiorPid.isValid())
- d->m_runTool->runControl()->setApplicationProcessHandle(d->m_inferiorPid);
-
- action(OperateByInstruction)->setEnabled(hasCapability(DisassemblerCapability));
- action(OperateByInstruction)->setChecked(boolSetting(OperateByInstruction));
-
- QTC_ASSERT(state() == DebuggerNotReady || state() == DebuggerFinished,
- qDebug() << state());
- d->m_progress.setProgressValue(200);
+ validateExecutable();
-// d->m_terminal.setup();
-// if (d->m_terminal.isUsable()) {
-// connect(&d->m_terminal, &Terminal::stdOutReady, [this](const QString &msg) {
-// d->m_runTool->appendMessage(msg, Utils::StdOutFormatSameLine);
-// });
-// connect(&d->m_terminal, &Terminal::stdErrReady, [this](const QString &msg) {
-// d->m_runTool->appendMessage(msg, Utils::StdErrFormatSameLine);
-// });
-// connect(&d->m_terminal, &Terminal::error, [this](const QString &msg) {
-// d->m_runTool->appendMessage(msg, Utils::ErrorMessageFormat);
-// });
-// }
+ d->setupViews();
+}
+void DebuggerEngine::start()
+{
+ EngineManager::registerEngine(this);
+ d->m_watchHandler.resetWatchers();
+ d->setInitialActionStates();
setState(EngineSetupRequested);
showMessage("CALL: SETUP ENGINE");
setupEngine();
@@ -521,7 +985,7 @@ void DebuggerEngine::gotoLocation(const Location &loc)
d->resetLocation();
if (loc.canBeDisassembled()
- && ((hasCapability(OperateByInstructionCapability) && boolSetting(OperateByInstruction))
+ && ((hasCapability(OperateByInstructionCapability) && d->operatesByInstruction())
|| !loc.hasDebugInfo()) )
{
d->m_disassemblerAgent.setLocation(loc);
@@ -546,13 +1010,37 @@ void DebuggerEngine::gotoLocation(const Location &loc)
if (newEditor)
editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true);
- if (loc.needsMarker())
+ if (loc.needsMarker()) {
d->m_locationMark.reset(new LocationMark(this, FileName::fromString(file), line));
+ d->m_locationMark->setToolTip(tr("Current debugger location of %1").arg(displayName()));
+ }
+}
+
+void DebuggerEngine::gotoCurrentLocation()
+{
+ int top = stackHandler()->currentIndex();
+ if (top >= 0)
+ gotoLocation(stackHandler()->currentFrame());
}
const DebuggerRunParameters &DebuggerEngine::runParameters() const
{
- return runTool()->runParameters();
+ return d->m_runParameters;
+}
+
+DebuggerRunParameters &DebuggerEngine::mutableRunParameters() const
+{
+ return d->m_runParameters;
+}
+
+IDevice::ConstPtr DebuggerEngine::device() const
+{
+ return d->m_device;
+}
+
+DebuggerEngine *DebuggerEngine::companionEngine() const
+{
+ return d->m_companionEngine;
}
DebuggerState DebuggerEngine::state() const
@@ -560,6 +1048,21 @@ DebuggerState DebuggerEngine::state() const
return d->m_state;
}
+void DebuggerEngine::abortDebugger()
+{
+ resetLocation();
+ if (!d->m_isDying) {
+ // Be friendly the first time. This will change targetState().
+ showMessage("ABORTING DEBUGGER. FIRST TIME.");
+ quitDebugger();
+ } else {
+ // We already tried. Try harder.
+ showMessage("ABORTING DEBUGGER. SECOND TIME.");
+ abortDebuggerProcess();
+ emit requestRunControlFinish();
+ }
+}
+
static bool isAllowedTransition(DebuggerState from, DebuggerState to)
{
switch (from) {
@@ -621,20 +1124,12 @@ static bool isAllowedTransition(DebuggerState from, DebuggerState to)
return false;
}
-void DebuggerEngine::setupSlaveEngine()
-{
- QTC_CHECK(state() == DebuggerNotReady);
- setState(EngineSetupRequested);
- showMessage("CALL: SETUP SLAVE ENGINE");
- setupEngine();
-}
-
void DebuggerEngine::notifyEngineSetupFailed()
{
showMessage("NOTE: ENGINE SETUP FAILED");
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << this << state());
setState(EngineSetupFailed);
- if (isMasterEngine() && runTool()) {
+ if (d->m_isPrimaryEngine) {
showMessage(tr("Debugging has failed"), NormalMessageFormat);
d->m_progress.setProgressValue(900);
d->m_progress.reportCanceled();
@@ -653,28 +1148,13 @@ void DebuggerEngine::notifyEngineSetupOk()
d->m_progress.setProgressValue(250);
QTC_ASSERT(state() == EngineSetupRequested, qDebug() << this << state());
setState(EngineSetupOk);
- if (isMasterEngine()) {
- if (runTool())
- runTool()->reportStarted();
- // Slaves will get called setupSlaveInferior() below.
- setState(EngineRunRequested);
- showMessage("CALL: RUN ENGINE");
- d->m_progress.setProgressValue(300);
- runEngine();
- }
-}
-
-void DebuggerEngine::runSlaveEngine()
-{
- QTC_ASSERT(isSlaveEngine(), return);
- QTC_CHECK(state() == EngineSetupOk);
+ // Slaves will get called setupSlaveInferior() below.
setState(EngineRunRequested);
- showMessage("CALL: RUN SLAVE ENGINE");
+ showMessage("CALL: RUN ENGINE");
d->m_progress.setProgressValue(300);
runEngine();
}
-
void DebuggerEngine::notifyEngineRunOkAndInferiorUnrunnable()
{
showMessage("NOTE: INFERIOR UNRUNNABLE");
@@ -769,7 +1249,7 @@ void DebuggerEngine::notifyInferiorStopOk()
return;
}
QTC_ASSERT(state() == InferiorStopRequested, qDebug() << this << state());
- showStatusMessage(tr("Stopped."));
+ showMessage(tr("Stopped."), StatusBar);
setState(InferiorStopOk);
}
@@ -777,10 +1257,11 @@ void DebuggerEngine::notifyInferiorSpontaneousStop()
{
showMessage("NOTE: INFERIOR SPONTANEOUS STOP");
QTC_ASSERT(state() == InferiorRunOk, qDebug() << this << state());
- showStatusMessage(tr("Stopped."));
+ showMessage(tr("Stopped."), StatusBar);
setState(InferiorStopOk);
if (boolSetting(RaiseOnInterrupt))
ICore::raiseWindow(Internal::mainWindow());
+ EngineManager::activateEngine(this);
}
void DebuggerEngine::notifyInferiorStopFailed()
@@ -791,13 +1272,277 @@ void DebuggerEngine::notifyInferiorStopFailed()
d->doShutdownEngine();
}
-void DebuggerEnginePrivate::doShutdownInferior()
+void DebuggerEnginePrivate::setInitialActionStates()
{
- m_engine->setState(InferiorShutdownRequested);
- //QTC_ASSERT(isMasterEngine(), return);
- resetLocation();
- m_engine->showMessage("CALL: SHUTDOWN INFERIOR");
- m_engine->shutdownInferior();
+ m_returnWindow->setVisible(false);
+ setBusyCursor(false);
+
+ m_recordForReverseOperationAction.setCheckable(true);
+ m_recordForReverseOperationAction.setChecked(false);
+ m_recordForReverseOperationAction.setIcon(Icons::RECORD_OFF.icon());
+ m_recordForReverseOperationAction.setToolTip(tr(
+ "<html><head/><body><p>Record information to enable stepping backwards.</p><p>"
+ "<b>Note:</b> This feature is very slow and unstable on the GDB side. "
+ "It exhibits unpredictable behavior when going backwards over system "
+ "calls and is very likely to destroy your debugging session.</p></body></html>"));
+
+ m_operateInReverseDirectionAction.setCheckable(true);
+ m_operateInReverseDirectionAction.setChecked(false);
+ m_operateInReverseDirectionAction.setIcon(Icons::DIRECTION_FORWARD.icon());
+
+ m_snapshotAction.setIcon(Utils::Icons::SNAPSHOT_TOOLBAR.icon());
+
+ m_attachToQmlPortAction.setEnabled(true);
+ m_attachToCoreAction.setEnabled(true);
+ m_attachToRemoteServerAction.setEnabled(true);
+ m_detachAction.setEnabled(false);
+
+ m_watchAction.setEnabled(true);
+ m_breakAction.setEnabled(false);
+ m_snapshotAction.setEnabled(false);
+ m_operateByInstructionAction.setEnabled(false);
+
+ m_exitAction.setEnabled(false);
+ m_abortAction.setEnabled(false);
+ m_resetAction.setEnabled(false);
+
+ m_interruptAction.setEnabled(false);
+ m_continueAction.setEnabled(false);
+
+ m_stepIntoAction.setEnabled(true);
+ m_stepOutAction.setEnabled(false);
+ m_runToLineAction.setEnabled(false);
+ m_runToSelectedFunctionAction.setEnabled(true);
+ m_returnFromFunctionAction.setEnabled(false);
+ m_jumpToLineAction.setEnabled(false);
+ m_stepOverAction.setEnabled(true);
+
+ action(AutoDerefPointers)->setEnabled(true);
+ action(ExpandStack)->setEnabled(false);
+
+ m_threadLabel->setEnabled(false);
+}
+
+void DebuggerEnginePrivate::updateState(bool alsoUpdateCompanion)
+{
+ if (!m_perspective)
+ return;
+ QTC_ASSERT(m_threadBox, return);
+ m_threadBox->setCurrentIndex(m_threadsHandler.currentThreadIndex());
+
+ const DebuggerState state = m_state;
+ const bool companionPreventsAction = m_engine->companionPreventsActions();
+
+ // Fixme: hint tr("Debugger is Busy");
+ // Exactly one of m_interuptAction and m_continueAction should be
+ // visible, possibly disabled.
+ if (state == DebuggerNotReady) {
+ // Happens when companion starts, otherwise this should not happen.
+ QTC_CHECK(m_companionEngine);
+ m_interruptAction.setVisible(true);
+ m_interruptAction.setEnabled(false);
+ m_continueAction.setVisible(false);
+ m_continueAction.setEnabled(false);
+ m_stepOverAction.setEnabled(true);
+ m_stepIntoAction.setEnabled(true);
+ m_stepOutAction.setEnabled(false);
+ m_exitAction.setEnabled(false);
+ m_debugWithoutDeployAction.setEnabled(true);
+ } else if (state == InferiorStopOk) {
+ // F5 continues, Shift-F5 kills. It is "continuable".
+ m_interruptAction.setVisible(false);
+ m_interruptAction.setEnabled(false);
+ m_continueAction.setVisible(true);
+ m_continueAction.setEnabled(!companionPreventsAction);
+ m_stepOverAction.setEnabled(!companionPreventsAction);
+ m_stepIntoAction.setEnabled(!companionPreventsAction);
+ m_stepOutAction.setEnabled(!companionPreventsAction);
+ m_exitAction.setEnabled(true);
+ m_debugWithoutDeployAction.setEnabled(false);
+ m_localsAndInspectorWindow->setShowLocals(true);
+ } else if (state == InferiorRunOk) {
+ // Shift-F5 interrupts. It is also "interruptible".
+ m_interruptAction.setVisible(true);
+ m_interruptAction.setEnabled(!companionPreventsAction);
+ m_continueAction.setVisible(false);
+ m_continueAction.setEnabled(false);
+ m_stepOverAction.setEnabled(false);
+ m_stepIntoAction.setEnabled(false);
+ m_stepOutAction.setEnabled(false);
+ m_exitAction.setEnabled(true);
+ m_debugWithoutDeployAction.setEnabled(false);
+ m_localsAndInspectorWindow->setShowLocals(false);
+ } else if (state == DebuggerFinished) {
+ const bool canRun = ProjectExplorerPlugin::canRunStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE);
+ // We don't want to do anything anymore.
+ m_interruptAction.setVisible(true);
+ m_interruptAction.setEnabled(false);
+ m_continueAction.setVisible(false);
+ m_continueAction.setEnabled(false);
+ m_stepOverAction.setEnabled(false);
+ m_stepIntoAction.setEnabled(false);
+ m_stepOutAction.setEnabled(false);
+ m_exitAction.setEnabled(false);
+ m_debugWithoutDeployAction.setEnabled(canRun);
+ setBusyCursor(false);
+ cleanupViews();
+ } else if (state == InferiorUnrunnable) {
+ // We don't want to do anything anymore.
+ m_interruptAction.setVisible(true);
+ m_interruptAction.setEnabled(false);
+ m_continueAction.setVisible(false);
+ m_continueAction.setEnabled(false);
+ m_stepOverAction.setEnabled(false);
+ m_stepIntoAction.setEnabled(false);
+ m_stepOutAction.setEnabled(false);
+ m_exitAction.setEnabled(true);
+ m_debugWithoutDeployAction.setEnabled(false);
+ // show locals in core dumps
+ m_localsAndInspectorWindow->setShowLocals(true);
+ } else {
+ // Everything else is "undisturbable".
+ m_interruptAction.setVisible(true);
+ m_interruptAction.setEnabled(false);
+ m_continueAction.setVisible(false);
+ m_continueAction.setEnabled(false);
+ m_stepOverAction.setEnabled(false);
+ m_stepIntoAction.setEnabled(false);
+ m_stepOutAction.setEnabled(false);
+ m_exitAction.setEnabled(false);
+ m_debugWithoutDeployAction.setEnabled(false);
+ }
+
+ m_attachToQmlPortAction.setEnabled(true);
+ m_attachToCoreAction.setEnabled(true);
+ m_attachToRemoteServerAction.setEnabled(true);
+
+ m_threadBox->setEnabled(state == InferiorStopOk || state == InferiorUnrunnable);
+ m_threadLabel->setEnabled(m_threadBox->isEnabled());
+
+ const bool isCore = m_engine->runParameters().startMode == AttachCore;
+ const bool stopped = state == InferiorStopOk;
+ const bool detachable = stopped && !isCore;
+ m_detachAction.setEnabled(detachable);
+
+ if (stopped)
+ QApplication::alert(mainWindow(), 3000);
+
+ updateReverseActions();
+
+ const bool canSnapshot = m_engine->hasCapability(SnapshotCapability);
+ m_snapshotAction.setVisible(canSnapshot);
+ m_snapshotAction.setEnabled(stopped && !isCore);
+
+ m_watchAction.setEnabled(true);
+ m_breakAction.setEnabled(true);
+
+ const bool canOperateByInstruction = m_engine->hasCapability(OperateByInstructionCapability);
+ m_operateByInstructionAction.setVisible(canOperateByInstruction);
+ m_operateByInstructionAction.setEnabled(canOperateByInstruction && (stopped || isCore));
+
+ m_abortAction.setEnabled(state != DebuggerNotReady
+ && state != DebuggerFinished);
+ m_resetAction.setEnabled((stopped || state == DebuggerNotReady)
+ && m_engine->hasCapability(ResetInferiorCapability));
+
+ m_stepIntoAction.setEnabled(stopped || state == DebuggerNotReady);
+ m_stepIntoAction.setToolTip(QString());
+
+ m_stepOverAction.setEnabled(stopped || state == DebuggerNotReady);
+ m_stepOverAction.setToolTip(QString());
+
+ m_stepOutAction.setEnabled(stopped);
+ m_runToLineAction.setEnabled(stopped && m_engine->hasCapability(RunToLineCapability));
+ m_runToSelectedFunctionAction.setEnabled(stopped);
+ m_returnFromFunctionAction.
+ setEnabled(stopped && m_engine->hasCapability(ReturnFromFunctionCapability));
+
+ const bool canJump = stopped && m_engine->hasCapability(JumpToLineCapability);
+ m_jumpToLineAction.setEnabled(canJump);
+
+ const bool actionsEnabled = m_engine->debuggerActionsEnabled();
+ const bool canDeref = actionsEnabled && m_engine->hasCapability(AutoDerefPointersCapability);
+ action(AutoDerefPointers)->setEnabled(canDeref);
+ action(AutoDerefPointers)->setEnabled(true);
+ action(ExpandStack)->setEnabled(actionsEnabled);
+
+ const bool notbusy = state == InferiorStopOk
+ || state == DebuggerNotReady
+ || state == DebuggerFinished
+ || state == InferiorUnrunnable;
+ setBusyCursor(!notbusy);
+
+ if (alsoUpdateCompanion && m_companionEngine)
+ m_companionEngine->updateState(false);
+}
+
+void DebuggerEnginePrivate::updateReverseActions()
+{
+ const bool stopped = m_state == InferiorStopOk;
+ const bool reverseEnabled = boolSetting(EnableReverseDebugging);
+ const bool canReverse = reverseEnabled && m_engine->hasCapability(ReverseSteppingCapability);
+ const bool doesRecord = m_recordForReverseOperationAction.isChecked();
+
+ m_recordForReverseOperationAction.setVisible(canReverse);
+ m_recordForReverseOperationAction.setEnabled(canReverse && stopped);
+ m_recordForReverseOperationAction.setIcon(doesRecord
+ ? Icons::RECORD_ON.icon()
+ : Icons::RECORD_OFF.icon());
+
+ m_operateInReverseDirectionAction.setVisible(canReverse);
+ m_operateInReverseDirectionAction.setEnabled(canReverse && stopped && doesRecord);
+ m_operateInReverseDirectionAction.setIcon(Icons::DIRECTION_BACKWARD.icon());
+ m_operateInReverseDirectionAction.setText(DebuggerEngine::tr("Operate in reverse direction"));
+}
+
+void DebuggerEnginePrivate::cleanupViews()
+{
+ const bool closeSource = boolSetting(CloseSourceBuffersOnExit);
+ const bool closeMemory = boolSetting(CloseMemoryBuffersOnExit);
+
+ QList<IDocument *> toClose;
+ foreach (IDocument *document, DocumentModel::openedDocuments()) {
+ const bool isMemory = document->property(Constants::OPENED_WITH_DISASSEMBLY).toBool();
+ if (document->property(Constants::OPENED_BY_DEBUGGER).toBool()) {
+ bool keepIt = true;
+ if (document->isModified())
+ keepIt = true;
+ else if (document->filePath().toString().contains("qeventdispatcher"))
+ keepIt = false;
+ else if (isMemory)
+ keepIt = !closeMemory;
+ else
+ keepIt = !closeSource;
+
+ if (keepIt)
+ document->setProperty(Constants::OPENED_BY_DEBUGGER, false);
+ else
+ toClose.append(document);
+ }
+ }
+ EditorManager::closeDocuments(toClose);
+}
+
+void DebuggerEnginePrivate::setBusyCursor(bool busy)
+{
+ //STATE_DEBUG("BUSY FROM: " << m_busy << " TO: " << busy);
+ if (m_isDying)
+ return;
+ if (busy == m_busy)
+ return;
+ m_busy = busy;
+ const QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
+ m_breakWindow->setCursor(cursor);
+ //m_consoleWindow->setCursor(cursor);
+ m_localsWindow->setCursor(cursor);
+ m_modulesWindow->setCursor(cursor);
+ m_logWindow->setCursor(cursor);
+ m_registerWindow->setCursor(cursor);
+ m_returnWindow->setCursor(cursor);
+ m_sourceFilesWindow->setCursor(cursor);
+ m_stackWindow->setCursor(cursor);
+ m_threadsWindow->setCursor(cursor);
+ m_watchersWindow->setCursor(cursor);
}
void DebuggerEngine::notifyInferiorShutdownFinished()
@@ -813,7 +1558,7 @@ void DebuggerEngine::notifyInferiorIll()
showMessage("NOTE: INFERIOR ILL");
// This can be issued in almost any state. The inferior could still be
// alive as some previous notifications might have been bogus.
- runTool()->startDying();
+ startDying();
if (state() == InferiorRunRequested) {
// We asked for running, but did not see a response.
// Assume the inferior is dead.
@@ -824,25 +1569,6 @@ void DebuggerEngine::notifyInferiorIll()
d->doShutdownInferior();
}
-void DebuggerEngine::shutdownSlaveEngine()
-{
- QTC_CHECK(isAllowedTransition(state(),EngineShutdownRequested));
- setState(EngineShutdownRequested);
- shutdownEngine();
-}
-
-void DebuggerEnginePrivate::doShutdownEngine()
-{
- // Slaves do not proceed by themselves.
- if (!isMasterEngine())
- return;
- m_engine->setState(EngineShutdownRequested);
- QTC_ASSERT(m_runTool, return);
- m_runTool->startDying();
- m_engine->showMessage("CALL: SHUTDOWN ENGINE");
- m_engine->shutdownEngine();
-}
-
void DebuggerEngine::notifyEngineShutdownFinished()
{
showMessage("NOTE: ENGINE SHUTDOWN FINISHED");
@@ -858,7 +1584,7 @@ void DebuggerEngine::notifyEngineIll()
// CALLGRIND_DUMP_STATS;
//#endif
showMessage("NOTE: ENGINE ILL ******");
- runTool()->startDying();
+ startDying();
switch (state()) {
case InferiorRunRequested:
case InferiorRunOk:
@@ -891,8 +1617,7 @@ void DebuggerEngine::notifyEngineSpontaneousShutdown()
#endif
showMessage("NOTE: ENGINE SPONTANEOUS SHUTDOWN");
setState(EngineShutdownFinished, true);
- if (isMasterEngine())
- d->doFinishDebugger();
+ d->doFinishDebugger();
}
void DebuggerEngine::notifyInferiorExited()
@@ -907,6 +1632,53 @@ void DebuggerEngine::notifyInferiorExited()
d->doShutdownEngine();
}
+void DebuggerEngine::updateState(bool alsoUpdateCompanion)
+{
+ d->updateState(alsoUpdateCompanion);
+}
+
+WatchTreeView *DebuggerEngine::inspectorView()
+{
+ return d->m_inspectorView;
+}
+
+void DebuggerEngine::showMessage(const QString &msg, int channel, int timeout) const
+{
+ //qDebug() << "PLUGIN OUTPUT: " << channel << msg;
+ QTC_ASSERT(d->m_logWindow, qDebug() << "MSG: " << msg; return);
+ switch (channel) {
+ case StatusBar:
+ d->m_logWindow->showInput(LogMisc, msg);
+ d->m_logWindow->showOutput(LogMisc, msg);
+ Debugger::showStatusMessage(msg, timeout);
+ break;
+ case LogMiscInput:
+ d->m_logWindow->showInput(LogMisc, msg);
+ d->m_logWindow->showOutput(LogMisc, msg);
+ break;
+ case LogInput:
+ d->m_logWindow->showInput(LogInput, msg);
+ d->m_logWindow->showOutput(LogInput, msg);
+ break;
+ case LogError:
+ d->m_logWindow->showInput(LogError, QLatin1String("ERROR: ") + msg);
+ d->m_logWindow->showOutput(LogError, QLatin1String("ERROR: ") + msg);
+ break;
+ case AppOutput:
+ case AppStuff:
+ d->m_logWindow->showOutput(channel, msg);
+ emit appendMessageRequested(msg, StdOutFormatSameLine, false);
+ break;
+ case AppError:
+ d->m_logWindow->showOutput(channel, msg);
+ emit appendMessageRequested(msg, StdErrFormatSameLine, false);
+ break;
+ default:
+ d->m_logWindow->showOutput(channel, msg);
+ break;
+ }
+}
+
void DebuggerEngine::notifyDebuggerProcessFinished(int exitCode,
QProcess::ExitStatus exitStatus, const QString &backendName)
{
@@ -930,7 +1702,7 @@ void DebuggerEngine::notifyDebuggerProcessFinished(int exitCode,
break;
default: {
// Initiate shutdown sequence
- masterEngine()->notifyInferiorIll();
+ notifyInferiorIll();
const QString msg = exitStatus == QProcess::CrashExit ?
tr("The %1 process terminated.") :
tr("The %2 process terminated unexpectedly (exit code %1).").arg(exitCode);
@@ -941,15 +1713,7 @@ void DebuggerEngine::notifyDebuggerProcessFinished(int exitCode,
}
}
-void DebuggerEngine::slaveEngineStateChanged(DebuggerEngine *slaveEngine,
- DebuggerState state)
-{
- Q_UNUSED(slaveEngine);
- Q_UNUSED(state);
-}
-
-static inline QString msgStateChanged(DebuggerState oldState, DebuggerState newState,
- bool forced, bool master)
+static QString msgStateChanged(DebuggerState oldState, DebuggerState newState, bool forced)
{
QString result;
QTextStream str(&result);
@@ -958,14 +1722,12 @@ static inline QString msgStateChanged(DebuggerState oldState, DebuggerState newS
str << " BY FORCE";
str << " from " << DebuggerEngine::stateName(oldState) << '(' << oldState
<< ") to " << DebuggerEngine::stateName(newState) << '(' << newState << ')';
- if (master)
- str << " [master]";
return result;
}
void DebuggerEngine::setState(DebuggerState state, bool forced)
{
- const QString msg = msgStateChanged(d->m_state, state, forced, isMasterEngine());
+ const QString msg = msgStateChanged(d->m_state, state, forced);
DebuggerState oldState = d->m_state;
d->m_state = state;
@@ -975,49 +1737,25 @@ void DebuggerEngine::setState(DebuggerState state, bool forced)
if (state == EngineRunRequested) {
DebuggerToolTipManager::registerEngine(this);
- }
-
- if (state == DebuggerFinished) {
- // Give up ownership on claimed breakpoints.
- for (Breakpoint bp : breakHandler()->engineBreakpoints(this))
- bp.notifyBreakpointReleased();
- DebuggerToolTipManager::deregisterEngine(this);
- d->m_memoryAgents.handleDebuggerFinished();
- prepareForRestart();
+ emit engineStarted();
}
showMessage(msg, LogDebug);
- updateViews();
- if (isSlaveEngine())
- masterEngine()->slaveEngineStateChanged(this, state);
-}
-
-void DebuggerEngine::updateViews()
-{
- // The slave engines are not entitled to change the view. Their wishes
- // should be coordinated by their master engine.
- Internal::updateState(runTool());
-}
-
-bool DebuggerEngine::isSlaveEngine() const
-{
- return d->m_masterEngine != nullptr;
-}
+ d->updateState(true);
-bool DebuggerEngine::isMasterEngine() const
-{
- return d->m_masterEngine == nullptr;
-}
+ if (oldState != d->m_state)
+ emit EngineManager::instance()->engineStateChanged(this);
-void DebuggerEngine::setMasterEngine(DebuggerEngine *masterEngine)
-{
- d->m_masterEngine = masterEngine;
+ if (state == DebuggerFinished) {
+ d->destroyPerspective();
+ emit engineFinished();
+ }
}
-DebuggerEngine *DebuggerEngine::masterEngine()
+bool DebuggerEngine::isPrimaryEngine() const
{
- return d->m_masterEngine ? d->m_masterEngine : this;
+ return d->m_isPrimaryEngine;
}
bool DebuggerEngine::canDisplayTooltip() const
@@ -1037,11 +1775,6 @@ QString DebuggerEngine::toFileInProject(const QUrl &fileUrl)
return d->m_fileFinder.findFile(fileUrl);
}
-void DebuggerEngine::removeBreakpointMarker(const Breakpoint &bp)
-{
- d->m_disassemblerAgent.removeBreakpointMarker(bp);
-}
-
QString DebuggerEngine::expand(const QString &string) const
{
return runParameters().macroExpander->expand(string);
@@ -1053,40 +1786,26 @@ QString DebuggerEngine::nativeStartupCommands() const
runParameters().additionalStartupCommands}).join('\n'));
}
-void DebuggerEngine::updateBreakpointMarker(const Breakpoint &bp)
+Perspective *DebuggerEngine::perspective() const
{
- d->m_disassemblerAgent.updateBreakpointMarker(bp);
+ return d->m_perspective;
+}
+
+void DebuggerEngine::updateMarkers()
+{
+ if (d->m_locationMark)
+ d->m_locationMark->updateIcon();
+
+ d->m_disassemblerAgent.updateLocationMarker();
}
bool DebuggerEngine::debuggerActionsEnabled() const
{
- return debuggerActionsEnabled(d->m_state);
+ return debuggerActionsEnabledHelper(d->m_state);
}
-bool DebuggerEngine::debuggerActionsEnabled(DebuggerState state)
+bool DebuggerEngine::companionPreventsActions() const
{
- switch (state) {
- case InferiorRunOk:
- case InferiorUnrunnable:
- case InferiorStopOk:
- return true;
- case InferiorStopRequested:
- case InferiorRunRequested:
- case InferiorRunFailed:
- case DebuggerNotReady:
- case EngineSetupRequested:
- case EngineSetupOk:
- case EngineSetupFailed:
- case EngineRunRequested:
- case EngineRunFailed:
- case InferiorStopFailed:
- case InferiorShutdownRequested:
- case InferiorShutdownFinished:
- case EngineShutdownRequested:
- case EngineShutdownFinished:
- case DebuggerFinished:
- return false;
- }
return false;
}
@@ -1096,7 +1815,6 @@ void DebuggerEngine::notifyInferiorPid(const ProcessHandle &pid)
return;
d->m_inferiorPid = pid;
if (pid.isValid()) {
- d->m_runTool->runControl()->setApplicationProcessHandle(pid);
showMessage(tr("Taking notice of pid %1").arg(pid.pid()));
DebuggerStartMode sm = runParameters().startMode;
if (sm == StartInternal || sm == StartExternal || sm == AttachExternal)
@@ -1111,21 +1829,30 @@ qint64 DebuggerEngine::inferiorPid() const
bool DebuggerEngine::isReverseDebugging() const
{
- return Internal::isReverseDebugging();
+ return d->m_operateInReverseDirectionAction.isChecked();
}
-void DebuggerEngine::showMessage(const QString &msg, int channel, int timeout) const
+void DebuggerEngine::handleBeginOfRecordingReached()
{
- if (DebuggerRunTool *tool = runTool())
- tool->showMessage(msg, channel, timeout);
+ showStatusMessage(tr("Reverse-execution history exhausted. Going forward again."));
+ d->m_operateInReverseDirectionAction.setChecked(false);
+ d->updateReverseActions();
+}
+
+void DebuggerEngine::handleRecordingFailed()
+{
+ showStatusMessage(tr("Reverse-execution recording failed.."));
+ d->m_operateInReverseDirectionAction.setChecked(false);
+ d->m_recordForReverseOperationAction.setChecked(false);
+ d->updateReverseActions();
+ executeRecordReverse(false);
}
// Called by DebuggerRunControl.
void DebuggerEngine::quitDebugger()
{
showMessage(QString("QUIT DEBUGGER REQUESTED IN STATE %1").arg(state()));
- QTC_ASSERT(runTool(), return);
- runTool()->startDying();
+ startDying();
switch (state()) {
case InferiorStopOk:
case InferiorStopFailed:
@@ -1134,7 +1861,7 @@ void DebuggerEngine::quitDebugger()
break;
case InferiorRunOk:
setState(InferiorStopRequested);
- showStatusMessage(tr("Attempting to interrupt."));
+ showMessage(tr("Attempting to interrupt."), StatusBar);
interruptInferior();
break;
case EngineSetupRequested:
@@ -1162,11 +1889,10 @@ void DebuggerEngine::quitDebugger()
void DebuggerEngine::requestInterruptInferior()
{
- QTC_CHECK(isMasterEngine());
QTC_ASSERT(state() == InferiorRunOk, qDebug() << this << state());
setState(InferiorStopRequested);
showMessage("CALL: INTERRUPT INFERIOR");
- showStatusMessage(tr("Attempting to interrupt."));
+ showMessage(tr("Attempting to interrupt."), StatusBar);
interruptInferior();
}
@@ -1176,15 +1902,24 @@ void DebuggerEngine::progressPing()
d->m_progress.setProgressValue(progress);
}
-DebuggerRunTool *DebuggerEngine::runTool() const
+bool DebuggerEngine::isStartupRunConfiguration() const
+{
+ return d->m_runConfiguration == RunConfiguration::startupRunConfiguration();
+}
+
+void DebuggerEngine::setCompanionEngine(DebuggerEngine *engine)
+{
+ d->m_companionEngine = engine;
+}
+
+void DebuggerEngine::setSecondaryEngine()
{
- return d->m_runTool.data();
+ d->m_isPrimaryEngine = false;
}
TerminalRunner *DebuggerEngine::terminal() const
{
- QTC_ASSERT(d->m_runTool, return nullptr);
- return d->m_runTool->terminalRunner();
+ return d->m_terminalRunner;
}
void DebuggerEngine::selectWatchData(const QString &)
@@ -1199,7 +1934,7 @@ void DebuggerEngine::watchPoint(const QPoint &pnt)
cmd.callback = [this](const DebuggerResponse &response) {
qulonglong addr = response.data["selected"].toAddress();
if (addr == 0)
- showStatusMessage(tr("Could not find a widget."));
+ showMessage(tr("Could not find a widget."), StatusBar);
// Add the watcher entry nevertheless, as that's the place where
// the user expects visual feedback.
watchHandler()->watchExpression(response.data["expr"].data(), QString(), true);
@@ -1297,112 +2032,42 @@ void DebuggerEngine::updateAll()
{
}
-#if 0
- // FIXME: Remove explicit use of BreakpointData
- if (!bp->engine && acceptsBreakpoint(id)) {
- QTC_CHECK(state == BreakpointNew);
- // Take ownership of the breakpoint.
- bp->engine = this;
- }
-#endif
-
-void DebuggerEngine::attemptBreakpointSynchronization()
+QString DebuggerEngine::displayName() const
{
- showMessage("ATTEMPT BREAKPOINT SYNCHRONIZATION");
- if (!stateAcceptsBreakpointChanges()) {
- showMessage("BREAKPOINT SYNCHRONIZATION NOT POSSIBLE IN CURRENT STATE");
- return;
- }
-
- BreakHandler *handler = breakHandler();
-
- for (Breakpoint bp : handler->unclaimedBreakpoints()) {
- // Take ownership of the breakpoint. Requests insertion.
- if (acceptsBreakpoint(bp)) {
- showMessage(QString("TAKING OWNERSHIP OF BREAKPOINT %1 IN STATE %2")
- .arg(bp.id().toString()).arg(bp.state()));
- bp.setEngine(this);
- } else {
- showMessage(QString("BREAKPOINT %1 IN STATE %2 IS NOT ACCEPTABLE")
- .arg(bp.id().toString()).arg(bp.state()));
- }
- }
-
- bool done = true;
- for (Breakpoint bp : handler->engineBreakpoints(this)) {
- switch (bp.state()) {
- case BreakpointNew:
- // Should not happen once claimed.
- QTC_CHECK(false);
- continue;
- case BreakpointInsertRequested:
- done = false;
- insertBreakpoint(bp);
- continue;
- case BreakpointChangeRequested:
- done = false;
- changeBreakpoint(bp);
- continue;
- case BreakpointRemoveRequested:
- done = false;
- removeBreakpoint(bp);
- continue;
- case BreakpointChangeProceeding:
- case BreakpointInsertProceeding:
- case BreakpointRemoveProceeding:
- done = false;
- //qDebug() << "BREAKPOINT " << id << " STILL IN PROGRESS, STATE"
- // << handler->state(id);
- continue;
- case BreakpointInserted:
- //qDebug() << "BREAKPOINT " << id << " IS GOOD";
- continue;
- case BreakpointDead:
- // Can happen temporarily during Breakpoint destruction.
- // BreakpointItem::deleteThis() intentionally lets the event loop run,
- // during which an attemptBreakpointSynchronization() might kick in.
- continue;
- }
- }
-
- if (done)
- showMessage("BREAKPOINTS ARE SYNCHRONIZED");
- else
- showMessage("BREAKPOINTS ARE NOT FULLY SYNCHRONIZED");
-}
-
-bool DebuggerEngine::acceptsBreakpoint(Breakpoint bp) const
-{
- Q_UNUSED(bp);
- return false;
+ //: e.g. LLDB for "myproject", shows up i
+ return tr("%1 for \"%2\"").arg(d->m_debuggerName, runParameters().displayName);
}
-void DebuggerEngine::insertBreakpoint(Breakpoint bp)
+void DebuggerEngine::insertBreakpoint(const Breakpoint &bp)
{
- BreakpointState state = bp.state();
- QTC_ASSERT(state == BreakpointInsertRequested,
- qDebug() << bp.id() << this << state);
+ QTC_ASSERT(bp, return);
+ BreakpointState state = bp->state();
+ QTC_ASSERT(state == BreakpointInsertionRequested,
+ qDebug() << bp->modelId() << this << state);
QTC_CHECK(false);
}
-void DebuggerEngine::removeBreakpoint(Breakpoint bp)
+void DebuggerEngine::removeBreakpoint(const Breakpoint &bp)
{
- BreakpointState state = bp.state();
+ QTC_ASSERT(bp, return);
+ BreakpointState state = bp->state();
QTC_ASSERT(state == BreakpointRemoveRequested,
- qDebug() << bp.id() << this << state);
+ qDebug() << bp->responseId() << this << state);
QTC_CHECK(false);
}
-void DebuggerEngine::changeBreakpoint(Breakpoint bp)
+void DebuggerEngine::updateBreakpoint(const Breakpoint &bp)
{
- BreakpointState state = bp.state();
- QTC_ASSERT(state == BreakpointChangeRequested,
- qDebug() << bp.id() << this << state);
+ QTC_ASSERT(bp, return);
+ BreakpointState state = bp->state();
+ QTC_ASSERT(state == BreakpointUpdateRequested,
+ qDebug() << bp->responseId() << this << state);
QTC_CHECK(false);
}
-void DebuggerEngine::enableSubBreakpoint(const QString &, bool)
+void DebuggerEngine::enableSubBreakpoint(const SubBreakpoint &sbp, bool)
{
+ QTC_ASSERT(sbp, return);
QTC_CHECK(false);
}
@@ -1411,67 +2076,27 @@ void DebuggerEngine::assignValueInDebugger(WatchItem *,
{
}
-void DebuggerEngine::detachDebugger()
-{
-}
-
-void DebuggerEngine::executeStep()
-{
-}
-
-void DebuggerEngine::executeStepOut()
-{
-}
-
-void DebuggerEngine::executeNext()
-{
-}
-
-void DebuggerEngine::executeStepI()
-{
-}
-
-void DebuggerEngine::executeNextI()
-{
-}
-
-void DebuggerEngine::executeReturn()
-{
-}
-
-void DebuggerEngine::continueInferior()
-{
-}
-
-void DebuggerEngine::interruptInferior()
-{
-}
-
-void DebuggerEngine::executeRunToLine(const ContextData &)
+void DebuggerEngine::handleRecordReverse(bool record)
{
+ executeRecordReverse(record);
+ d->updateReverseActions();
}
-void DebuggerEngine::executeRunToFunction(const QString &)
+void DebuggerEngine::handleReverseDirection(bool reverse)
{
+ executeReverse(reverse);
+ updateMarkers();
+ d->updateReverseActions();
}
-void DebuggerEngine::executeJumpToLine(const ContextData &)
+void DebuggerEngine::executeDebuggerCommand(const QString &)
{
-}
-
-void DebuggerEngine::executeDebuggerCommand(const QString &, DebuggerLanguages)
-{
- showStatusMessage(tr("This debugger cannot handle user input."));
-}
-
-BreakHandler *DebuggerEngine::breakHandler() const
-{
- return Internal::breakHandler();
+ showMessage(tr("This debugger cannot handle user input."), StatusBar);
}
bool DebuggerEngine::isDying() const
{
- return !runTool() || runTool()->isDying();
+ return d->m_isDying;
}
QString DebuggerEngine::msgStopped(const QString &reason)
@@ -1535,22 +2160,379 @@ void DebuggerEngine::updateMemoryViews()
void DebuggerEngine::openDisassemblerView(const Location &location)
{
- auto agent = new DisassemblerAgent(this);
+ DisassemblerAgent *agent = new DisassemblerAgent(this);
agent->setLocation(location);
}
-void DebuggerRunParameters::validateExecutable()
+void DebuggerEngine::raiseWatchersWindow()
+{
+ if (d->m_watchersView) {
+ if (auto dock = qobject_cast<QDockWidget *>(d->m_watchersView->parentWidget())) {
+ if (QAction *act = dock->toggleViewAction()) {
+ if (!act->isChecked())
+ QTimer::singleShot(1, act, [act] { act->trigger(); });
+ dock->raise();
+ }
+ }
+ }
+}
+
+void DebuggerEngine::openMemoryEditor()
+{
+ AddressDialog dialog;
+ if (dialog.exec() != QDialog::Accepted)
+ return;
+ MemoryViewSetupData data;
+ data.startAddress = dialog.address();
+ openMemoryView(data);
+}
+
+void DebuggerEngine::updateLocalsView(const GdbMi &all)
{
+ WatchHandler *handler = watchHandler();
+
+ const GdbMi typeInfo = all["typeinfo"];
+ handler->recordTypeInfo(typeInfo);
+
+ const GdbMi data = all["data"];
+ handler->insertItems(data);
+
+ const GdbMi ns = all["qtnamespace"];
+ if (ns.isValid()) {
+ setQtNamespace(ns.data());
+ showMessage("FOUND NAMESPACED QT: " + ns.data());
+ }
+
+ static int count = 0;
+ showMessage(QString("<Rebuild Watchmodel %1 @ %2 >")
+ .arg(++count).arg(LogWindow::logTimeStamp()), LogMiscInput);
+ showMessage(tr("Finished retrieving data"), 400, StatusBar);
+
+ DebuggerToolTipManager::updateEngine(this);
+
+ const bool partial = all["partial"].toInt();
+ if (!partial)
+ updateMemoryViews();
+}
+
+bool DebuggerEngine::canHandleToolTip(const DebuggerToolTipContext &context) const
+{
+ return state() == InferiorStopOk && context.isCppEditor;
+}
+
+void DebuggerEngine::updateItem(const QString &iname)
+{
+ if (d->m_lookupRequests.contains(iname)) {
+ showMessage(QString("IGNORING REPEATED REQUEST TO EXPAND " + iname));
+ WatchHandler *handler = watchHandler();
+ WatchItem *item = handler->findItem(iname);
+ QTC_CHECK(item);
+ WatchModelBase *model = handler->model();
+ QTC_CHECK(model);
+ if (item && !model->hasChildren(model->indexForItem(item))) {
+ handler->notifyUpdateStarted(UpdateParameters(iname));
+ item->setValue(decodeData({}, "notaccessible"));
+ item->setHasChildren(false);
+ item->outdated = false;
+ item->update();
+ handler->notifyUpdateFinished();
+ return;
+ }
+ // We could legitimately end up here after expanding + closing + re-expaning an item.
+ }
+ d->m_lookupRequests.insert(iname);
+
+ UpdateParameters params;
+ params.partialVariable = iname;
+ doUpdateLocals(params);
+}
+
+void DebuggerEngine::updateWatchData(const QString &iname)
+{
+ // This is used in cases where re-evaluation is ok for the same iname
+ // e.g. when changing the expression in a watcher.
+ UpdateParameters params;
+ params.partialVariable = iname;
+ doUpdateLocals(params);
+}
+
+void DebuggerEngine::expandItem(const QString &iname)
+{
+ updateItem(iname);
+}
+
+void DebuggerEngine::handleExecDetach()
+{
+ resetLocation();
+ detachDebugger();
+}
+
+void DebuggerEngine::handleExecContinue()
+{
+ resetLocation();
+ continueInferior();
+}
+
+void DebuggerEngine::handleExecInterrupt()
+{
+ resetLocation();
+ requestInterruptInferior();
+}
+
+void DebuggerEngine::handleReset()
+{
+ resetLocation();
+ resetInferior();
+}
+
+void DebuggerEngine::handleExecStep()
+{
+ if (state() == DebuggerNotReady) {
+ DebuggerRunTool::setBreakOnMainNextTime();
+ ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE);
+ } else {
+ resetLocation();
+ if (d->operatesByInstruction())
+ executeStepI();
+ else
+ executeStep();
+ }
+}
+
+void DebuggerEngine::handleExecNext()
+{
+ if (state() == DebuggerNotReady) {
+ DebuggerRunTool::setBreakOnMainNextTime();
+ ProjectExplorerPlugin::runStartupProject(ProjectExplorer::Constants::DEBUG_RUN_MODE);
+ } else {
+ resetLocation();
+ if (d->operatesByInstruction())
+ executeNextI();
+ else
+ executeNext();
+ }
+}
+
+void DebuggerEngine::handleExecStepOut()
+{
+ resetLocation();
+ executeStepOut();
+}
+
+void DebuggerEngine::handleExecReturn()
+{
+ resetLocation();
+ executeReturn();
+}
+
+void DebuggerEngine::handleExecJumpToLine()
+{
+ resetLocation();
+ if (BaseTextEditor *textEditor = BaseTextEditor::currentTextEditor()) {
+ ContextData location = getLocationContext(textEditor->textDocument(),
+ textEditor->currentLine());
+ if (location.isValid())
+ executeJumpToLine(location);
+ }
+}
+
+void DebuggerEngine::handleExecRunToLine()
+{
+ resetLocation();
+ if (BaseTextEditor *textEditor = BaseTextEditor::currentTextEditor()) {
+ ContextData location = getLocationContext(textEditor->textDocument(),
+ textEditor->currentLine());
+ if (location.isValid())
+ executeRunToLine(location);
+ }
+}
+
+void DebuggerEngine::handleExecRunToSelectedFunction()
+{
+ BaseTextEditor *textEditor = BaseTextEditor::currentTextEditor();
+ QTC_ASSERT(textEditor, return);
+ QTextCursor cursor = textEditor->textCursor();
+ QString functionName = cursor.selectedText();
+ if (functionName.isEmpty()) {
+ const QTextBlock block = cursor.block();
+ const QString line = block.text();
+ foreach (const QString &str, line.trimmed().split(QLatin1Char('('))) {
+ QString a;
+ for (int i = str.size(); --i >= 0; ) {
+ if (!str.at(i).isLetterOrNumber())
+ break;
+ a = str.at(i) + a;
+ }
+ if (!a.isEmpty()) {
+ functionName = a;
+ break;
+ }
+ }
+ }
+
+ if (functionName.isEmpty()) {
+ showMessage(tr("No function selected."), StatusBar);
+ } else {
+ showMessage(tr("Running to function \"%1\".").arg(functionName), StatusBar);
+ resetLocation();
+ executeRunToFunction(functionName);
+ }
+}
+
+void DebuggerEngine::handleAddToWatchWindow()
+{
+ // Requires a selection, but that's the only case we want anyway.
+ BaseTextEditor *textEditor = BaseTextEditor::currentTextEditor();
+ if (!textEditor)
+ return;
+ QTextCursor tc = textEditor->textCursor();
+ QString exp;
+ if (tc.hasSelection()) {
+ exp = tc.selectedText();
+ } else {
+ int line, column;
+ exp = cppExpressionAt(textEditor->editorWidget(), tc.position(), &line, &column);
+ }
+ if (hasCapability(WatchComplexExpressionsCapability))
+ exp = removeObviousSideEffects(exp);
+ else
+ exp = fixCppExpression(exp);
+ exp = exp.trimmed();
+ if (exp.isEmpty()) {
+ // Happens e.g. when trying to evaluate 'char' or 'return'.
+ AsynchronousMessageBox::warning(tr("Warning"),
+ tr("Select a valid expression to evaluate."));
+ return;
+ }
+ watchHandler()->watchVariable(exp);
+}
+
+void DebuggerEngine::handleFrameDown()
+{
+ frameDown();
+}
+
+void DebuggerEngine::handleFrameUp()
+{
+ frameUp();
+}
+
+void DebuggerEngine::checkState(DebuggerState state, const char *file, int line)
+{
+ const DebuggerState current = d->m_state;
+ if (current == state)
+ return;
+
+ QString msg = QString("UNEXPECTED STATE: %1 WANTED: %2 IN %3:%4")
+ .arg(stateName(current)).arg(stateName(state)).arg(QLatin1String(file)).arg(line);
+
+ showMessage(msg, LogError);
+ qDebug("%s", qPrintable(msg));
+}
+
+bool DebuggerEngine::isNativeMixedEnabled() const
+{
+ return d->m_runParameters.isNativeMixedDebugging();
+}
+
+bool DebuggerEngine::isNativeMixedActive() const
+{
+ return isNativeMixedEnabled(); //&& boolSetting(OperateNativeMixed);
+}
+
+bool DebuggerEngine::isNativeMixedActiveFrame() const
+{
+ if (!isNativeMixedActive())
+ return false;
+ if (stackHandler()->frames().isEmpty())
+ return false;
+ StackFrame frame = stackHandler()->frameAt(0);
+ return frame.language == QmlLanguage;
+}
+
+void DebuggerEngine::startDying() const
+{
+ d->m_isDying = true;
+ if (DebuggerEngine *other = d->m_companionEngine)
+ other->d->m_isDying = true;
+}
+
+bool DebuggerRunParameters::isCppDebugging() const
+{
+ return cppEngineType == GdbEngineType
+ || cppEngineType == LldbEngineType
+ || cppEngineType == CdbEngineType;
+}
+
+bool DebuggerRunParameters::isNativeMixedDebugging() const
+{
+ return nativeMixedEnabled && isCppDebugging() && isQmlDebugging;
+}
+
+QString DebuggerEngine::formatStartParameters() const
+{
+ const DebuggerRunParameters &sp = d->m_runParameters;
+ QString rc;
+ QTextStream str(&rc);
+ str << "Start parameters: '" << sp.displayName << "' mode: " << sp.startMode
+ << "\nABI: " << sp.toolChainAbi.toString() << '\n';
+ str << "Languages: ";
+ if (sp.isCppDebugging())
+ str << "c++ ";
+ if (sp.isQmlDebugging)
+ str << "qml";
+ str << '\n';
+ if (!sp.inferior.executable.isEmpty()) {
+ str << "Executable: " << QDir::toNativeSeparators(sp.inferior.executable)
+ << ' ' << sp.inferior.commandLineArguments;
+ if (d->m_terminalRunner)
+ str << " [terminal]";
+ str << '\n';
+ if (!sp.inferior.workingDirectory.isEmpty())
+ str << "Directory: " << QDir::toNativeSeparators(sp.inferior.workingDirectory)
+ << '\n';
+ }
+ QString cmd = sp.debugger.executable;
+ if (!cmd.isEmpty())
+ str << "Debugger: " << QDir::toNativeSeparators(cmd) << '\n';
+ if (!sp.coreFile.isEmpty())
+ str << "Core: " << QDir::toNativeSeparators(sp.coreFile) << '\n';
+ if (sp.attachPID.isValid())
+ str << "PID: " << sp.attachPID.pid() << ' ' << sp.crashParameter << '\n';
+ if (!sp.projectSourceDirectory.isEmpty()) {
+ str << "Project: " << QDir::toNativeSeparators(sp.projectSourceDirectory) << '\n';
+ str << "Additional Search Directories:"
+ << sp.additionalSearchDirectories.join(QLatin1Char(' ')) << '\n';
+ }
+ if (!sp.remoteChannel.isEmpty())
+ str << "Remote: " << sp.remoteChannel << '\n';
+ if (!sp.qmlServer.host().isEmpty())
+ str << "QML server: " << sp.qmlServer.host() << ':' << sp.qmlServer.port() << '\n';
+ str << "Sysroot: " << sp.sysRoot << '\n';
+ str << "Debug Source Location: " << sp.debugSourceLocation.join(QLatin1Char(':')) << '\n';
+ return rc;
+}
+
+// CppDebuggerEngine
+
+Context CppDebuggerEngine::languageContext() const
+{
+ return Context(Constants::C_CPPDEBUGGER);
+}
+
+void CppDebuggerEngine::validateExecutable()
+{
+ DebuggerRunParameters &rp = mutableRunParameters();
const bool warnOnRelease = boolSetting(WarnOnReleaseBuilds);
bool warnOnInappropriateDebugger = false;
QString detailedWarning;
- switch (toolChainAbi.binaryFormat()) {
+ switch (rp.toolChainAbi.binaryFormat()) {
case Abi::PEFormat: {
QString preferredDebugger;
- if (toolChainAbi.osFlavor() == Abi::WindowsMSysFlavor) {
- if (cppEngineType == CdbEngineType)
+ if (rp.toolChainAbi.osFlavor() == Abi::WindowsMSysFlavor) {
+ if (rp.cppEngineType == CdbEngineType)
preferredDebugger = "GDB";
- } else if (cppEngineType != CdbEngineType) {
+ } else if (rp.cppEngineType != CdbEngineType) {
// osFlavor() is MSVC, so the recommended debugger is CDB
preferredDebugger = "CDB";
}
@@ -1562,12 +2544,12 @@ void DebuggerRunParameters::validateExecutable()
"experience for this binary format.").arg(preferredDebugger);
break;
}
- if (warnOnRelease && cppEngineType == CdbEngineType) {
- if (!symbolFile.endsWith(".exe", Qt::CaseInsensitive))
- symbolFile.append(".exe");
+ if (warnOnRelease && rp.cppEngineType == CdbEngineType) {
+ if (!rp.symbolFile.endsWith(".exe", Qt::CaseInsensitive))
+ rp.symbolFile.append(".exe");
QString errorMessage;
QStringList rc;
- if (getPDBFiles(symbolFile, &rc, &errorMessage) && !rc.isEmpty())
+ if (getPDBFiles(rp.symbolFile, &rc, &errorMessage) && !rc.isEmpty())
return;
if (!errorMessage.isEmpty()) {
detailedWarning.append('\n');
@@ -1579,7 +2561,7 @@ void DebuggerRunParameters::validateExecutable()
break;
}
case Abi::ElfFormat: {
- if (cppEngineType == CdbEngineType) {
+ if (rp.cppEngineType == CdbEngineType) {
warnOnInappropriateDebugger = true;
detailedWarning = DebuggerEngine::tr(
"The inferior is in the ELF format.\n"
@@ -1588,11 +2570,11 @@ void DebuggerRunParameters::validateExecutable()
break;
}
- Utils::ElfReader reader(symbolFile);
+ Utils::ElfReader reader(rp.symbolFile);
const ElfData elfData = reader.readHeaders();
const QString error = reader.errorString();
- Internal::showMessage("EXAMINING " + symbolFile, LogDebug);
+ showMessage("EXAMINING " + rp.symbolFile, LogDebug);
QByteArray msg = "ELF SECTIONS: ";
static const QList<QByteArray> interesting = {
@@ -1615,15 +2597,15 @@ void DebuggerRunParameters::validateExecutable()
if (interesting.contains(header.name))
seen.insert(header.name);
}
- Internal::showMessage(QString::fromUtf8(msg), LogDebug);
+ showMessage(QString::fromUtf8(msg), LogDebug);
if (!error.isEmpty()) {
- Internal::showMessage("ERROR WHILE READING ELF SECTIONS: " + error, LogDebug);
+ showMessage("ERROR WHILE READING ELF SECTIONS: " + error, LogDebug);
return;
}
if (elfData.sectionHeaders.isEmpty()) {
- Internal::showMessage("NO SECTION HEADERS FOUND. IS THIS AN EXECUTABLE?", LogDebug);
+ showMessage("NO SECTION HEADERS FOUND. IS THIS AN EXECUTABLE?", LogDebug);
return;
}
@@ -1654,7 +2636,7 @@ void DebuggerRunParameters::validateExecutable()
QRegExp exp = itExp->first;
int index = exp.indexIn(string);
if (index != -1) {
- sourcePathMap.insert(string.left(index) + exp.cap(1), itExp->second);
+ rp.sourcePathMap.insert(string.left(index) + exp.cap(1), itExp->second);
found = true;
break;
}
@@ -1696,127 +2678,6 @@ void DebuggerRunParameters::validateExecutable()
}
}
-void DebuggerEngine::updateLocalsView(const GdbMi &all)
-{
- WatchHandler *handler = watchHandler();
-
- const GdbMi typeInfo = all["typeinfo"];
- handler->recordTypeInfo(typeInfo);
-
- const GdbMi data = all["data"];
- handler->insertItems(data);
-
- const GdbMi ns = all["qtnamespace"];
- if (ns.isValid()) {
- setQtNamespace(ns.data());
- showMessage("FOUND NAMESPACED QT: " + ns.data());
- }
-
- static int count = 0;
- showMessage(QString("<Rebuild Watchmodel %1 @ %2 >")
- .arg(++count).arg(LogWindow::logTimeStamp()), LogMiscInput);
- showStatusMessage(tr("Finished retrieving data"), 400);
-
- DebuggerToolTipManager::updateEngine(this);
-
- const bool partial = all["partial"].toInt();
- if (!partial)
- updateMemoryViews();
-}
-
-bool DebuggerEngine::canHandleToolTip(const DebuggerToolTipContext &context) const
-{
- return state() == InferiorStopOk && context.isCppEditor;
-}
-
-void DebuggerEngine::updateItem(const QString &iname)
-{
- if (d->m_lookupRequests.contains(iname)) {
- showMessage(QString("IGNORING REPEATED REQUEST TO EXPAND " + iname));
- WatchHandler *handler = watchHandler();
- WatchItem *item = handler->findItem(iname);
- QTC_CHECK(item);
- WatchModelBase *model = handler->model();
- QTC_CHECK(model);
- if (item && !model->hasChildren(model->indexForItem(item))) {
- handler->notifyUpdateStarted(UpdateParameters(iname));
- item->setValue(decodeData({}, "notaccessible"));
- item->setHasChildren(false);
- item->outdated = false;
- item->update();
- handler->notifyUpdateFinished();
- return;
- }
- // We could legitimately end up here after expanding + closing + re-expaning an item.
- }
- d->m_lookupRequests.insert(iname);
-
- UpdateParameters params;
- params.partialVariable = iname;
- doUpdateLocals(params);
-}
-
-void DebuggerEngine::updateWatchData(const QString &iname)
-{
- // This is used in cases where re-evaluation is ok for the same iname
- // e.g. when changing the expression in a watcher.
- UpdateParameters params;
- params.partialVariable = iname;
- doUpdateLocals(params);
-}
-
-void DebuggerEngine::expandItem(const QString &iname)
-{
- updateItem(iname);
-}
-
-void DebuggerEngine::checkState(DebuggerState state, const char *file, int line)
-{
- const DebuggerState current = d->m_state;
- if (current == state)
- return;
-
- QString msg = QString("UNEXPECTED STATE: %1 WANTED: %2 IN %3:%4")
- .arg(stateName(current)).arg(stateName(state)).arg(QLatin1String(file)).arg(line);
-
- showMessage(msg, LogError);
- qDebug("%s", qPrintable(msg));
-}
-
-bool DebuggerEngine::isNativeMixedEnabled() const
-{
- if (DebuggerRunTool *rt = runTool())
- return rt->runParameters().isNativeMixedDebugging();
- return false;
-}
-
-bool DebuggerEngine::isNativeMixedActive() const
-{
- return isNativeMixedEnabled(); //&& boolSetting(OperateNativeMixed);
-}
-
-bool DebuggerEngine::isNativeMixedActiveFrame() const
-{
- if (!isNativeMixedActive())
- return false;
- if (stackHandler()->frames().isEmpty())
- return false;
- StackFrame frame = stackHandler()->frameAt(0);
- return frame.language == QmlLanguage;
-}
-
-bool DebuggerRunParameters::isCppDebugging() const
-{
- return cppEngineType == CdbEngineType
- || cppEngineType == GdbEngineType
- || cppEngineType == LldbEngineType;
-}
-
-bool DebuggerRunParameters::isNativeMixedDebugging() const
-{
- return nativeMixedEnabled && isCppDebugging() && isQmlDebugging;
-}
-
} // namespace Internal
} // namespace Debugger