/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ****************************************************************************/ #include "debuggerengine.h" #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 "enginemanager.h" #include "memoryagent.h" #include "moduleshandler.h" #include "registerhandler.h" #include "peripheralregisterhandler.h" #include "sourcefileshandler.h" #include "sourceutils.h" #include "stackhandler.h" #include "stackwindow.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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Core; using namespace Debugger::Internal; using namespace ProjectExplorer; using namespace TextEditor; using namespace Utils; //#define WITH_BENCHMARK #ifdef WITH_BENCHMARK #include #endif namespace Debugger { QDebug operator<<(QDebug d, DebuggerState state) { //return d << DebuggerEngine::stateName(state) << '(' << int(state) << ')'; return d << DebuggerEngine::stateName(state); } QDebug operator<<(QDebug str, const DebuggerRunParameters &sp) { QDebug nospace = str.nospace(); nospace << "executable=" << sp.inferior.executable << " coreFile=" << sp.coreFile << " processArgs=" << sp.inferior.commandLineArguments << " inferior environment=<" << sp.inferior.environment.size() << " variables>" << " debugger environment=<" << sp.debugger.environment.size() << " variables>" << " workingDir=" << sp.inferior.workingDirectory << " attachPID=" << sp.attachPID.pid() << " remoteChannel=" << sp.remoteChannel << " abi=" << sp.toolChainAbi.toString() << '\n'; return str; } 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 = Utils::FilePath::fromString(frame.file); m_lineNumber = frame.line; m_needsMarker = marker; m_functionName = frame.function; m_hasDebugInfo = frame.isUsable(); m_address = frame.address; m_from = frame.module; } LocationMark::LocationMark(DebuggerEngine *engine, const FilePath &file, int line) : TextMark(file, line, Constants::TEXT_MARK_CATEGORY_LOCATION), m_engine(engine) { 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 && m_engine->hasCapability(JumpToLineCapability); } void LocationMark::dragToLine(int line) { if (m_engine) { if (BaseTextEditor *textEditor = BaseTextEditor::currentTextEditor()) { ContextData location = getLocationContext(textEditor->textDocument(), line); if (location.isValid()) m_engine->executeJumpToLine(location); } } } ////////////////////////////////////////////////////////////////////// // // MemoryAgentSet // ////////////////////////////////////////////////////////////////////// class MemoryAgentSet { public: ~MemoryAgentSet() { qDeleteAll(m_agents); m_agents.clear(); } // Called by engine to create a new view. void createBinEditor(const MemoryViewSetupData &data, DebuggerEngine *engine) { auto agent = new MemoryAgent(data, engine); if (agent->isUsable()) { m_agents.push_back(agent); } else { delete agent; AsynchronousMessageBox::warning( DebuggerEngine::tr("No Memory Viewer Available"), DebuggerEngine::tr("The memory contents cannot be shown as no viewer plugin " "for binary data has been loaded.")); } } // On stack frame completed and on request. void updateContents() { for (MemoryAgent *agent : m_agents) { if (agent) agent->updateContents(); } } void handleDebuggerFinished() { for (MemoryAgent *agent : m_agents) { if (agent) agent->setFinished(); // Prevent triggering updates, etc. } } private: std::vector m_agents; }; ////////////////////////////////////////////////////////////////////// // // DebuggerEnginePrivate // ////////////////////////////////////////////////////////////////////// class DebuggerEnginePrivate : public QObject { Q_OBJECT public: DebuggerEnginePrivate(DebuggerEngine *engine) : m_engine(engine), m_breakHandler(engine), m_modulesHandler(engine), m_registerHandler(engine), m_peripheralRegisterHandler(engine), m_sourceFilesHandler(engine), m_stackHandler(engine), m_threadsHandler(engine), m_watchHandler(engine), m_disassemblerAgent(engine), m_toolTipManager(engine) { m_debuggerName = DebuggerEngine::tr("Debugger"); m_logWindow = new LogWindow(m_engine); // Needed before start() m_logWindow->setObjectName("Debugger.Dock.Output"); connect(action(EnableReverseDebugging), &SavedAction::valueChanged, this, [this] { updateState(); if (m_companionEngine) m_companionEngine->d->updateState(); }); static int contextCount = 0; m_context = Context(Id("Debugger.Engine.").withSuffix(++contextCount)); ActionManager::registerAction(&m_continueAction, Constants::CONTINUE, m_context); ActionManager::registerAction(&m_exitAction, Constants::STOP, m_context); ActionManager::registerAction(&m_interruptAction, Constants::INTERRUPT, m_context); ActionManager::registerAction(&m_abortAction, Constants::ABORT, m_context); ActionManager::registerAction(&m_stepOverAction, Constants::NEXT, m_context); ActionManager::registerAction(&m_stepIntoAction, Constants::STEP, m_context); ActionManager::registerAction(&m_stepOutAction, Constants::STEPOUT, m_context); ActionManager::registerAction(&m_runToLineAction, Constants::RUNTOLINE, m_context); ActionManager::registerAction(&m_runToSelectedFunctionAction, Constants::RUNTOSELECTEDFUNCTION, m_context); ActionManager::registerAction(&m_jumpToLineAction, Constants::JUMPTOLINE, m_context); ActionManager::registerAction(&m_returnFromFunctionAction, Constants::RETURNFROMFUNCTION, m_context); ActionManager::registerAction(&m_detachAction, Constants::DETACH, m_context); ActionManager::registerAction(&m_resetAction, Constants::RESET, m_context); ActionManager::registerAction(&m_watchAction, Constants::WATCH, m_context); ActionManager::registerAction(&m_operateByInstructionAction, Constants::OPERATE_BY_INSTRUCTION, m_context); ActionManager::registerAction(&m_openMemoryEditorAction, Constants::OPEN_MEMORY_EDITOR, m_context); ActionManager::registerAction(&m_frameUpAction, Constants::FRAME_UP, m_context); ActionManager::registerAction(&m_frameDownAction, Constants::FRAME_DOWN, m_context); } ~DebuggerEnginePrivate() { ActionManager::unregisterAction(&m_continueAction, Constants::CONTINUE); ActionManager::unregisterAction(&m_exitAction, Constants::STOP); ActionManager::unregisterAction(&m_interruptAction, Constants::INTERRUPT); ActionManager::unregisterAction(&m_abortAction, Constants::ABORT); ActionManager::unregisterAction(&m_stepOverAction, Constants::NEXT); ActionManager::unregisterAction(&m_stepIntoAction, Constants::STEP); ActionManager::unregisterAction(&m_stepOutAction, Constants::STEPOUT); ActionManager::unregisterAction(&m_runToLineAction, Constants::RUNTOLINE); ActionManager::unregisterAction(&m_runToSelectedFunctionAction, Constants::RUNTOSELECTEDFUNCTION); ActionManager::unregisterAction(&m_jumpToLineAction, Constants::JUMPTOLINE); ActionManager::unregisterAction(&m_returnFromFunctionAction, Constants::RETURNFROMFUNCTION); ActionManager::unregisterAction(&m_detachAction, Constants::DETACH); ActionManager::unregisterAction(&m_resetAction, Constants::RESET); ActionManager::unregisterAction(&m_watchAction, Constants::WATCH); ActionManager::unregisterAction(&m_operateByInstructionAction, Constants::OPERATE_BY_INSTRUCTION); ActionManager::unregisterAction(&m_openMemoryEditorAction, Constants::OPEN_MEMORY_EDITOR); ActionManager::unregisterAction(&m_frameUpAction, Constants::FRAME_UP); ActionManager::unregisterAction(&m_frameDownAction, Constants::FRAME_DOWN); destroyPerspective(); delete m_logWindow; delete m_breakWindow; delete m_returnWindow; delete m_localsWindow; delete m_watchersWindow; delete m_inspectorWindow; delete m_registerWindow; delete m_peripheralRegisterWindow; 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_peripheralRegisterView; delete m_modulesView; delete m_sourceFilesView; delete m_stackView; delete m_threadsView; } void updateActionToolTips() { // update tooltips that are visible on the button in the mode selector const QString displayName = m_engine->displayName(); m_continueAction.setToolTip(tr("Continue %1").arg(displayName)); m_interruptAction.setToolTip(tr("Interrupt %1").arg(displayName)); } void setupViews(); void destroyPerspective() { if (!m_perspective) return; Perspective *perspective = m_perspective; m_perspective = nullptr; EngineManager::unregisterEngine(m_engine); // This triggers activity in the EngineManager which // recognizes the rampdown by the m_perpective == nullptr above. perspective->destroy(); // disconnect the follow font size connection TextEditorSettings::instance()->disconnect(this); delete perspective; } 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(m_state == EngineShutdownFinished, qDebug() << m_state); resetLocation(); 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. if (boolSetting(SwitchModeOnExit)) EngineManager::deactivateDebugMode(); } void scheduleResetLocation() { m_stackHandler.scheduleResetLocation(); m_watchHandler.scheduleResetLocation(); m_disassemblerAgent.scheduleResetLocation(); m_locationTimer.setSingleShot(true); m_locationTimer.start(80); } void resetLocation() { m_lookupRequests.clear(); m_locationTimer.stop(); m_locationMark.reset(); m_stackHandler.resetLocation(); m_disassemblerAgent.resetLocation(); m_toolTipManager.resetLocation(); } public: void setInitialActionStates(); void setBusyCursor(bool on); void cleanupViews(); void updateState(); void updateReverseActions(); DebuggerEngine *m_engine = nullptr; // Not owned. QString m_runId; QString m_debuggerName; QPointer m_perspective; DebuggerRunParameters m_runParameters; IDevice::ConstPtr m_device; QPointer m_companionEngine; bool m_isPrimaryEngine = true; // The current state. DebuggerState m_state = DebuggerNotReady; // Terminal m_terminal; ProcessHandle m_inferiorPid; BreakHandler m_breakHandler; ModulesHandler m_modulesHandler; RegisterHandler m_registerHandler; PeripheralRegisterHandler m_peripheralRegisterHandler; SourceFilesHandler m_sourceFilesHandler; StackHandler m_stackHandler; ThreadsHandler m_threadsHandler; WatchHandler m_watchHandler; QFutureInterface m_progress; DisassemblerAgent m_disassemblerAgent; MemoryAgentSet m_memoryAgents; QScopedPointer m_locationMark; QTimer m_locationTimer; QString m_qtNamespace; // Safety net to avoid infinite lookups. QSet m_lookupRequests; // FIXME: Integrate properly. QPointer m_alertBox; QPointer m_breakView; QPointer m_returnView; QPointer m_localsView; QPointer m_watchersView; QPointer m_inspectorView; QPointer m_registerView; QPointer m_peripheralRegisterView; QPointer m_modulesView; QPointer m_sourceFilesView; QPointer m_stackView; QPointer m_threadsView; QPointer m_breakWindow; QPointer m_returnWindow; QPointer m_localsWindow; QPointer m_watchersWindow; QPointer m_inspectorWindow; QPointer m_registerWindow; QPointer m_peripheralRegisterWindow; QPointer m_modulesWindow; QPointer m_sourceFilesWindow; QPointer m_stackWindow; QPointer m_threadsWindow; QPointer m_logWindow; QPointer m_localsAndInspectorWindow; QPointer m_threadLabel; bool m_busy = false; bool m_isDying = false; 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")}; QAction m_frameUpAction{QCoreApplication::translate("Debugger::Internal::DebuggerPluginPrivate", "Move to Calling Frame")}; QAction m_frameDownAction{QCoreApplication::translate("Debugger::Internal::DebuggerPluginPrivate", "Move to Called Frame")}; QAction m_openMemoryEditorAction{QCoreApplication::translate("Debugger::Internal::DebuggerPluginPrivate", "Memory...")}; // 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 Allow Reversal of Direction")}; OptionalAction m_operateInReverseDirectionAction{tr("Reverse Direction")}; OptionalAction m_snapshotAction{tr("Take Snapshot of Process State")}; QPointer m_terminalRunner; DebuggerToolTipManager m_toolTipManager; Context m_context; }; void DebuggerEnginePrivate::setupViews() { const DebuggerRunParameters &rp = m_runParameters; const QString engineId = EngineManager::registerEngine(m_engine); QTC_CHECK(!m_perspective); const QString perspectiveId = "Debugger.Perspective." + m_runId + '.' + m_debuggerName; const QString settingsId = "Debugger.Perspective." + m_debuggerName; m_perspective = new Perspective(perspectiveId, m_engine->displayName(), Debugger::Constants::PRESET_PERSPECTIVE_ID, settingsId); 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(false); m_operateByInstructionAction.setToolTip("

