diff options
Diffstat (limited to 'src/plugins/debugger/debuggerengine.cpp')
-rw-r--r-- | src/plugins/debugger/debuggerengine.cpp | 1943 |
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 |