/************************************************************************** ** ** This file is part of Qt Creator ** ** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). ** ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** Commercial Usage ** ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** GNU Lesser General Public License Usage ** ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at http://qt.nokia.com/contact. ** **************************************************************************/ #include "debuggerplugin.h" #include "breakhandler.h" #include "debuggeractions.h" #include "debuggerdialogs.h" #include "debuggerconstants.h" #include "debuggermanager.h" #include "debuggerrunner.h" #include "debuggerstringutils.h" #include "ui_commonoptionspage.h" #include "ui_dumperoptionpage.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 #include #include #include #include #include #include #include #include using namespace Core; using namespace Debugger::Constants; using namespace Debugger::Internal; using namespace ProjectExplorer; using namespace TextEditor; namespace Debugger { namespace Constants { const char * const M_DEBUG_START_DEBUGGING = "QtCreator.Menu.Debug.StartDebugging"; const char * const STARTEXTERNAL = "Debugger.StartExternal"; const char * const ATTACHEXTERNAL = "Debugger.AttachExternal"; const char * const ATTACHCORE = "Debugger.AttachCore"; const char * const ATTACHREMOTE = "Debugger.AttachRemote"; const char * const DETACH = "Debugger.Detach"; const char * const RUN_TO_LINE = "Debugger.RunToLine"; const char * const RUN_TO_FUNCTION = "Debugger.RunToFunction"; const char * const JUMP_TO_LINE = "Debugger.JumpToLine"; const char * const TOGGLE_BREAK = "Debugger.ToggleBreak"; const char * const BREAK_BY_FUNCTION = "Debugger.BreakByFunction"; const char * const BREAK_AT_MAIN = "Debugger.BreakAtMain"; const char * const ADD_TO_WATCH = "Debugger.AddToWatch"; const char * const OPERATE_BY_INSTRUCTION = "Debugger.OperateByInstruction"; #ifdef Q_WS_MAC const char * const INTERRUPT_KEY = "Shift+F5"; const char * const RESET_KEY = "Ctrl+Shift+F5"; const char * const STEP_KEY = "F7"; const char * const STEPOUT_KEY = "Shift+F7"; const char * const NEXT_KEY = "F6"; const char * const REVERSE_KEY = ""; const char * const RUN_TO_LINE_KEY = "Shift+F8"; const char * const RUN_TO_FUNCTION_KEY = "Ctrl+F6"; const char * const JUMP_TO_LINE_KEY = "Alt+D,Alt+L"; const char * const TOGGLE_BREAK_KEY = "F8"; const char * const BREAK_BY_FUNCTION_KEY = "Alt+D,Alt+F"; const char * const BREAK_AT_MAIN_KEY = "Alt+D,Alt+M"; const char * const ADD_TO_WATCH_KEY = "Alt+D,Alt+W"; #else const char * const INTERRUPT_KEY = "Shift+F5"; const char * const RESET_KEY = "Ctrl+Shift+F5"; const char * const STEP_KEY = "F11"; const char * const STEPOUT_KEY = "Shift+F11"; const char * const NEXT_KEY = "F10"; const char * const REVERSE_KEY = "F12"; const char * const RUN_TO_LINE_KEY = ""; const char * const RUN_TO_FUNCTION_KEY = ""; const char * const JUMP_TO_LINE_KEY = ""; const char * const TOGGLE_BREAK_KEY = "F9"; const char * const BREAK_BY_FUNCTION_KEY = ""; const char * const BREAK_AT_MAIN_KEY = ""; const char * const ADD_TO_WATCH_KEY = "Ctrl+Alt+Q"; #endif } // namespace Constants } // namespace Debugger static ProjectExplorer::SessionManager *sessionManager() { return ProjectExplorer::ProjectExplorerPlugin::instance()->session(); } static QSettings *settings() { return ICore::instance()->settings(); } static QToolButton *toolButton(QAction *action) { QToolButton *button = new QToolButton; button->setDefaultAction(action); return button; } /////////////////////////////////////////////////////////////////////// // // DebugMode // /////////////////////////////////////////////////////////////////////// namespace Debugger { namespace Internal { class DebugMode : public Core::BaseMode { Q_OBJECT public: DebugMode(QObject *parent = 0); ~DebugMode(); }; DebugMode::DebugMode(QObject *parent) : BaseMode(parent) { setName(tr("Debug")); setUniqueModeName(Constants::MODE_DEBUG); setIcon(QIcon(":/fancyactionbar/images/mode_Debug.png")); setPriority(Constants::P_MODE_DEBUG); } DebugMode::~DebugMode() { // Make sure the editor manager does not get deleted EditorManager::instance()->setParent(0); } /////////////////////////////////////////////////////////////////////// // // DebuggerListener: Close the debugging session if running. // /////////////////////////////////////////////////////////////////////// class DebuggerListener : public Core::ICoreListener { Q_OBJECT public: explicit DebuggerListener(QObject *parent = 0); virtual bool coreAboutToClose(); }; DebuggerListener::DebuggerListener(QObject *parent) : Core::ICoreListener(parent) { } bool DebuggerListener::coreAboutToClose() { DebuggerManager *mgr = DebuggerManager::instance(); if (!mgr) return true; // Ask to terminate the session. const QString title = tr("Close Debugging Session"); bool cleanTermination = false; switch (mgr->state()) { case DebuggerNotReady: return true; case AdapterStarted: // Most importantly, terminating a running case AdapterStartFailed: // debuggee can cause problems. case InferiorUnrunnable: case InferiorStartFailed: case InferiorStopped: case InferiorShutDown: cleanTermination = true; break; default: break; } const QString question = cleanTermination ? tr("A debugging session is still in progress. Would you like to terminate it?") : tr("A debugging session is still in progress. Terminating the session in the current" " state (%1) can leave the target in an inconsistent state." " Would you still like to terminate it?") .arg(QLatin1String(DebuggerManager::stateName(mgr->state()))); QMessageBox::StandardButton answer = QMessageBox::question(mgr->mainWindow(), title, question, QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes); if (answer != QMessageBox::Yes) return false; mgr->exitDebugger(); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); return true; } } // namespace Internal } // namespace Debugger /////////////////////////////////////////////////////////////////////// // // LocationMark // /////////////////////////////////////////////////////////////////////// namespace Debugger { namespace Internal { static QIcon locationMarkIcon() { static const QIcon icon(":/debugger/images/location.svg"); return icon; } // Used in "real" editors class LocationMark : public TextEditor::BaseTextMark { Q_OBJECT public: LocationMark(const QString &fileName, int linenumber) : BaseTextMark(fileName, linenumber) {} QIcon icon() const { return locationMarkIcon(); } void updateLineNumber(int /*lineNumber*/) {} void updateBlock(const QTextBlock & /*block*/) {} void removedFromEditor() {} }; } // namespace Internal } // namespace Debugger /////////////////////////////////////////////////////////////////////// // // CommonOptionsPage // /////////////////////////////////////////////////////////////////////// namespace Debugger { namespace Internal { class CommonOptionsPage : public Core::IOptionsPage { Q_OBJECT public: CommonOptionsPage() {} // IOptionsPage QString id() const { return QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE); } QString trName() const { return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE); } QString category() const { return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); } QString trCategory() const { return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); } QWidget *createPage(QWidget *parent); void apply() { m_group.apply(settings()); } void finish() { m_group.finish(); } private: Ui::CommonOptionsPage m_ui; Utils::SavedActionSet m_group; }; QWidget *CommonOptionsPage::createPage(QWidget *parent) { QWidget *w = new QWidget(parent); m_ui.setupUi(w); m_group.clear(); m_group.insert(theDebuggerAction(ListSourceFiles), m_ui.checkBoxListSourceFiles); m_group.insert(theDebuggerAction(UseAlternatingRowColors), m_ui.checkBoxUseAlternatingRowColors); m_group.insert(theDebuggerAction(UseMessageBoxForSignals), m_ui.checkBoxUseMessageBoxForSignals); m_group.insert(theDebuggerAction(SkipKnownFrames), m_ui.checkBoxSkipKnownFrames); m_group.insert(theDebuggerAction(UseToolTipsInMainEditor), m_ui.checkBoxUseToolTipsInMainEditor); m_group.insert(theDebuggerAction(AutoDerefPointers), 0); m_group.insert(theDebuggerAction(UseToolTipsInLocalsView), 0); m_group.insert(theDebuggerAction(UseToolTipsInBreakpointsView), 0); m_group.insert(theDebuggerAction(UseAddressInBreakpointsView), 0); m_group.insert(theDebuggerAction(UseAddressInStackView), 0); m_group.insert(theDebuggerAction(EnableReverseDebugging), m_ui.checkBoxEnableReverseDebugging); m_group.insert(theDebuggerAction(MaximalStackDepth), m_ui.spinBoxMaximalStackDepth); #ifdef USE_REVERSE_DEBUGGING m_ui.checkBoxEnableReverseDebugging->hide(); #endif return w; } } // namespace Internal } // namespace Debugger /////////////////////////////////////////////////////////////////////// // // DebuggingHelperOptionPage // /////////////////////////////////////////////////////////////////////// namespace Debugger { namespace Internal { class DebuggingHelperOptionPage : public Core::IOptionsPage { Q_OBJECT public: DebuggingHelperOptionPage() {} // IOptionsPage QString id() const { return QLatin1String("DebuggingHelper"); } QString trName() const { return tr("Debugging Helper"); } QString category() const { return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); } QString trCategory() const { return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); } QWidget *createPage(QWidget *parent); void apply() { m_group.apply(settings()); } void finish() { m_group.finish(); } private: Q_SLOT void updateState(); friend class DebuggerPlugin; Ui::DebuggingHelperOptionPage m_ui; Utils::SavedActionSet m_group; }; QWidget *DebuggingHelperOptionPage::createPage(QWidget *parent) { QWidget *w = new QWidget(parent); m_ui.setupUi(w); m_ui.dumperLocationChooser->setExpectedKind(Utils::PathChooser::Command); m_ui.dumperLocationChooser->setPromptDialogTitle(tr("Choose DebuggingHelper Location")); m_ui.dumperLocationChooser->setInitialBrowsePathBackup( Core::ICore::instance()->resourcePath() + "../../lib"); connect(m_ui.checkBoxUseDebuggingHelpers, SIGNAL(toggled(bool)), this, SLOT(updateState())); connect(m_ui.checkBoxUseCustomDebuggingHelperLocation, SIGNAL(toggled(bool)), this, SLOT(updateState())); m_group.clear(); m_group.insert(theDebuggerAction(UseDebuggingHelpers), m_ui.checkBoxUseDebuggingHelpers); m_group.insert(theDebuggerAction(UseCustomDebuggingHelperLocation), m_ui.checkBoxUseCustomDebuggingHelperLocation); m_group.insert(theDebuggerAction(CustomDebuggingHelperLocation), m_ui.dumperLocationChooser); m_group.insert(theDebuggerAction(UseCodeModel), m_ui.checkBoxUseCodeModel); #ifdef QT_DEBUG m_group.insert(theDebuggerAction(DebugDebuggingHelpers), m_ui.checkBoxDebugDebuggingHelpers); #else m_ui.checkBoxDebugDebuggingHelpers->hide(); #endif m_ui.dumperLocationChooser-> setEnabled(theDebuggerAction(UseCustomDebuggingHelperLocation)->value().toBool()); #ifndef QT_DEBUG #if 0 cmd = am->registerAction(m_manager->m_dumpLogAction, Constants::DUMP_LOG, globalcontext); //cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+D,Ctrl+L"))); cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F11"))); mdebug->addAction(cmd); #endif #endif updateState(); return w; } void DebuggingHelperOptionPage::updateState() { m_ui.checkBoxUseCustomDebuggingHelperLocation->setEnabled( m_ui.checkBoxUseDebuggingHelpers->isChecked()); bool locationEnabled = m_ui.checkBoxUseDebuggingHelpers->isChecked() && m_ui.checkBoxUseCustomDebuggingHelperLocation->isChecked(); m_ui.dumperLocationChooser->setEnabled(locationEnabled); m_ui.dumperLocationLabel->setEnabled(locationEnabled); } } // namespace Internal } // namespace Debugger /////////////////////////////////////////////////////////////////////// // // DebuggerPlugin // /////////////////////////////////////////////////////////////////////// DebuggerPlugin::DebuggerPlugin() : m_manager(0), m_debugMode(0), m_locationMark(0), m_gdbRunningContext(0), m_cmdLineEnabledEngines(AllEngineTypes), m_cmdLineAttachPid(0), m_cmdLineWinCrashEvent(0), m_toggleLockedAction(0) {} DebuggerPlugin::~DebuggerPlugin() {} void DebuggerPlugin::shutdown() { QTC_ASSERT(m_manager, /**/); if (m_manager) m_manager->shutdown(); writeSettings(); delete DebuggerSettings::instance(); //qDebug() << "DebuggerPlugin::~DebuggerPlugin"; removeObject(m_debugMode); // FIXME: when using the line below, BreakWindow etc gets deleted twice. // so better leak for now... delete m_debugMode; m_debugMode = 0; delete m_locationMark; m_locationMark = 0; delete m_manager; m_manager = 0; } static QString msgParameterMissing(const QString &a) { return DebuggerPlugin::tr("Option '%1' is missing the parameter.").arg(a); } static QString msgInvalidNumericParameter(const QString &a, const QString &number) { return DebuggerPlugin::tr("The parameter '%1' of option '%2' is not a number.").arg(number, a); } // Parse arguments bool DebuggerPlugin::parseArgument(QStringList::const_iterator &it, const QStringList::const_iterator &cend, QString *errorMessage) { const QString &option = *it; // '-debug ' if (*it == QLatin1String("-debug")) { ++it; if (it == cend) { *errorMessage = msgParameterMissing(*it); return false; } bool ok; m_cmdLineAttachPid = it->toULongLong(&ok); if (!ok) { m_cmdLineAttachPid = 0; m_cmdLineAttachCore = *it; } return true; } // -wincrashevent . A handle used for // a handshake when attaching to a crashed Windows process. if (*it == QLatin1String("-wincrashevent")) { ++it; if (it == cend) { *errorMessage = msgParameterMissing(*it); return false; } bool ok; m_cmdLineWinCrashEvent = it->toULongLong(&ok); if (!ok) { *errorMessage = msgInvalidNumericParameter(option, *it); return false; } return true; } // engine disabling if (option == QLatin1String("-disable-cdb")) { m_cmdLineEnabledEngines &= ~CdbEngineType; return true; } if (option == QLatin1String("-disable-gdb")) { m_cmdLineEnabledEngines &= ~GdbEngineType; return true; } if (option == QLatin1String("-disable-sdb")) { m_cmdLineEnabledEngines &= ~ScriptEngineType; return true; } *errorMessage = tr("Invalid debugger option: %1").arg(option); return false; } bool DebuggerPlugin::parseArguments(const QStringList &args, QString *errorMessage) { const QStringList::const_iterator cend = args.constEnd(); for (QStringList::const_iterator it = args.constBegin(); it != cend; ++it) if (!parseArgument(it, cend, errorMessage)) return false; if (Debugger::Constants::Internal::debug) qDebug().nospace() << args << "engines=0x" << QString::number(m_cmdLineEnabledEngines, 16) << " pid" << m_cmdLineAttachPid << " core" << m_cmdLineAttachCore << '\n'; return true; } bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMessage) { // Do not fail the whole plugin if something goes wrong here if (!parseArguments(arguments, errorMessage)) { *errorMessage = tr("Error evaluating command line arguments: %1") .arg(*errorMessage); qWarning("%s\n", qPrintable(*errorMessage)); errorMessage->clear(); } m_manager = new DebuggerManager; const QList engineOptionPages = m_manager->initializeEngines(m_cmdLineEnabledEngines); ICore *core = ICore::instance(); QTC_ASSERT(core, return false); Core::ActionManager *am = core->actionManager(); QTC_ASSERT(am, return false); Core::UniqueIDManager *uidm = core->uniqueIDManager(); QTC_ASSERT(uidm, return false); QList globalcontext; globalcontext << Core::Constants::C_GLOBAL_ID; QList cppcontext; cppcontext << uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX); QList debuggercontext; debuggercontext << uidm->uniqueIdentifier(C_GDBDEBUGGER); QList cppeditorcontext; cppeditorcontext << uidm->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR); QList texteditorcontext; texteditorcontext << uidm->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR); m_gdbRunningContext = uidm->uniqueIdentifier(Constants::GDBRUNNING); //Core::ActionContainer *mcppcontext = // am->actionContainer(CppEditor::Constants::M_CONTEXT); // External apps m_startExternalAction = new QAction(this); m_startExternalAction->setText(tr("Start and Debug External Application...")); connect(m_startExternalAction, SIGNAL(triggered()), this, SLOT(startExternalApplication())); m_attachExternalAction = new QAction(this); m_attachExternalAction->setText(tr("Attach to Running External Application...")); connect(m_attachExternalAction, SIGNAL(triggered()), this, SLOT(attachExternalApplication())); m_attachCoreAction = new QAction(this); m_attachCoreAction->setText(tr("Attach to Core...")); connect(m_attachCoreAction, SIGNAL(triggered()), this, SLOT(attachCore())); m_startRemoteAction = new QAction(this); m_startRemoteAction->setText(tr("Start and Attach to Remote Application...")); connect(m_startRemoteAction, SIGNAL(triggered()), this, SLOT(startRemoteApplication())); m_detachAction = new QAction(this); m_detachAction->setText(tr("Detach Debugger")); connect(m_detachAction, SIGNAL(triggered()), m_manager, SLOT(detachDebugger())); Core::ActionContainer *mdebug = am->actionContainer(ProjectExplorer::Constants::M_DEBUG); Core::ActionContainer *mstart = am->actionContainer(ProjectExplorer::Constants::M_DEBUG_STARTDEBUGGING); Core::Command *cmd = 0; const DebuggerManagerActions actions = m_manager->debuggerManagerActions(); cmd = am->registerAction(actions.continueAction, ProjectExplorer::Constants::DEBUG, QList() << m_gdbRunningContext); mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE); cmd = am->registerAction(m_startExternalAction, Constants::STARTEXTERNAL, globalcontext); mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE); cmd = am->registerAction(m_attachExternalAction, Constants::ATTACHEXTERNAL, globalcontext); mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE); cmd = am->registerAction(m_attachCoreAction, Constants::ATTACHCORE, globalcontext); mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE); cmd = am->registerAction(m_startRemoteAction, Constants::ATTACHREMOTE, globalcontext); mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE); cmd = am->registerAction(m_detachAction, Constants::DETACH, globalcontext); mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); cmd = am->registerAction(actions.stopAction, Constants::INTERRUPT, globalcontext); cmd->setAttribute(Core::Command::CA_UpdateText); cmd->setAttribute(Core::Command::CA_UpdateIcon); cmd->setDefaultKeySequence(QKeySequence(Constants::INTERRUPT_KEY)); cmd->setDefaultText(tr("Stop Debugger/Interrupt Debugger")); mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); cmd = am->registerAction(actions.resetAction, Constants::RESET, globalcontext); cmd->setAttribute(Core::Command::CA_UpdateText); cmd->setDefaultKeySequence(QKeySequence(Constants::RESET_KEY)); cmd->setDefaultText(tr("Reset Debugger")); //disabled mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE); QAction *sep = new QAction(this); sep->setSeparator(true); cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Step"), globalcontext); mdebug->addAction(cmd); cmd = am->registerAction(actions.nextAction, Constants::NEXT, debuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::NEXT_KEY)); mdebug->addAction(cmd); cmd = am->registerAction(actions.stepAction, Constants::STEP, debuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::STEP_KEY)); mdebug->addAction(cmd); cmd = am->registerAction(actions.stepOutAction, Constants::STEPOUT, debuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::STEPOUT_KEY)); mdebug->addAction(cmd); cmd = am->registerAction(actions.runToLineAction, Constants::RUN_TO_LINE, debuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_LINE_KEY)); mdebug->addAction(cmd); cmd = am->registerAction(actions.runToFunctionAction, Constants::RUN_TO_FUNCTION, debuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_FUNCTION_KEY)); mdebug->addAction(cmd); cmd = am->registerAction(actions.jumpToLineAction, Constants::JUMP_TO_LINE, debuggercontext); mdebug->addAction(cmd); #ifdef USE_REVERSE_DEBUGGING cmd = am->registerAction(actions.reverseDirectionAction, Constants::REVERSE, debuggercontext); cmd->setDefaultKeySequence(QKeySequence(Constants::REVERSE_KEY)); mdebug->addAction(cmd); #endif sep = new QAction(this); sep->setSeparator(true); cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Break"), globalcontext); mdebug->addAction(cmd); cmd = am->registerAction(theDebuggerAction(OperateByInstruction), Constants::OPERATE_BY_INSTRUCTION, debuggercontext); mdebug->addAction(cmd); cmd = am->registerAction(actions.breakAction, Constants::TOGGLE_BREAK, cppeditorcontext); cmd->setDefaultKeySequence(QKeySequence(Constants::TOGGLE_BREAK_KEY)); mdebug->addAction(cmd); //mcppcontext->addAction(cmd); sep = new QAction(this); sep->setSeparator(true); cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Watch"), globalcontext); mdebug->addAction(cmd); cmd = am->registerAction(actions.watchAction, Constants::ADD_TO_WATCH, cppeditorcontext); //cmd->setDefaultKeySequence(QKeySequence(tr("ALT+D,ALT+W"))); mdebug->addAction(cmd); // Views menu cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Views"), globalcontext); mdebug->addAction(cmd); ActionContainer *viewsMenu = am->createMenu(Constants::M_DEBUG_VIEWS); QMenu *m = viewsMenu->menu(); m->setEnabled(true); m->setTitle(tr("&Views")); mdebug->addMenu(viewsMenu, Core::Constants::G_DEFAULT_THREE); m_toggleLockedAction = new QAction(tr("Locked"), this); m_toggleLockedAction->setCheckable(true); m_toggleLockedAction->setChecked(true); connect(m_toggleLockedAction, SIGNAL(toggled(bool)), m_manager->mainWindow(), SLOT(setLocked(bool))); foreach (QDockWidget *dockWidget, m_manager->mainWindow()->dockWidgets()) { cmd = am->registerAction(dockWidget->toggleViewAction(), "Debugger." + dockWidget->objectName(), debuggercontext); viewsMenu->addAction(cmd); //m->addAction(dockWidget->toggleViewAction()); } m->addSeparator(); m->addAction(m_toggleLockedAction); m->addSeparator(); QAction *resetToSimpleAction = viewsMenu->menu()->addAction(tr("Reset to default layout")); connect(resetToSimpleAction, SIGNAL(triggered()), m_manager, SLOT(setSimpleDockWidgetArrangement())); // FIXME: addAutoReleasedObject(new CommonOptionsPage); addAutoReleasedObject(new DebuggingHelperOptionPage); foreach (Core::IOptionsPage* op, engineOptionPages) addAutoReleasedObject(op); addAutoReleasedObject(new DebuggerListener); m_locationMark = 0; // // Debug mode setup // m_debugMode = new DebugMode(this); //addAutoReleasedObject(m_debugMode); // register factory of DebuggerRunControl m_debuggerRunControlFactory = new DebuggerRunControlFactory(m_manager); addAutoReleasedObject(m_debuggerRunControlFactory); QList context; context.append(uidm->uniqueIdentifier(Core::Constants::C_EDITORMANAGER)); context.append(uidm->uniqueIdentifier(Debugger::Constants::C_GDBDEBUGGER)); context.append(uidm->uniqueIdentifier(Core::Constants::C_NAVIGATION_PANE)); m_debugMode->setContext(context); QBoxLayout *editorHolderLayout = new QVBoxLayout; editorHolderLayout->setMargin(0); editorHolderLayout->setSpacing(0); QWidget *editorAndFindWidget = new QWidget; editorAndFindWidget->setLayout(editorHolderLayout); editorHolderLayout->addWidget(new EditorManagerPlaceHolder(m_debugMode)); editorHolderLayout->addWidget(new FindToolBarPlaceHolder(editorAndFindWidget)); MiniSplitter *rightPaneSplitter = new MiniSplitter; rightPaneSplitter->addWidget(editorAndFindWidget); rightPaneSplitter->addWidget(new RightPanePlaceHolder(m_debugMode)); rightPaneSplitter->setStretchFactor(0, 1); rightPaneSplitter->setStretchFactor(1, 0); QWidget *centralWidget = new QWidget; m_manager->mainWindow()->setCentralWidget(centralWidget); MiniSplitter *splitter = new MiniSplitter; splitter->addWidget(m_manager->mainWindow()); splitter->addWidget(new OutputPanePlaceHolder(m_debugMode)); splitter->setStretchFactor(0, 10); splitter->setStretchFactor(1, 0); splitter->setOrientation(Qt::Vertical); MiniSplitter *splitter2 = new MiniSplitter; splitter2->addWidget(new NavigationWidgetPlaceHolder(m_debugMode)); splitter2->addWidget(splitter); splitter2->setStretchFactor(0, 0); splitter2->setStretchFactor(1, 1); m_debugMode->setWidget(splitter2); Utils::StyledBar *debugToolBar = new Utils::StyledBar; debugToolBar->setProperty("topBorder", true); QHBoxLayout *debugToolBarLayout = new QHBoxLayout(debugToolBar); debugToolBarLayout->setMargin(0); debugToolBarLayout->setSpacing(0); debugToolBarLayout->addWidget(toolButton(am->command(ProjectExplorer::Constants::DEBUG)->action())); debugToolBarLayout->addWidget(toolButton(am->command(Constants::INTERRUPT)->action())); debugToolBarLayout->addWidget(toolButton(am->command(Constants::NEXT)->action())); debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEP)->action())); debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEPOUT)->action())); debugToolBarLayout->addWidget(toolButton(am->command(Constants::OPERATE_BY_INSTRUCTION)->action())); #ifdef USE_REVERSE_DEBUGGING debugToolBarLayout->addWidget(new Utils::StyledSeparator); debugToolBarLayout->addWidget(toolButton(am->command(Constants::REVERSE)->action())); #endif debugToolBarLayout->addWidget(new Utils::StyledSeparator); debugToolBarLayout->addWidget(new QLabel(tr("Threads:"))); QComboBox *threadBox = new QComboBox; threadBox->setModel(m_manager->threadsModel()); connect(threadBox, SIGNAL(activated(int)), m_manager->threadsWindow(), SIGNAL(threadSelected(int))); debugToolBarLayout->addWidget(threadBox); debugToolBarLayout->addWidget(m_manager->statusLabel(), 10); QBoxLayout *toolBarAddingLayout = new QVBoxLayout(centralWidget); toolBarAddingLayout->setMargin(0); toolBarAddingLayout->setSpacing(0); toolBarAddingLayout->addWidget(rightPaneSplitter); toolBarAddingLayout->addWidget(debugToolBar); m_manager->setSimpleDockWidgetArrangement(); readSettings(); connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)), this, SLOT(onModeChanged(Core::IMode*))); m_debugMode->widget()->setFocusProxy(EditorManager::instance()); addObject(m_debugMode); // // Connections // // ProjectExplorer connect(sessionManager(), SIGNAL(sessionLoaded()), m_manager, SLOT(sessionLoaded())); connect(sessionManager(), SIGNAL(aboutToSaveSession()), m_manager, SLOT(aboutToSaveSession())); connect(sessionManager(), SIGNAL(aboutToUnloadSession()), m_manager, SLOT(aboutToUnloadSession())); // EditorManager QObject *editorManager = core->editorManager(); connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)), this, SLOT(editorAboutToClose(Core::IEditor*))); connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)), this, SLOT(editorOpened(Core::IEditor*))); // Application interaction connect(m_manager, SIGNAL(currentTextEditorRequested(QString*,int*,QObject**)), this, SLOT(queryCurrentTextEditor(QString*,int*,QObject**))); connect(m_manager, SIGNAL(setSessionValueRequested(QString,QVariant)), this, SLOT(setSessionValue(QString,QVariant))); connect(m_manager, SIGNAL(sessionValueRequested(QString,QVariant*)), this, SLOT(querySessionValue(QString,QVariant*))); connect(m_manager, SIGNAL(resetLocationRequested()), this, SLOT(resetLocation())); connect(m_manager, SIGNAL(gotoLocationRequested(QString,int,bool)), this, SLOT(gotoLocation(QString,int,bool))); connect(m_manager, SIGNAL(stateChanged(int)), this, SLOT(handleStateChanged(int))); connect(m_manager, SIGNAL(previousModeRequested()), this, SLOT(activatePreviousMode())); connect(m_manager, SIGNAL(debugModeRequested()), this, SLOT(activateDebugMode())); connect(theDebuggerAction(SettingsDialog), SIGNAL(triggered()), this, SLOT(showSettingsDialog())); handleStateChanged(DebuggerNotReady); return true; } void DebuggerPlugin::extensionsInitialized() { // time gdb -i mi -ex 'debuggerplugin.cpp:800' -ex r -ex q bin/qtcreator.bin const QByteArray env = qgetenv("QTC_DEBUGGER_TEST"); //qDebug() << "EXTENSIONS INITIALIZED:" << env; if (!env.isEmpty()) m_manager->runTest(QString::fromLocal8Bit(env)); if (m_cmdLineAttachPid) QTimer::singleShot(0, this, SLOT(attachCmdLinePid())); if (!m_cmdLineAttachCore.isEmpty()) QTimer::singleShot(0, this, SLOT(attachCmdLineCore())); } void DebuggerPlugin::attachCmdLinePid() { m_manager->showStatusMessage(tr("Attaching to PID %1.").arg(m_cmdLineAttachPid)); const QString crashParameter = m_cmdLineWinCrashEvent ? QString::number(m_cmdLineWinCrashEvent) : QString(); attachExternalApplication(m_cmdLineAttachPid, crashParameter); } /*! Activates the previous mode when the current mode is the debug mode. */ void DebuggerPlugin::activatePreviousMode() { Core::ModeManager *const modeManager = ICore::instance()->modeManager(); if (modeManager->currentMode() == modeManager->mode(Constants::MODE_DEBUG) && !m_previousMode.isEmpty()) { modeManager->activateMode(m_previousMode); m_previousMode.clear(); } } void DebuggerPlugin::activateDebugMode() { ModeManager *modeManager = ModeManager::instance(); m_previousMode = QLatin1String(modeManager->currentMode()->uniqueModeName()); modeManager->activateMode(QLatin1String(MODE_DEBUG)); } void DebuggerPlugin::queryCurrentTextEditor(QString *fileName, int *lineNumber, QObject **object) { EditorManager *editorManager = EditorManager::instance(); if (!editorManager) return; Core::IEditor *editor = editorManager->currentEditor(); ITextEditor *textEditor = qobject_cast(editor); if (!textEditor) return; if (fileName) *fileName = textEditor->file()->fileName(); if (lineNumber) *lineNumber = textEditor->currentLine(); if (object) *object = textEditor->widget(); } void DebuggerPlugin::editorOpened(Core::IEditor *editor) { if (ITextEditor *textEditor = qobject_cast(editor)) { connect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)), this, SLOT(requestMark(TextEditor::ITextEditor*,int))); connect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)), this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int))); connect(textEditor, SIGNAL(markContextMenuRequested(TextEditor::ITextEditor*,int,QMenu*)), this, SLOT(requestContextMenu(TextEditor::ITextEditor*,int,QMenu*))); } } void DebuggerPlugin::editorAboutToClose(Core::IEditor *editor) { if (ITextEditor *textEditor = qobject_cast(editor)) { disconnect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)), this, SLOT(requestMark(TextEditor::ITextEditor*,int))); disconnect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)), this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int))); disconnect(textEditor, SIGNAL(markContextMenuRequested(TextEditor::ITextEditor*,int,QMenu*)), this, SLOT(requestContextMenu(TextEditor::ITextEditor*,int,QMenu*))); } } void DebuggerPlugin::requestContextMenu(TextEditor::ITextEditor *editor, int lineNumber, QMenu *menu) { QString fileName = editor->file()->fileName(); QString position = fileName + QString(":%1").arg(lineNumber); BreakpointData *data = m_manager->findBreakpoint(fileName, lineNumber); if (data) { // existing breakpoint QAction *act = new QAction(tr("Remove Breakpoint"), menu); act->setData(position); connect(act, SIGNAL(triggered()), this, SLOT(breakpointSetRemoveMarginActionTriggered())); menu->addAction(act); QAction *act2; if (data->enabled) act2 = new QAction(tr("Disable Breakpoint"), menu); else act2 = new QAction(tr("Enable Breakpoint"), menu); act2->setData(position); connect(act2, SIGNAL(triggered()), this, SLOT(breakpointEnableDisableMarginActionTriggered())); menu->addAction(act2); } else { // non-existing QAction *act = new QAction(tr("Set Breakpoint"), menu); act->setData(position); connect(act, SIGNAL(triggered()), this, SLOT(breakpointSetRemoveMarginActionTriggered())); menu->addAction(act); } } void DebuggerPlugin::breakpointSetRemoveMarginActionTriggered() { if (QAction *act = qobject_cast(sender())) { QString str = act->data().toString(); int pos = str.lastIndexOf(':'); m_manager->toggleBreakpoint(str.left(pos), str.mid(pos + 1).toInt()); } } void DebuggerPlugin::breakpointEnableDisableMarginActionTriggered() { if (QAction *act = qobject_cast(sender())) { QString str = act->data().toString(); int pos = str.lastIndexOf(':'); m_manager->toggleBreakpointEnabled(str.left(pos), str.mid(pos + 1).toInt()); } } void DebuggerPlugin::requestMark(TextEditor::ITextEditor *editor, int lineNumber) { m_manager->toggleBreakpoint(editor->file()->fileName(), lineNumber); } void DebuggerPlugin::showToolTip(TextEditor::ITextEditor *editor, const QPoint &point, int pos) { if (!theDebuggerBoolSetting(UseToolTipsInMainEditor) || m_manager->state() == DebuggerNotReady) return; m_manager->setToolTipExpression(point, editor, pos); } void DebuggerPlugin::setSessionValue(const QString &name, const QVariant &value) { //qDebug() << "SET SESSION VALUE" << name << value; QTC_ASSERT(sessionManager(), return); sessionManager()->setValue(name, value); } void DebuggerPlugin::querySessionValue(const QString &name, QVariant *value) { QTC_ASSERT(sessionManager(), return); *value = sessionManager()->value(name); //qDebug() << "GET SESSION VALUE: " << name << value; } void DebuggerPlugin::setConfigValue(const QString &name, const QVariant &value) { QTC_ASSERT(m_debugMode, return); settings()->setValue(name, value); } QVariant DebuggerPlugin::configValue(const QString &name) const { QTC_ASSERT(m_debugMode, return QVariant()); return settings()->value(name); } void DebuggerPlugin::queryConfigValue(const QString &name, QVariant *value) { QTC_ASSERT(m_debugMode, return); *value = settings()->value(name); } void DebuggerPlugin::resetLocation() { //qDebug() << "RESET_LOCATION: current:" << currentTextEditor(); //qDebug() << "RESET_LOCATION: locations:" << m_locationMark; //qDebug() << "RESET_LOCATION: stored:" << m_locationMark->editor(); delete m_locationMark; m_locationMark = 0; } void DebuggerPlugin::gotoLocation(const QString &file, int line, bool setMarker) { TextEditor::BaseTextEditor::openEditorAt(file, line); if (setMarker) { resetLocation(); m_locationMark = new LocationMark(file, line); } } void DebuggerPlugin::handleStateChanged(int state) { const bool startIsContinue = (state == InferiorStopped); ICore *core = ICore::instance(); if (startIsContinue) { core->addAdditionalContext(m_gdbRunningContext); core->updateContext(); } else { core->removeAdditionalContext(m_gdbRunningContext); core->updateContext(); } const bool started = state == InferiorRunning || state == InferiorRunningRequested || state == InferiorStopping || state == InferiorStopped; const bool starting = state == EngineStarting; //const bool running = state == InferiorRunning; const bool detachable = state == InferiorStopped && m_manager->startParameters()->startMode != AttachCore; m_startExternalAction->setEnabled(!started && !starting); m_attachExternalAction->setEnabled(!started && !starting); #ifdef Q_OS_WIN m_attachCoreAction->setEnabled(false); #else m_attachCoreAction->setEnabled(!started && !starting); #endif m_startRemoteAction->setEnabled(!started && !starting); m_detachAction->setEnabled(detachable); } void DebuggerPlugin::writeSettings() const { QTC_ASSERT(m_manager, return); QTC_ASSERT(m_manager->mainWindow(), return); QSettings *s = settings(); DebuggerSettings::instance()->writeSettings(s); s->beginGroup(QLatin1String("DebugMode")); m_manager->mainWindow()->saveSettings(s); s->endGroup(); } void DebuggerPlugin::readSettings() { QSettings *s = settings(); DebuggerSettings::instance()->readSettings(s); QString defaultCommand("gdb"); #ifdef Q_OS_WIN defaultCommand.append(".exe"); #endif //QString defaultScript = ICore::instance()->resourcePath() + // QLatin1String("/gdb/qt4macros"); QString defaultScript; s->beginGroup(QLatin1String("DebugMode")); m_manager->mainWindow()->restoreSettings(s); m_toggleLockedAction->setChecked(m_manager->mainWindow()->isLocked()); s->endGroup(); } void DebuggerPlugin::onModeChanged(IMode *mode) { // FIXME: This one gets always called, even if switching between modes // different then the debugger mode. E.g. Welcome and Help mode and // also on shutdown. if (mode != m_debugMode) return; EditorManager *editorManager = EditorManager::instance(); if (editorManager->currentEditor()) editorManager->currentEditor()->widget()->setFocus(); } void DebuggerPlugin::showSettingsDialog() { Core::ICore::instance()->showOptionsDialog( QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_PAGE)); } static RunConfigurationPtr activeRunConfiguration() { ProjectExplorer::Project *project = ProjectExplorerPlugin::instance()->currentProject(); if (project) return project->activeRunConfiguration(); return RunConfigurationPtr(); } void DebuggerPlugin::startExternalApplication() { const DebuggerStartParametersPtr sp(new DebuggerStartParameters); StartExternalDialog dlg(m_manager->mainWindow()); dlg.setExecutableFile( configValue(_("LastExternalExecutableFile")).toString()); dlg.setExecutableArguments( configValue(_("LastExternalExecutableArguments")).toString()); if (dlg.exec() != QDialog::Accepted) return; setConfigValue(_("LastExternalExecutableFile"), dlg.executableFile()); setConfigValue(_("LastExternalExecutableArguments"), dlg.executableArguments()); sp->executable = dlg.executableFile(); sp->startMode = StartExternal; if (!dlg.executableArguments().isEmpty()) sp->processArgs = dlg.executableArguments().split(QLatin1Char(' ')); if (dlg.breakAtMain()) m_manager->breakByFunctionMain(); RunConfigurationPtr rc = activeRunConfiguration(); if (rc.isNull()) rc = DebuggerRunControlFactory::createDefaultRunConfiguration(sp->executable); if (RunControl *runControl = m_debuggerRunControlFactory ->create(rc, ProjectExplorer::Constants::DEBUGMODE, sp)) runControl->start(); } void DebuggerPlugin::attachExternalApplication() { AttachExternalDialog dlg(m_manager->mainWindow()); if (dlg.exec() == QDialog::Accepted) attachExternalApplication(dlg.attachPID()); } void DebuggerPlugin::attachExternalApplication(qint64 pid, const QString &crashParameter) { if (pid == 0) { QMessageBox::warning(m_manager->mainWindow(), tr("Warning"), tr("Cannot attach to PID 0")); return; } const DebuggerStartParametersPtr sp(new DebuggerStartParameters); sp->attachPID = pid; sp->crashParameter = crashParameter; sp->startMode = crashParameter.isEmpty() ? AttachExternal : AttachCrashedExternal; RunConfigurationPtr rc = activeRunConfiguration(); if (rc.isNull()) rc = DebuggerRunControlFactory::createDefaultRunConfiguration(); if (RunControl *runControl = m_debuggerRunControlFactory ->create(rc, ProjectExplorer::Constants::DEBUGMODE, sp)) runControl->start(); } void DebuggerPlugin::attachCmdLineCore() { m_manager->showStatusMessage(tr("Attaching to core %1.").arg(m_cmdLineAttachCore)); attachCore(m_cmdLineAttachCore, QString()); } void DebuggerPlugin::attachCore() { AttachCoreDialog dlg(m_manager->mainWindow()); dlg.setExecutableFile( configValue(_("LastExternalExecutableFile")).toString()); dlg.setCoreFile( configValue(_("LastExternalCoreFile")).toString()); if (dlg.exec() != QDialog::Accepted) return; setConfigValue(_("LastExternalExecutableFile"), dlg.executableFile()); setConfigValue(_("LastExternalCoreFile"), dlg.coreFile()); attachCore(dlg.coreFile(), dlg.executableFile()); } void DebuggerPlugin::attachCore(const QString &core, const QString &exe) { const DebuggerStartParametersPtr sp(new DebuggerStartParameters); sp->executable = exe; sp->coreFile = core; sp->startMode = AttachCore; RunConfigurationPtr rc = activeRunConfiguration(); if (rc.isNull()) rc = DebuggerRunControlFactory::createDefaultRunConfiguration(); if (RunControl *runControl = m_debuggerRunControlFactory ->create(rc, ProjectExplorer::Constants::DEBUGMODE, sp)) runControl->start(); } void DebuggerPlugin::startRemoteApplication() { const DebuggerStartParametersPtr sp(new DebuggerStartParameters); StartRemoteDialog dlg(m_manager->mainWindow()); QStringList arches; arches.append(_("i386:x86-64:intel")); arches.append(_("i386")); dlg.setRemoteArchitectures(arches); dlg.setRemoteChannel( configValue(_("LastRemoteChannel")).toString()); dlg.setRemoteArchitecture( configValue(_("LastRemoteArchitecture")).toString()); dlg.setServerStartScript( configValue(_("LastServerStartScript")).toString()); dlg.setUseServerStartScript( configValue(_("LastUseServerStartScript")).toBool()); if (dlg.exec() != QDialog::Accepted) return; setConfigValue(_("LastRemoteChannel"), dlg.remoteChannel()); setConfigValue(_("LastRemoteArchitecture"), dlg.remoteArchitecture()); setConfigValue(_("LastServerStartScript"), dlg.serverStartScript()); setConfigValue(_("LastUseServerStartScript"), dlg.useServerStartScript()); sp->remoteChannel = dlg.remoteChannel(); sp->remoteArchitecture = dlg.remoteArchitecture(); sp->startMode = StartRemote; if (dlg.useServerStartScript()) sp->serverStartScript = dlg.serverStartScript(); RunConfigurationPtr rc = activeRunConfiguration(); if (rc.isNull()) rc = DebuggerRunControlFactory::createDefaultRunConfiguration(); if (RunControl *runControl = m_debuggerRunControlFactory ->create(rc, ProjectExplorer::Constants::DEBUGMODE, sp)) runControl->start(); } #include "debuggerplugin.moc" Q_EXPORT_PLUGIN(DebuggerPlugin)