" + tr("Switches the debugger to instruction-wise " "operation mode. In this mode, stepping operates on single " "instructions and the source location view also shows the " "disassembled instructions.")); m_operateByInstructionAction.setIconVisibleInMenu(false); connect(&m_operateByInstructionAction, &QAction::triggered, m_engine, &DebuggerEngine::operateByInstructionTriggered); m_frameDownAction.setEnabled(true); connect(&m_frameDownAction, &QAction::triggered, m_engine, &DebuggerEngine::handleFrameDown); m_frameUpAction.setEnabled(true); connect(&m_frameUpAction, &QAction::triggered, m_engine, &DebuggerEngine::handleFrameUp); m_openMemoryEditorAction.setEnabled(true); m_openMemoryEditorAction.setVisible(m_engine->hasCapability(ShowMemoryCapability)); connect(&m_openMemoryEditorAction, &QAction::triggered, m_engine, &DebuggerEngine::openMemoryEditor); 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(); 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("Debugger.Dock.Modules." + engineId); 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("Debugger.Dock.Register." + engineId); m_registerWindow->setWindowTitle(tr("Reg&isters")); m_peripheralRegisterView = new BaseTreeView; m_peripheralRegisterView->setModel(m_peripheralRegisterHandler.model()); m_peripheralRegisterView->setRootIsDecorated(true); m_peripheralRegisterView->setSettings(settings, "Debugger.PeripheralRegisterView"); connect(m_peripheralRegisterView, &BaseTreeView::aboutToShow, m_engine, &DebuggerEngine::reloadPeripheralRegisters, Qt::QueuedConnection); m_peripheralRegisterWindow = addSearch(m_peripheralRegisterView); m_peripheralRegisterWindow->setObjectName("Debugger.Dock.PeripheralRegister." + engineId); m_peripheralRegisterWindow->setWindowTitle(tr("Peripheral Reg&isters")); m_stackView = new StackTreeView; 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("Debugger.Dock.Stack." + engineId); 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("Debugger.Dock.SourceFiles." + engineId); 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_threadsView->setSpanColumn(ThreadData::FunctionColumn); m_threadsWindow = addSearch(m_threadsView); m_threadsWindow->setObjectName("Debugger.Dock.Threads." + engineId); 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_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("Debugger.Dock.Locals." + engineId); 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("Debugger.Dock.Inspector." + engineId); 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("Debugger.Dock.Watchers." + engineId); m_watchersWindow->setWindowTitle(tr("&Expressions")); m_localsAndInspectorWindow = new LocalsAndInspectorWindow( m_localsWindow, m_inspectorWindow, m_returnWindow); m_localsAndInspectorWindow->setObjectName("Debugger.Dock.LocalsAndInspector." + engineId); 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); m_breakView->setSpanColumn(BreakpointFunctionColumn); m_breakView->setSettings(settings, "Debugger.BreakWindow"); m_breakView->setModel(m_breakHandler.model()); m_breakView->setRootIsDecorated(true); m_breakWindow = addSearch(m_breakView); m_breakWindow->setObjectName("Debugger.Dock.Break." + engineId); m_breakWindow->setWindowTitle(tr("&Breakpoints")); m_perspective->useSubPerspectiveSwitcher(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); connect(&m_detachAction, &QAction::triggered, m_engine, &DebuggerEngine::handleExecDetach); 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_TOOLBAR.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("Restarts 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::handleExecStepOver); m_stepIntoAction.setIcon(Icons::STEP_INTO_TOOLBAR.icon()); connect(&m_stepIntoAction, &QAction::triggered, m_engine, &DebuggerEngine::handleExecStepIn); 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); connect(&m_watchAction, &QAction::triggered, m_engine, &DebuggerEngine::handleAddToWatchWindow); 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_perspective->addToolBarWidget(m_threadsHandler.threadSwitcher()); 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_peripheralRegisterWindow->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->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::SplitVertical, m_localsAndInspectorWindow, true, Qt::RightDockWidgetArea); m_perspective->addWindow(m_registerWindow, Perspective::AddToTab, m_localsAndInspectorWindow, false, Qt::RightDockWidgetArea); m_perspective->addWindow(m_peripheralRegisterWindow, Perspective::AddToTab, m_localsAndInspectorWindow, false, Qt::RightDockWidgetArea); m_perspective->addWindow(m_logWindow, Perspective::AddToTab, nullptr, false, Qt::TopDockWidgetArea); m_perspective->select(); m_watchHandler.loadSessionDataForEngine(); } ////////////////////////////////////////////////////////////////////// // // DebuggerEngine // ////////////////////////////////////////////////////////////////////// DebuggerEngine::DebuggerEngine() : d(new DebuggerEnginePrivate(this)) { } DebuggerEngine::~DebuggerEngine() { // EngineManager::unregisterEngine(this); delete d; } void DebuggerEngine::setDebuggerName(const QString &name) { d->m_debuggerName = name; d->updateActionToolTips(); } QString DebuggerEngine::debuggerName() const { return d->m_debuggerName; } QString DebuggerEngine::stateName(int s) { # define SN(x) case x: return QLatin1String(#x); switch (s) { SN(DebuggerNotReady) SN(EngineSetupRequested) SN(EngineSetupOk) SN(EngineSetupFailed) SN(EngineRunFailed) SN(EngineRunRequested) SN(InferiorRunRequested) SN(InferiorRunOk) SN(InferiorRunFailed) SN(InferiorUnrunnable) SN(InferiorStopRequested) SN(InferiorStopOk) SN(InferiorStopFailed) SN(InferiorShutdownRequested) SN(InferiorShutdownFinished) SN(EngineShutdownRequested) SN(EngineShutdownFinished) SN(DebuggerFinished) } return QLatin1String(""); # undef SN } 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::isPeripheralRegistersWindowVisible() const { return d->m_peripheralRegisterWindow->isVisible(); } bool DebuggerEngine::isModulesWindowVisible() const { return d->m_modulesWindow->isVisible(); } void DebuggerEngine::frameUp() { int currentIndex = stackHandler()->currentIndex(); activateFrame(qMin(currentIndex + 1, stackHandler()->stackSize() - 1)); } void DebuggerEngine::frameDown() { int currentIndex = stackHandler()->currentIndex(); activateFrame(qMax(currentIndex - 1, 0)); } void DebuggerEngine::doUpdateLocals(const UpdateParameters &) { } ModulesHandler *DebuggerEngine::modulesHandler() const { return &d->m_modulesHandler; } RegisterHandler *DebuggerEngine::registerHandler() const { return &d->m_registerHandler; } PeripheralRegisterHandler *DebuggerEngine::peripheralRegisterHandler() const { return &d->m_peripheralRegisterHandler; } StackHandler *DebuggerEngine::stackHandler() const { return &d->m_stackHandler; } ThreadsHandler *DebuggerEngine::threadsHandler() const { return &d->m_threadsHandler; } WatchHandler *DebuggerEngine::watchHandler() const { return &d->m_watchHandler; } SourceFilesHandler *DebuggerEngine::sourceFilesHandler() const { return &d->m_sourceFilesHandler; } BreakHandler *DebuggerEngine::breakHandler() const { return &d->m_breakHandler; } LogWindow *DebuggerEngine::logWindow() const { return d->m_logWindow; } DisassemblerAgent *DebuggerEngine::disassemblerAgent() const { return &d->m_disassemblerAgent; } void DebuggerEngine::fetchMemory(MemoryAgent *, quint64 addr, quint64 length) { Q_UNUSED(addr) Q_UNUSED(length) } void DebuggerEngine::changeMemory(MemoryAgent *, quint64 addr, const QByteArray &data) { Q_UNUSED(addr) Q_UNUSED(data) } void DebuggerEngine::setRegisterValue(const QString &name, const QString &value) { Q_UNUSED(name) Q_UNUSED(value) } void DebuggerEngine::setPeripheralRegisterValue(quint64 address, quint64 value) { Q_UNUSED(address) Q_UNUSED(value) } void DebuggerEngine::setRunParameters(const DebuggerRunParameters &runParameters) { d->m_runParameters = runParameters; d->updateActionToolTips(); } void DebuggerEngine::setRunId(const QString &id) { d->m_runId = id; } void DebuggerEngine::setRunTool(DebuggerRunTool *runTool) { RunControl *runControl = runTool->runControl(); d->m_device = runControl->device(); if (!d->m_device) d->m_device = d->m_runParameters.inferior.device; d->m_terminalRunner = runTool->terminalRunner(); validateRunParameters(d->m_runParameters); d->setupViews(); } void DebuggerEngine::start() { d->m_watchHandler.resetWatchers(); d->setInitialActionStates(); setState(EngineSetupRequested); showMessage("CALL: SETUP ENGINE"); setupEngine(); } void DebuggerEngine::resetLocation() { // Do it after some delay to avoid flicker. d->scheduleResetLocation(); } void DebuggerEngine::gotoLocation(const Location &loc) { d->resetLocation(); if (loc.canBeDisassembled() && ((hasCapability(OperateByInstructionCapability) && operatesByInstruction()) || !loc.hasDebugInfo()) ) { d->m_disassemblerAgent.setLocation(loc); return; } if (loc.fileName().isEmpty()) { showMessage("CANNOT GO TO THIS LOCATION"); return; } const QString file = loc.fileName().toString(); const int line = loc.lineNumber(); bool newEditor = false; IEditor *editor = EditorManager::openEditor(file, Id(), EditorManager::IgnoreNavigationHistory | EditorManager::DoNotSwitchToDesignMode | EditorManager::SwitchSplitIfAlreadyVisible, &newEditor); QTC_ASSERT(editor, return); // Unreadable file? editor->gotoLine(line, 0, !boolSetting(StationaryEditorWhileStepping)); if (newEditor) editor->document()->setProperty(Constants::OPENED_BY_DEBUGGER, true); if (loc.needsMarker()) { d->m_locationMark.reset(new LocationMark(this, loc.fileName(), line)); d->m_locationMark->setToolTip(tr("Current debugger location of %1").arg(displayName())); } } void DebuggerEngine::gotoCurrentLocation() { if (d->m_state == InferiorStopOk || d->m_state == InferiorUnrunnable) { int top = stackHandler()->currentIndex(); if (top >= 0) gotoLocation(stackHandler()->currentFrame()); } } const DebuggerRunParameters &DebuggerEngine::runParameters() 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 { 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(); } } void DebuggerEngine::updateUi(bool isCurrentEngine) { updateState(); if (isCurrentEngine) { gotoCurrentLocation(); } else { d->m_locationMark.reset(); d->m_disassemblerAgent.resetLocation(); } } static bool isAllowedTransition(DebuggerState from, DebuggerState to) { switch (from) { case DebuggerNotReady: return to == EngineSetupRequested; case EngineSetupRequested: return to == EngineSetupOk || to == EngineSetupFailed; case EngineSetupFailed: // In is the engine's task to go into a proper "Shutdown" // state before calling notifyEngineSetupFailed return to == DebuggerFinished; case EngineSetupOk: return to == EngineRunRequested || to == EngineShutdownRequested; case EngineRunRequested: return to == EngineRunFailed || to == InferiorRunRequested || to == InferiorRunOk || to == InferiorStopOk || to == InferiorUnrunnable; case EngineRunFailed: return to == EngineShutdownRequested; case InferiorRunRequested: return to == InferiorRunOk || to == InferiorRunFailed; case InferiorRunFailed: return to == InferiorStopOk; case InferiorRunOk: return to == InferiorStopRequested || to == InferiorStopOk // A spontaneous stop. || to == InferiorShutdownFinished; // A spontaneous exit. case InferiorStopRequested: return to == InferiorStopOk || to == InferiorStopFailed; case InferiorStopOk: return to == InferiorRunRequested || to == InferiorShutdownRequested || to == InferiorStopOk || to == InferiorShutdownFinished; case InferiorStopFailed: return to == EngineShutdownRequested; case InferiorUnrunnable: return to == InferiorShutdownRequested; case InferiorShutdownRequested: return to == InferiorShutdownFinished; case InferiorShutdownFinished: return to == EngineShutdownRequested; case EngineShutdownRequested: return to == EngineShutdownFinished; case EngineShutdownFinished: return to == DebuggerFinished; case DebuggerFinished: return to == EngineSetupRequested; // Happens on restart. } qDebug() << "UNKNOWN DEBUGGER STATE:" << from; return false; } void DebuggerEngine::notifyEngineSetupFailed() { showMessage("NOTE: ENGINE SETUP FAILED"); QTC_ASSERT(state() == EngineSetupRequested, qDebug() << this << state()); setState(EngineSetupFailed); if (d->m_isPrimaryEngine) { showMessage(tr("Debugging has failed."), NormalMessageFormat); d->m_progress.setProgressValue(900); d->m_progress.reportCanceled(); d->m_progress.reportFinished(); } setState(DebuggerFinished); } void DebuggerEngine::notifyEngineSetupOk() { //#ifdef WITH_BENCHMARK // CALLGRIND_START_INSTRUMENTATION; //#endif showMessage("NOTE: ENGINE SETUP OK"); d->m_progress.setProgressValue(250); QTC_ASSERT(state() == EngineSetupRequested, qDebug() << this << state()); setState(EngineSetupOk); setState(EngineRunRequested); showMessage("CALL: RUN ENGINE"); d->m_progress.setProgressValue(300); runEngine(); } void DebuggerEngine::notifyEngineRunOkAndInferiorUnrunnable() { showMessage("NOTE: INFERIOR UNRUNNABLE"); d->m_progress.setProgressValue(1000); d->m_progress.reportFinished(); QTC_ASSERT(state() == EngineRunRequested, qDebug() << this << state()); showStatusMessage(tr("Loading finished.")); setState(InferiorUnrunnable); } void DebuggerEngine::notifyEngineRunFailed() { showMessage("NOTE: ENGINE RUN FAILED"); QTC_ASSERT(state() == EngineRunRequested, qDebug() << this << state()); d->m_progress.setProgressValue(900); d->m_progress.reportCanceled(); d->m_progress.reportFinished(); showStatusMessage(tr("Run failed.")); setState(EngineRunFailed); d->doShutdownEngine(); } void DebuggerEngine::notifyEngineRunAndInferiorRunOk() { showMessage("NOTE: ENGINE RUN AND INFERIOR RUN OK"); d->m_progress.setProgressValue(1000); d->m_progress.reportFinished(); QTC_ASSERT(state() == EngineRunRequested, qDebug() << this << state()); showStatusMessage(tr("Running.")); setState(InferiorRunOk); } void DebuggerEngine::notifyEngineRunAndInferiorStopOk() { showMessage("NOTE: ENGINE RUN AND INFERIOR STOP OK"); d->m_progress.setProgressValue(1000); d->m_progress.reportFinished(); QTC_ASSERT(state() == EngineRunRequested, qDebug() << this << state()); showStatusMessage(tr("Stopped.")); setState(InferiorStopOk); } void DebuggerEngine::notifyInferiorRunRequested() { showMessage("NOTE: INFERIOR RUN REQUESTED"); QTC_ASSERT(state() == InferiorStopOk, qDebug() << this << state()); showStatusMessage(tr("Run requested...")); setState(InferiorRunRequested); } void DebuggerEngine::notifyInferiorRunOk() { if (state() == InferiorRunOk) { showMessage("NOTE: INFERIOR RUN OK - REPEATED."); return; } showMessage("NOTE: INFERIOR RUN OK"); showStatusMessage(tr("Running.")); // Transition from StopRequested can happen in remotegdbadapter. QTC_ASSERT(state() == InferiorRunRequested || state() == InferiorStopOk || state() == InferiorStopRequested, qDebug() << this << state()); setState(InferiorRunOk); } void DebuggerEngine::notifyInferiorRunFailed() { showMessage("NOTE: INFERIOR RUN FAILED"); QTC_ASSERT(state() == InferiorRunRequested, qDebug() << this << state()); setState(InferiorRunFailed); setState(InferiorStopOk); if (isDying()) d->doShutdownInferior(); } void DebuggerEngine::notifyInferiorStopOk() { showMessage("NOTE: INFERIOR STOP OK"); // Ignore spurious notifications after we are set to die. if (isDying()) { showMessage("NOTE: ... WHILE DYING. "); // Forward state to "StopOk" if needed. if (state() == InferiorStopRequested || state() == InferiorRunRequested || state() == InferiorRunOk) { showMessage("NOTE: ... FORWARDING TO 'STOP OK'. "); setState(InferiorStopOk); } if (state() == InferiorStopOk || state() == InferiorStopFailed) d->doShutdownInferior(); showMessage("NOTE: ... IGNORING STOP MESSAGE"); return; } QTC_ASSERT(state() == InferiorStopRequested, qDebug() << this << state()); showMessage(tr("Stopped."), StatusBar); setState(InferiorStopOk); } void DebuggerEngine::notifyInferiorSpontaneousStop() { showMessage("NOTE: INFERIOR SPONTANEOUS STOP"); QTC_ASSERT(state() == InferiorRunOk, qDebug() << this << state()); d->m_perspective->select(); showMessage(tr("Stopped."), StatusBar); setState(InferiorStopOk); if (boolSetting(RaiseOnInterrupt)) ICore::raiseWindow(DebuggerMainWindow::instance()); } void DebuggerEngine::notifyInferiorStopFailed() { showMessage("NOTE: INFERIOR STOP FAILED"); QTC_ASSERT(state() == InferiorStopRequested, qDebug() << this << state()); setState(InferiorStopFailed); d->doShutdownEngine(); } void DebuggerEnginePrivate::setInitialActionStates() { m_returnWindow->setVisible(false); setBusyCursor(false); m_recordForReverseOperationAction.setCheckable(true); m_recordForReverseOperationAction.setChecked(false); m_recordForReverseOperationAction.setIcon(Icons::RECORD_OFF.icon()); m_recordForReverseOperationAction.setToolTip(QString("

%1

" "%2%3

").arg( tr("Record information to enable stepping backwards."), tr("Note: "), tr("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."))); m_operateInReverseDirectionAction.setCheckable(true); m_operateInReverseDirectionAction.setChecked(false); m_operateInReverseDirectionAction.setIcon(Icons::DIRECTION_FORWARD.icon()); m_snapshotAction.setIcon(Utils::Icons::SNAPSHOT_TOOLBAR.icon()); 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_runToLineAction.setVisible(false); m_runToSelectedFunctionAction.setEnabled(true); m_returnFromFunctionAction.setEnabled(false); m_jumpToLineAction.setEnabled(false); m_jumpToLineAction.setVisible(false); m_stepOverAction.setEnabled(true); action(AutoDerefPointers)->setEnabled(true); action(ExpandStack)->setEnabled(false); m_threadLabel->setEnabled(false); } void DebuggerEnginePrivate::updateState() { // Can happen in mixed debugging. if (!m_threadLabel) return; QTC_ASSERT(m_threadLabel, return); 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); } 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_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_localsAndInspectorWindow->setShowLocals(false); } else if (state == DebuggerFinished) { // 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); 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); // 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); } const bool threadsEnabled = state == InferiorStopOk || state == InferiorUnrunnable; m_threadsHandler.threadSwitcher()->setEnabled(threadsEnabled); m_threadLabel->setEnabled(threadsEnabled); 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(ICore::dialogParent(), 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); const bool canRunToLine = m_engine->hasCapability(RunToLineCapability); m_runToLineAction.setVisible(canRunToLine); m_runToLineAction.setEnabled(stopped && canRunToLine); m_runToSelectedFunctionAction.setEnabled(stopped); const bool canReturnFromFunction = m_engine->hasCapability(ReturnFromFunctionCapability); m_returnFromFunctionAction.setVisible(canReturnFromFunction); m_returnFromFunctionAction.setEnabled(stopped && canReturnFromFunction); const bool canJump = m_engine->hasCapability(JumpToLineCapability); m_jumpToLineAction.setVisible(canJump); m_jumpToLineAction.setEnabled(stopped && 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); } 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 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_peripheralRegisterWindow->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() { showMessage("INFERIOR FINISHED SHUT DOWN"); QTC_ASSERT(state() == InferiorShutdownRequested, qDebug() << this << state()); setState(InferiorShutdownFinished); d->doShutdownEngine(); } 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. startDying(); if (state() == InferiorRunRequested) { // We asked for running, but did not see a response. // Assume the inferior is dead. // FIXME: Use timeout? setState(InferiorRunFailed); setState(InferiorStopOk); } d->doShutdownInferior(); } void DebuggerEngine::notifyEngineShutdownFinished() { showMessage("NOTE: ENGINE SHUTDOWN FINISHED"); QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << this << state()); setState(EngineShutdownFinished); d->doFinishDebugger(); } void DebuggerEngine::notifyEngineIll() { //#ifdef WITH_BENCHMARK // CALLGRIND_STOP_INSTRUMENTATION; // CALLGRIND_DUMP_STATS; //#endif showMessage("NOTE: ENGINE ILL ******"); startDying(); switch (state()) { case InferiorRunRequested: case InferiorRunOk: // The engine does not look overly ill right now, so attempt to // properly interrupt at least once. If that fails, we are on the // shutdown path due to d->m_targetState anyways. setState(InferiorStopRequested, true); showMessage("ATTEMPT TO INTERRUPT INFERIOR"); interruptInferior(); break; case InferiorStopRequested: notifyInferiorStopFailed(); break; case InferiorStopOk: showMessage("FORWARDING STATE TO InferiorShutdownFinished"); setState(InferiorShutdownFinished, true); d->doShutdownEngine(); break; default: d->doShutdownEngine(); break; } } void DebuggerEngine::notifyEngineSpontaneousShutdown() { #ifdef WITH_BENCHMARK CALLGRIND_STOP_INSTRUMENTATION; CALLGRIND_DUMP_STATS; #endif showMessage("NOTE: ENGINE SPONTANEOUS SHUTDOWN"); setState(EngineShutdownFinished, true); d->doFinishDebugger(); } void DebuggerEngine::notifyInferiorExited() { #ifdef WITH_BENCHMARK CALLGRIND_STOP_INSTRUMENTATION; CALLGRIND_DUMP_STATS; #endif showMessage("NOTE: INFERIOR EXITED"); d->resetLocation(); setState(InferiorShutdownFinished); d->doShutdownEngine(); } void DebuggerEngine::updateState() { d->updateState(); } 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); DebuggerMainWindow::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, "ERROR: " + msg); d->m_logWindow->showOutput(LogError, "ERROR: " + msg); break; case AppOutput: case AppStuff: d->m_logWindow->showOutput(channel, msg); emit appendMessageRequested(msg, StdOutFormat, false); break; case AppError: d->m_logWindow->showOutput(channel, msg); emit appendMessageRequested(msg, StdErrFormat, false); break; default: d->m_logWindow->showOutput(channel, msg); break; } } void DebuggerEngine::notifyDebuggerProcessFinished(int exitCode, QProcess::ExitStatus exitStatus, const QString &backendName) { showMessage(QString("%1 PROCESS FINISHED, status %2, exit code %3") .arg(backendName).arg(exitStatus).arg(exitCode)); switch (state()) { case DebuggerFinished: // Nothing to do. break; case EngineShutdownRequested: case InferiorShutdownRequested: notifyEngineShutdownFinished(); break; case InferiorRunOk: // This could either be a real gdb/lldb crash or a quickly exited inferior // in the terminal adapter. In this case the stub proc will die soon, // too, so there's no need to act here. showMessage(QString("The %1 process exited somewhat unexpectedly.").arg(backendName)); notifyEngineSpontaneousShutdown(); break; default: { // Initiate shutdown sequence notifyInferiorIll(); const QString msg = exitStatus == QProcess::CrashExit ? tr("The %1 process terminated.") : tr("The %2 process terminated unexpectedly (exit code %1).").arg(exitCode); AsynchronousMessageBox::critical(tr("Unexpected %1 Exit").arg(backendName), msg.arg(backendName)); break; } } } static QString msgStateChanged(DebuggerState oldState, DebuggerState newState, bool forced) { QString result; QTextStream str(&result); str << "State changed"; if (forced) str << " BY FORCE"; str << " from " << DebuggerEngine::stateName(oldState) << '(' << oldState << ") to " << DebuggerEngine::stateName(newState) << '(' << newState << ')'; return result; } void DebuggerEngine::setState(DebuggerState state, bool forced) { const QString msg = msgStateChanged(d->m_state, state, forced); DebuggerState oldState = d->m_state; d->m_state = state; if (!forced && !isAllowedTransition(oldState, state)) qDebug() << "*** UNEXPECTED STATE TRANSITION: " << this << msg; if (state == EngineRunRequested) { emit engineStarted(); if (d->m_perspective) d->m_perspective->select(); } showMessage(msg, LogDebug); d->updateState(); if (d->m_companionEngine) d->m_companionEngine->d->updateState(); if (oldState != d->m_state) emit EngineManager::instance()->engineStateChanged(this); if (state == DebuggerFinished) { d->setBusyCursor(false); // Give up ownership on claimed breakpoints. d->m_breakHandler.releaseAllBreakpoints(); d->m_toolTipManager.deregisterEngine(); d->m_memoryAgents.handleDebuggerFinished(); d->destroyPerspective(); emit engineFinished(); } } bool DebuggerEngine::isPrimaryEngine() const { return d->m_isPrimaryEngine; } bool DebuggerEngine::canDisplayTooltip() const { return state() == InferiorStopOk; } QString DebuggerEngine::expand(const QString &string) const { return runParameters().macroExpander->expand(string); } QString DebuggerEngine::nativeStartupCommands() const { return expand(QStringList({stringSetting(GdbStartupCommands), runParameters().additionalStartupCommands}).join('\n')); } Perspective *DebuggerEngine::perspective() const { return d->m_perspective; } void DebuggerEngine::updateMarkers() { if (d->m_locationMark) d->m_locationMark->updateIcon(); d->m_disassemblerAgent.updateLocationMarker(); } void DebuggerEngine::updateToolTips() { d->m_toolTipManager.updateToolTips(); } DebuggerToolTipManager *DebuggerEngine::toolTipManager() { return &d->m_toolTipManager; } bool DebuggerEngine::operatesByInstruction() const { return d->m_operateByInstructionAction.isChecked(); } bool DebuggerEngine::debuggerActionsEnabled() const { return debuggerActionsEnabledHelper(d->m_state); } void DebuggerEngine::operateByInstructionTriggered(bool on) { // Go to source only if we have the file. // if (DebuggerEngine *cppEngine = m_engine->cppEngine()) { d->m_stackHandler.rootItem()->updateAll(); if (d->m_stackHandler.currentIndex() >= 0) { const StackFrame frame = d->m_stackHandler.currentFrame(); if (on || frame.isUsable()) gotoLocation(Location(frame, true)); } // } } bool DebuggerEngine::companionPreventsActions() const { return false; } void DebuggerEngine::notifyInferiorPid(const ProcessHandle &pid) { if (d->m_inferiorPid == pid) return; d->m_inferiorPid = pid; if (pid.isValid()) { showMessage(tr("Taking notice of pid %1").arg(pid.pid())); DebuggerStartMode sm = runParameters().startMode; if (sm == StartInternal || sm == StartExternal || sm == AttachExternal) d->m_inferiorPid.activate(); } } qint64 DebuggerEngine::inferiorPid() const { return d->m_inferiorPid.pid(); } bool DebuggerEngine::isReverseDebugging() const { return d->m_operateInReverseDirectionAction.isChecked(); } void DebuggerEngine::handleBeginOfRecordingReached() { 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())); startDying(); switch (state()) { case InferiorStopOk: case InferiorStopFailed: case InferiorUnrunnable: d->doShutdownInferior(); break; case InferiorRunOk: setState(InferiorStopRequested); showMessage(tr("Attempting to interrupt."), StatusBar); interruptInferior(); break; case EngineSetupRequested: notifyEngineSetupFailed(); break; case EngineSetupOk: notifyEngineSetupFailed(); break; case EngineRunRequested: notifyEngineRunFailed(); break; case EngineShutdownRequested: case InferiorShutdownRequested: break; case EngineRunFailed: case DebuggerFinished: case InferiorShutdownFinished: break; default: // FIXME: We should disable the actions connected to that. notifyInferiorIll(); break; } } void DebuggerEngine::requestInterruptInferior() { QTC_ASSERT(state() == InferiorRunOk, qDebug() << this << state()); setState(InferiorStopRequested); showMessage("CALL: INTERRUPT INFERIOR"); showMessage(tr("Attempting to interrupt."), StatusBar); interruptInferior(); } void DebuggerEngine::progressPing() { int progress = qMin(d->m_progress.progressValue() + 2, 800); d->m_progress.setProgressValue(progress); } void DebuggerEngine::setCompanionEngine(DebuggerEngine *engine) { d->m_companionEngine = engine; } void DebuggerEngine::setSecondaryEngine() { d->m_isPrimaryEngine = false; } TerminalRunner *DebuggerEngine::terminal() const { return d->m_terminalRunner; } void DebuggerEngine::selectWatchData(const QString &) { } void DebuggerEngine::watchPoint(const QPoint &pnt) { DebuggerCommand cmd("watchPoint", NeedsFullStop); cmd.arg("x", pnt.x()); cmd.arg("y", pnt.y()); cmd.callback = [this](const DebuggerResponse &response) { qulonglong addr = response.data["selected"].toAddress(); if (addr == 0) 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); }; runCommand(cmd); } void DebuggerEngine::runCommand(const DebuggerCommand &) { // Overridden in the engines that use the interface. QTC_CHECK(false); } void DebuggerEngine::fetchDisassembler(DisassemblerAgent *) { } void DebuggerEngine::activateFrame(int) { } void DebuggerEngine::reloadModules() { } void DebuggerEngine::examineModules() { } void DebuggerEngine::loadSymbols(const QString &) { } void DebuggerEngine::loadAllSymbols() { } void DebuggerEngine::loadSymbolsForStack() { } void DebuggerEngine::requestModuleSymbols(const QString &) { } void DebuggerEngine::requestModuleSections(const QString &) { } void DebuggerEngine::reloadRegisters() { } void DebuggerEngine::reloadPeripheralRegisters() { } void DebuggerEngine::reloadSourceFiles() { } void DebuggerEngine::reloadFullStack() { } void DebuggerEngine::loadAdditionalQmlStack() { } void DebuggerEngine::reloadDebuggingHelpers() { } void DebuggerEngine::addOptionPages(QList *) const { } QString DebuggerEngine::qtNamespace() const { return d->m_qtNamespace; } void DebuggerEngine::setQtNamespace(const QString &ns) { d->m_qtNamespace = ns; } void DebuggerEngine::createSnapshot() { } void DebuggerEngine::updateLocals() { // if the engine is not running - do nothing if (state() == DebuggerState::DebuggerFinished || state() == DebuggerState::DebuggerNotReady) return; watchHandler()->resetValueCache(); doUpdateLocals(UpdateParameters()); } Context DebuggerEngine::debuggerContext() const { return d->m_context; } void DebuggerEngine::updateAll() { } QString DebuggerEngine::displayName() const { //: e.g. LLDB for "myproject", shows up i return tr("%1 for \"%2\"").arg(d->m_debuggerName, runParameters().displayName); } void DebuggerEngine::insertBreakpoint(const Breakpoint &bp) { QTC_ASSERT(bp, return); BreakpointState state = bp->state(); QTC_ASSERT(state == BreakpointInsertionRequested, qDebug() << bp->modelId() << this << state); QTC_CHECK(false); } void DebuggerEngine::removeBreakpoint(const Breakpoint &bp) { QTC_ASSERT(bp, return); BreakpointState state = bp->state(); QTC_ASSERT(state == BreakpointRemoveRequested, qDebug() << bp->responseId() << this << state); QTC_CHECK(false); } void DebuggerEngine::updateBreakpoint(const Breakpoint &bp) { QTC_ASSERT(bp, return); BreakpointState state = bp->state(); QTC_ASSERT(state == BreakpointUpdateRequested, qDebug() << bp->responseId() << this << state); QTC_CHECK(false); } void DebuggerEngine::enableSubBreakpoint(const SubBreakpoint &sbp, bool) { QTC_ASSERT(sbp, return); QTC_CHECK(false); } void DebuggerEngine::assignValueInDebugger(WatchItem *, const QString &, const QVariant &) { } void DebuggerEngine::handleRecordReverse(bool record) { executeRecordReverse(record); d->updateReverseActions(); } void DebuggerEngine::handleReverseDirection(bool reverse) { executeReverse(reverse); updateMarkers(); d->updateReverseActions(); } void DebuggerEngine::executeDebuggerCommand(const QString &) { showMessage(tr("This debugger cannot handle user input."), StatusBar); } bool DebuggerEngine::isDying() const { return d->m_isDying; } QString DebuggerEngine::msgStopped(const QString &reason) { return reason.isEmpty() ? tr("Stopped.") : tr("Stopped: \"%1\".").arg(reason); } QString DebuggerEngine::msgStoppedBySignal(const QString &meaning, const QString &name) { return tr("Stopped: %1 (Signal %2).").arg(meaning, name); } QString DebuggerEngine::msgStoppedByException(const QString &description, const QString &threadId) { return tr("Stopped in thread %1 by: %2.").arg(threadId, description); } QString DebuggerEngine::msgInterrupted() { return tr("Interrupted."); } bool DebuggerEngine::showStoppedBySignalMessageBox(QString meaning, QString name) { if (d->m_alertBox) return false; if (name.isEmpty()) name = ' ' + tr("", "name") + ' '; if (meaning.isEmpty()) meaning = ' ' + tr("", "meaning") + ' '; const QString msg = tr("

The inferior stopped because it received a " "signal from the operating system.

" "" "
Signal name : %1
Signal meaning : %2
") .arg(name, meaning); d->m_alertBox = AsynchronousMessageBox::information(tr("Signal Received"), msg); return true; } void DebuggerEngine::showStoppedByExceptionMessageBox(const QString &description) { const QString msg = tr("

The inferior stopped because it triggered an exception.

%1"). arg(description); AsynchronousMessageBox::information(tr("Exception Triggered"), msg); } void DebuggerEngine::openMemoryView(const MemoryViewSetupData &data) { d->m_memoryAgents.createBinEditor(data, this); } void DebuggerEngine::updateMemoryViews() { d->m_memoryAgents.updateContents(); } void DebuggerEngine::openDisassemblerView(const Location &location) { DisassemblerAgent *agent = new DisassemblerAgent(this); agent->setLocation(location); } void DebuggerEngine::raiseWatchersWindow() { if (d->m_watchersView && d->m_watchersWindow) { auto currentPerspective = DebuggerMainWindow::currentPerspective(); QTC_ASSERT(currentPerspective, return); // if a companion engine has taken over - do not raise the watchers if (currentPerspective->name() != d->m_engine->displayName()) return; if (auto dock = qobject_cast(d->m_watchersWindow->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("") .arg(++count).arg(LogWindow::logTimeStamp()), LogMiscInput); showMessage(tr("Finished retrieving data."), 400, StatusBar); d->m_toolTipManager.updateToolTips(); 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 && !item->wantsChildren) { updateToolTips(); return; } 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::handleExecStepIn() { resetLocation(); executeStepIn(operatesByInstruction()); } void DebuggerEngine::handleExecStepOver() { resetLocation(); executeStepOver(operatesByInstruction()); } 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('(')) { 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()->rowCount() == 0) return false; StackFrame frame = stackHandler()->frameAt(0); return frame.language == QmlLanguage; } void DebuggerEngine::startDying() const { d->m_isDying = true; } QString DebuggerEngine::runId() const { return d->m_runId; } bool DebuggerRunParameters::isCppDebugging() const { return cppEngineType == GdbEngineType || cppEngineType == LldbEngineType || cppEngineType == CdbEngineType || cppEngineType == UvscEngineType; } 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: " << sp.inferior.commandLine().toUserOutput(); if (d->m_terminalRunner) str << " [terminal]"; str << '\n'; if (!sp.inferior.workingDirectory.isEmpty()) str << "Directory: " << QDir::toNativeSeparators(sp.inferior.workingDirectory) << '\n'; } if (!sp.debugger.executable.isEmpty()) str << "Debugger: " << sp.debugger.executable.toUserOutput() << '\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: " << sp.projectSourceDirectory.toUserOutput() << '\n'; str << "Additional Search Directories:"; for (const FilePath &dir : sp.additionalSearchDirectories) str << ' ' << dir; str << '\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(':') << '\n'; return rc; } static void createNewDock(QWidget *widget) { auto dockWidget = new QDockWidget; dockWidget->setWidget(widget); dockWidget->setWindowTitle(widget->windowTitle()); dockWidget->setFeatures(QDockWidget::DockWidgetClosable); dockWidget->show(); } void DebuggerEngine::showModuleSymbols(const QString &moduleName, const Symbols &symbols) { auto w = new QTreeWidget; w->setUniformRowHeights(true); w->setColumnCount(5); w->setRootIsDecorated(false); w->setAlternatingRowColors(true); w->setSortingEnabled(true); w->setObjectName("Symbols." + moduleName); QStringList header; header.append(tr("Symbol")); header.append(tr("Address")); header.append(tr("Code")); header.append(tr("Section")); header.append(tr("Name")); w->setHeaderLabels(header); w->setWindowTitle(tr("Symbols in \"%1\"").arg(moduleName)); for (const Symbol &s : symbols) { auto it = new QTreeWidgetItem; it->setData(0, Qt::DisplayRole, s.name); it->setData(1, Qt::DisplayRole, s.address); it->setData(2, Qt::DisplayRole, s.state); it->setData(3, Qt::DisplayRole, s.section); it->setData(4, Qt::DisplayRole, s.demangled); w->addTopLevelItem(it); } createNewDock(w); } void DebuggerEngine::showModuleSections(const QString &moduleName, const Sections §ions) { auto w = new QTreeWidget; w->setUniformRowHeights(true); w->setColumnCount(5); w->setRootIsDecorated(false); w->setAlternatingRowColors(true); w->setSortingEnabled(true); w->setObjectName("Sections." + moduleName); QStringList header; header.append(tr("Name")); header.append(tr("From")); header.append(tr("To")); header.append(tr("Address")); header.append(tr("Flags")); w->setHeaderLabels(header); w->setWindowTitle(tr("Sections in \"%1\"").arg(moduleName)); for (const Section &s : sections) { auto it = new QTreeWidgetItem; it->setData(0, Qt::DisplayRole, s.name); it->setData(1, Qt::DisplayRole, s.from); it->setData(2, Qt::DisplayRole, s.to); it->setData(3, Qt::DisplayRole, s.address); it->setData(4, Qt::DisplayRole, s.flags); w->addTopLevelItem(it); } createNewDock(w); } // CppDebuggerEngine Context CppDebuggerEngine::languageContext() const { return Context(Constants::C_CPPDEBUGGER); } void CppDebuggerEngine::validateRunParameters(DebuggerRunParameters &rp) { const bool warnOnRelease = boolSetting(WarnOnReleaseBuilds) && rp.toolChainAbi.osFlavor() != Abi::AndroidLinuxFlavor; bool warnOnInappropriateDebugger = false; QString detailedWarning; switch (rp.toolChainAbi.binaryFormat()) { case Abi::PEFormat: { QString preferredDebugger; if (rp.toolChainAbi.osFlavor() == Abi::WindowsMSysFlavor) { if (rp.cppEngineType == CdbEngineType) preferredDebugger = "GDB"; } else if (rp.cppEngineType != CdbEngineType) { // osFlavor() is MSVC, so the recommended debugger is CDB preferredDebugger = "CDB"; } if (!preferredDebugger.isEmpty()) { warnOnInappropriateDebugger = true; detailedWarning = DebuggerEngine::tr( "The inferior is in the Portable Executable format.\n" "Selecting %1 as debugger would improve the debugging " "experience for this binary format.").arg(preferredDebugger); break; } if (warnOnRelease && rp.cppEngineType == CdbEngineType && rp.startMode != AttachToRemoteServer) { QTC_ASSERT(!rp.symbolFile.isEmpty(), return); if (!rp.symbolFile.exists() && !rp.symbolFile.endsWith(".exe")) rp.symbolFile = rp.symbolFile.stringAppended(".exe"); QString errorMessage; QStringList rc; if (getPDBFiles(rp.symbolFile.toString(), &rc, &errorMessage) && !rc.isEmpty()) return; if (!errorMessage.isEmpty()) { detailedWarning.append('\n'); detailedWarning.append(errorMessage); } } else { return; } break; } case Abi::ElfFormat: { if (rp.cppEngineType == CdbEngineType) { warnOnInappropriateDebugger = true; detailedWarning = DebuggerEngine::tr( "The inferior is in the ELF format.\n" "Selecting GDB or LLDB as debugger would improve the debugging " "experience for this binary format."); break; } Utils::ElfReader reader(rp.symbolFile.toString()); const ElfData elfData = reader.readHeaders(); const QString error = reader.errorString(); showMessage("EXAMINING " + rp.symbolFile.toString(), LogDebug); QByteArray msg = "ELF SECTIONS: "; static const QList interesting = { ".debug_info", ".debug_abbrev", ".debug_line", ".debug_str", ".debug_loc", ".debug_range", ".gdb_index", ".note.gnu.build-id", ".gnu.hash", ".gnu_debuglink" }; QSet seen; for (const ElfSectionHeader &header : elfData.sectionHeaders) { msg.append(header.name); msg.append(' '); if (interesting.contains(header.name)) seen.insert(header.name); } showMessage(QString::fromUtf8(msg), LogDebug); if (!error.isEmpty()) { showMessage("ERROR WHILE READING ELF SECTIONS: " + error, LogDebug); return; } if (elfData.sectionHeaders.isEmpty()) { showMessage("NO SECTION HEADERS FOUND. IS THIS AN EXECUTABLE?", LogDebug); return; } // Note: .note.gnu.build-id also appears in regular release builds. // bool hasBuildId = elfData.indexOf(".note.gnu.build-id") >= 0; bool hasEmbeddedInfo = elfData.indexOf(".debug_info") >= 0; bool hasLink = elfData.indexOf(".gnu_debuglink") >= 0; if (hasEmbeddedInfo) { const GlobalDebuggerOptions *options = Internal::globalDebuggerOptions(); SourcePathRegExpMap globalRegExpSourceMap; globalRegExpSourceMap.reserve(options->sourcePathRegExpMap.size()); for (const auto &entry : qAsConst(options->sourcePathRegExpMap)) { const QString expanded = Utils::globalMacroExpander()->expand(entry.second); if (!expanded.isEmpty()) globalRegExpSourceMap.push_back(qMakePair(entry.first, expanded)); } if (globalRegExpSourceMap.isEmpty()) return; if (QSharedPointer mapper = reader.readSection(".debug_str")) { const char *str = mapper->start; const char *limit = str + mapper->fdlen; bool found = false; while (str < limit) { const QString string = QString::fromUtf8(str); for (auto itExp = globalRegExpSourceMap.begin(), itEnd = globalRegExpSourceMap.end(); itExp != itEnd; ++itExp) { const QRegularExpressionMatch match = itExp->first.match(string); if (match.hasMatch()) { rp.sourcePathMap.insert(string.left(match.capturedStart()) + match.captured(1), itExp->second); found = true; break; } } if (found) break; const int len = int(strlen(str)); if (len == 0) break; str += len + 1; } } } if (hasEmbeddedInfo || hasLink) return; for (const QByteArray &name : qAsConst(interesting)) { const QString found = seen.contains(name) ? DebuggerEngine::tr("Found.") : DebuggerEngine::tr("Not found."); detailedWarning.append('\n' + DebuggerEngine::tr("Section %1: %2").arg(QString::fromUtf8(name)).arg(found)); } break; } default: return; } if (warnOnInappropriateDebugger) { AsynchronousMessageBox::information(DebuggerEngine::tr("Warning"), DebuggerEngine::tr("The selected debugger may be inappropriate for the inferior.\n" "Examining symbols and setting breakpoints by file name and line number " "may fail.\n") + '\n' + detailedWarning); } else if (warnOnRelease) { AsynchronousMessageBox::information(DebuggerEngine::tr("Warning"), DebuggerEngine::tr("This does not seem to be a \"Debug\" build.\n" "Setting breakpoints by file name and line number may fail.") + '\n' + detailedWarning); } } } // namespace Internal } // namespace Debugger #include "debuggerengine.moc"