diff options
Diffstat (limited to 'src/plugins/debugger/debuggermanager.cpp')
-rw-r--r-- | src/plugins/debugger/debuggermanager.cpp | 1298 |
1 files changed, 1298 insertions, 0 deletions
diff --git a/src/plugins/debugger/debuggermanager.cpp b/src/plugins/debugger/debuggermanager.cpp new file mode 100644 index 0000000000..bcf314ae41 --- /dev/null +++ b/src/plugins/debugger/debuggermanager.cpp @@ -0,0 +1,1298 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ + +#include "debuggermanager.h" + +#include "assert.h" +#include "debuggerconstants.h" +#include "idebuggerengine.h" + +#include "breakwindow.h" +#include "disassemblerwindow.h" +#include "debuggeroutputwindow.h" +#include "moduleswindow.h" +#include "registerwindow.h" +#include "stackwindow.h" +#include "threadswindow.h" +#include "watchwindow.h" + +#include "ui_breakbyfunction.h" + +#include "disassemblerhandler.h" +#include "breakhandler.h" +#include "moduleshandler.h" +#include "registerhandler.h" +#include "stackhandler.h" +#include "watchhandler.h" + +#include "startexternaldialog.h" +#include "attachexternaldialog.h" + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QFileInfo> +#include <QtCore/QTime> + +#include <QtGui/QAction> +#include <QtGui/QComboBox> +#include <QtGui/QDockWidget> +#include <QtGui/QErrorMessage> +#include <QtGui/QFileDialog> +#include <QtGui/QLabel> +#include <QtGui/QMainWindow> +#include <QtGui/QMessageBox> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QStatusBar> +#include <QtGui/QTextBlock> +#include <QtGui/QTextCursor> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> +#include <QtGui/QToolTip> + +using namespace Debugger; +using namespace Debugger::Internal; +using namespace Debugger::Constants; + +static const QString tooltipIName = "tooltip"; + +/////////////////////////////////////////////////////////////////////// +// +// BreakByFunctionDialog +// +/////////////////////////////////////////////////////////////////////// + +class BreakByFunctionDialog : public QDialog, Ui::BreakByFunctionDialog +{ + Q_OBJECT + +public: + explicit BreakByFunctionDialog(QWidget *parent) + : QDialog(parent) + { + setupUi(this); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + } + QString functionName() const { return functionLineEdit->text(); } +}; + + +/////////////////////////////////////////////////////////////////////// +// +// DebuggerManager +// +/////////////////////////////////////////////////////////////////////// + +static IDebuggerEngine *gdbEngine = 0; +static IDebuggerEngine *winEngine = 0; +static IDebuggerEngine *scriptEngine = 0; + +extern IDebuggerEngine *createGdbEngine(DebuggerManager *parent); +extern IDebuggerEngine *createWinEngine(DebuggerManager *) { return 0; } +extern IDebuggerEngine *createScriptEngine(DebuggerManager *parent); + +DebuggerManager::DebuggerManager() +{ + init(); +} + +DebuggerManager::~DebuggerManager() +{ + delete gdbEngine; + delete winEngine; + delete scriptEngine; +} + +void DebuggerManager::init() +{ + m_status = -1; + m_busy = false; + + m_attachedPID = 0; + m_startMode = startInternal; + + m_disassemblerHandler = 0; + m_modulesHandler = 0; + m_registerHandler = 0; + + m_breakWindow = new BreakWindow; + m_disassemblerWindow = new DisassemblerWindow; + m_modulesWindow = new ModulesWindow; + m_outputWindow = new DebuggerOutputWindow; + m_registerWindow = new RegisterWindow; + m_stackWindow = new StackWindow; + m_threadsWindow = new ThreadsWindow; + m_localsWindow = new WatchWindow(WatchWindow::LocalsType); + m_watchersWindow = new WatchWindow(WatchWindow::WatchersType); + //m_tooltipWindow = new WatchWindow(WatchWindow::TooltipType); + //m_watchersWindow = new QTreeView; + m_tooltipWindow = new QTreeView; + + m_mainWindow = new QMainWindow; + m_mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North); + m_mainWindow->setDocumentMode(true); + + // Stack + m_stackHandler = new StackHandler; + QAbstractItemView *stackView = + qobject_cast<QAbstractItemView *>(m_stackWindow); + stackView->setModel(m_stackHandler->stackModel()); + connect(stackView, SIGNAL(frameActivated(int)), + this, SLOT(activateFrame(int))); + + // Threads + m_threadsHandler = new ThreadsHandler; + QAbstractItemView *threadsView = + qobject_cast<QAbstractItemView *>(m_threadsWindow); + threadsView->setModel(m_threadsHandler->threadsModel()); + connect(threadsView, SIGNAL(threadSelected(int)), + this, SLOT(selectThread(int))); + + // Disassembler + m_disassemblerHandler = new DisassemblerHandler; + QAbstractItemView *disassemblerView = + qobject_cast<QAbstractItemView *>(m_disassemblerWindow); + disassemblerView->setModel(m_disassemblerHandler->model()); + + // Breakpoints + m_breakHandler = new BreakHandler; + QAbstractItemView *breakView = + qobject_cast<QAbstractItemView *>(m_breakWindow); + breakView->setModel(m_breakHandler->model()); + connect(breakView, SIGNAL(breakPointActivated(int)), + m_breakHandler, SLOT(activateBreakPoint(int))); + connect(breakView, SIGNAL(breakPointDeleted(int)), + m_breakHandler, SLOT(removeBreakpoint(int))); + connect(m_breakHandler, SIGNAL(gotoLocation(QString,int,bool)), + this, SLOT(gotoLocation(QString,int,bool))); + connect(m_breakHandler, SIGNAL(sessionValueRequested(QString,QVariant*)), + this, SIGNAL(sessionValueRequested(QString,QVariant*))); + connect(m_breakHandler, SIGNAL(setSessionValueRequested(QString,QVariant)), + this, SIGNAL(setSessionValueRequested(QString,QVariant))); + + // Modules + QAbstractItemView *modulesView = + qobject_cast<QAbstractItemView *>(m_modulesWindow); + m_modulesHandler = new ModulesHandler; + modulesView->setModel(m_modulesHandler->model()); + connect(modulesView, SIGNAL(reloadModulesRequested()), + this, SLOT(reloadModules())); + connect(modulesView, SIGNAL(loadSymbolsRequested(QString)), + this, SLOT(loadSymbols(QString))); + connect(modulesView, SIGNAL(loadAllSymbolsRequested()), + this, SLOT(loadAllSymbols())); + + + // Registers + QAbstractItemView *registerView = + qobject_cast<QAbstractItemView *>(m_registerWindow); + m_registerHandler = new RegisterHandler; + registerView->setModel(m_registerHandler->model()); + + + m_watchHandler = new WatchHandler; + + // Locals + QTreeView *localsView = qobject_cast<QTreeView *>(m_localsWindow); + localsView->setModel(m_watchHandler->model()); + connect(localsView, SIGNAL(requestExpandChildren(QModelIndex)), + this, SLOT(expandChildren(QModelIndex))); + connect(localsView, SIGNAL(requestCollapseChildren(QModelIndex)), + this, SLOT(collapseChildren(QModelIndex))); + connect(localsView, SIGNAL(requestAssignValue(QString,QString)), + this, SLOT(assignValueInDebugger(QString,QString))); + connect(localsView, SIGNAL(requestWatchExpression(QString)), + this, SLOT(watchExpression(QString))); + + // Watchers + QTreeView *watchersView = qobject_cast<QTreeView *>(m_watchersWindow); + watchersView->setModel(m_watchHandler->model()); + connect(watchersView, SIGNAL(requestAssignValue(QString,QString)), + this, SLOT(assignValueInDebugger(QString,QString))); + connect(watchersView, SIGNAL(requestExpandChildren(QModelIndex)), + this, SLOT(expandChildren(QModelIndex))); + connect(watchersView, SIGNAL(requestCollapseChildren(QModelIndex)), + this, SLOT(collapseChildren(QModelIndex))); + connect(watchersView, SIGNAL(requestWatchExpression(QString)), + this, SLOT(watchExpression(QString))); + connect(watchersView, SIGNAL(requestRemoveWatchExpression(QString)), + this, SLOT(removeWatchExpression(QString))); + + // Tooltip + QTreeView *tooltipView = qobject_cast<QTreeView *>(m_tooltipWindow); + tooltipView->setModel(m_watchHandler->model()); + + connect(m_watchHandler, SIGNAL(watchModelUpdateRequested()), + this, SLOT(updateWatchModel())); + + m_startExternalAction = new QAction(this); + m_startExternalAction->setText(tr("Start and Debug External Application...")); + + m_attachExternalAction = new QAction(this); + m_attachExternalAction->setText(tr("Attach to Running External Application...")); + + m_continueAction = new QAction(this); + m_continueAction->setText(tr("Continue")); + m_continueAction->setIcon(QIcon(":/gdbdebugger/images/debugger_continue_small.png")); + + m_stopAction = new QAction(this); + m_stopAction->setText(tr("Interrupt")); + m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_interrupt_small.png")); + + m_resetAction = new QAction(this); + m_resetAction->setText(tr("Reset Debugger")); + + m_nextAction = new QAction(this); + m_nextAction->setText(tr("Step Over")); + //m_nextAction->setShortcut(QKeySequence(tr("F6"))); + m_nextAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepover_small.png")); + + m_stepAction = new QAction(this); + m_stepAction->setText(tr("Step Into")); + //m_stepAction->setShortcut(QKeySequence(tr("F7"))); + m_stepAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepinto_small.png")); + + m_nextIAction = new QAction(this); + m_nextIAction->setText(tr("Step Over Instruction")); + //m_nextIAction->setShortcut(QKeySequence(tr("Shift+F6"))); + m_nextIAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepoverproc_small.png")); + + m_stepIAction = new QAction(this); + m_stepIAction->setText(tr("Step One Instruction")); + //m_stepIAction->setShortcut(QKeySequence(tr("Shift+F9"))); + m_stepIAction->setIcon(QIcon(":/gdbdebugger/images/debugger_steponeproc_small.png")); + + m_stepOutAction = new QAction(this); + m_stepOutAction->setText(tr("Step Out")); + //m_stepOutAction->setShortcut(QKeySequence(tr("Shift+F7"))); + m_stepOutAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stepout_small.png")); + + m_runToLineAction = new QAction(this); + m_runToLineAction->setText(tr("Run to Line")); + + m_runToFunctionAction = new QAction(this); + m_runToFunctionAction->setText(tr("Run to Outermost Function")); + + m_jumpToLineAction = new QAction(this); + m_jumpToLineAction->setText(tr("Jump to Line")); + + m_breakAction = new QAction(this); + m_breakAction->setText(tr("Toggle Breakpoint")); + + m_breakByFunctionAction = new QAction(this); + m_breakByFunctionAction->setText(tr("Set Breakpoint at Function...")); + + m_breakAtMainAction = new QAction(this); + m_breakAtMainAction->setText(tr("Set Breakpoint at Function 'main'")); + + m_debugDumpersAction = new QAction(this); + m_debugDumpersAction->setText(tr("Debug Custom Dumpers")); + m_debugDumpersAction->setCheckable(true); + + m_skipKnownFramesAction = new QAction(this); + m_skipKnownFramesAction->setText(tr("Skip Known Frames When Stepping")); + m_skipKnownFramesAction->setCheckable(true); + + m_useCustomDumpersAction = new QAction(this); + m_useCustomDumpersAction->setText(tr("Use Custom Display for Qt Objects")); + m_useCustomDumpersAction->setToolTip(tr("Checking this will make the debugger " + "try to use code to format certain data (QObject, QString, ...) nicely. ")); + m_useCustomDumpersAction->setCheckable(true); + m_useCustomDumpersAction->setChecked(true); + + m_useCustomDumpersAction = new QAction(this); + m_useCustomDumpersAction->setText(tr("Use Custom Display for Qt Objects")); + m_useCustomDumpersAction->setToolTip(tr("Checking this will make the debugger " + "try to use code to format certain data (QObject, QString, ...) nicely. ")); + m_useCustomDumpersAction->setCheckable(true); + m_useCustomDumpersAction->setChecked(true); + + m_useFastStartAction = new QAction(this); + m_useFastStartAction->setText(tr("Fast Debugger Start")); + m_useFastStartAction->setToolTip(tr("Checking this will make the debugger " + "start fast by loading only very few debug symbols on start up. This " + "might lead to situations where breakpoints can not be set properly. " + "So uncheck this option if you experience breakpoint related problems.")); + m_useFastStartAction->setCheckable(true); + m_useFastStartAction->setChecked(true); + + // FIXME + m_useFastStartAction->setChecked(false); + m_useFastStartAction->setEnabled(false); + + m_dumpLogAction = new QAction(this); + m_dumpLogAction->setText(tr("Dump Log File for Debugging Purposes")); + + m_watchAction = new QAction(this); + m_watchAction->setText(tr("Add to Watch Window")); + + // For usuage hints oin focus{In,Out} + //connect(m_outputWindow, SIGNAL(statusMessageRequested(QString,int)), + // this, SLOT(showStatusMessage(QString,int))); + + connect(m_continueAction, SIGNAL(triggered()), + this, SLOT(continueExec())); + + connect(m_startExternalAction, SIGNAL(triggered()), + this, SLOT(startExternalApplication())); + connect(m_attachExternalAction, SIGNAL(triggered()), + this, SLOT(attachExternalApplication())); + + connect(m_stopAction, SIGNAL(triggered()), + this, SLOT(interruptDebuggingRequest())); + connect(m_resetAction, SIGNAL(triggered()), + this, SLOT(exitDebugger())); + connect(m_nextAction, SIGNAL(triggered()), + this, SLOT(nextExec())); + connect(m_stepAction, SIGNAL(triggered()), + this, SLOT(stepExec())); + connect(m_nextIAction, SIGNAL(triggered()), + this, SLOT(nextIExec())); + connect(m_stepIAction, SIGNAL(triggered()), + this, SLOT(stepIExec())); + connect(m_stepOutAction, SIGNAL(triggered()), + this, SLOT(stepOutExec())); + connect(m_runToLineAction, SIGNAL(triggered()), + this, SLOT(runToLineExec())); + connect(m_runToFunctionAction, SIGNAL(triggered()), + this, SLOT(runToFunctionExec())); + connect(m_jumpToLineAction, SIGNAL(triggered()), + this, SLOT(jumpToLineExec())); + connect(m_watchAction, SIGNAL(triggered()), + this, SLOT(addToWatchWindow())); + connect(m_breakAction, SIGNAL(triggered()), + this, SLOT(toggleBreakpoint())); + connect(m_breakByFunctionAction, SIGNAL(triggered()), + this, SLOT(breakByFunction())); + connect(m_breakAtMainAction, SIGNAL(triggered()), + this, SLOT(breakAtMain())); + + connect(m_useFastStartAction, SIGNAL(triggered()), + this, SLOT(saveSessionData())); + connect(m_useCustomDumpersAction, SIGNAL(triggered()), + this, SLOT(saveSessionData())); + connect(m_skipKnownFramesAction, SIGNAL(triggered()), + this, SLOT(saveSessionData())); + connect(m_dumpLogAction, SIGNAL(triggered()), + this, SLOT(dumpLog())); + + connect(m_outputWindow, SIGNAL(commandExecutionRequested(QString)), + this, SLOT(executeDebuggerCommand(QString))); + + + m_breakDock = createDockForWidget(m_breakWindow); + + m_disassemblerDock = createDockForWidget(m_disassemblerWindow); + connect(m_disassemblerDock->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(reloadDisassembler()), Qt::QueuedConnection); + + m_modulesDock = createDockForWidget(m_modulesWindow); + connect(m_modulesDock->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(reloadModules()), Qt::QueuedConnection); + + m_registerDock = createDockForWidget(m_registerWindow); + connect(m_registerDock->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(reloadRegisters()), Qt::QueuedConnection); + + m_outputDock = createDockForWidget(m_outputWindow); + + m_stackDock = createDockForWidget(m_stackWindow); + + m_threadsDock = createDockForWidget(m_threadsWindow); + + setStatus(DebuggerProcessNotReady); + gdbEngine = createGdbEngine(this); + winEngine = createWinEngine(this); + scriptEngine = createScriptEngine(this); + setDebuggerType(GdbDebugger); +} + +void DebuggerManager::setDebuggerType(DebuggerType type) +{ + switch (type) { + case GdbDebugger: + m_engine = gdbEngine; + break; + case ScriptDebugger: + m_engine = scriptEngine; + break; + case WinDebugger: + m_engine = winEngine; + break; + } +} + +IDebuggerEngine *DebuggerManager::engine() +{ + return m_engine; +} + +IDebuggerManagerAccessForEngines *DebuggerManager::engineInterface() +{ + return dynamic_cast<IDebuggerManagerAccessForEngines *>(this); +} + +IDebuggerManagerAccessForDebugMode *DebuggerManager::debugModeInterface() +{ + return dynamic_cast<IDebuggerManagerAccessForDebugMode *>(this); +} + +void DebuggerManager::createDockWidgets() +{ + QSplitter *localsAndWatchers = new QSplitter(Qt::Vertical, 0); + localsAndWatchers->setWindowTitle(m_localsWindow->windowTitle()); + localsAndWatchers->addWidget(m_localsWindow); + localsAndWatchers->addWidget(m_watchersWindow); + localsAndWatchers->setStretchFactor(0, 3); + localsAndWatchers->setStretchFactor(1, 1); + m_watchDock = createDockForWidget(localsAndWatchers); +} + +QDockWidget *DebuggerManager::createDockForWidget(QWidget *widget) +{ + QDockWidget *dockWidget = new QDockWidget(widget->windowTitle(), m_mainWindow); + dockWidget->setObjectName(widget->windowTitle()); + //dockWidget->setAllowedAreas(Qt::BottomDockWidgetArea | Qt::RightDockWidgetArea); + dockWidget->setAllowedAreas(Qt::AllDockWidgetAreas); // that space is needed. + //dockWidget->setFeatures(QDockWidget::NoDockWidgetFeatures); + dockWidget->setFeatures(QDockWidget::AllDockWidgetFeatures); + dockWidget->setTitleBarWidget(new QWidget(dockWidget)); + dockWidget->setWidget(widget); + connect(dockWidget->toggleViewAction(), SIGNAL(toggled(bool)), + this, SLOT(dockToggled(bool)), Qt::QueuedConnection); + m_dockWidgets.append(dockWidget); + return dockWidget; +} + +void DebuggerManager::setSimpleDockWidgetArrangement() +{ + foreach (QDockWidget *dockWidget, m_dockWidgets) + m_mainWindow->removeDockWidget(dockWidget); + + foreach (QDockWidget *dockWidget, m_dockWidgets) { + m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget); + dockWidget->show(); + } + + m_mainWindow->tabifyDockWidget(m_watchDock, m_breakDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_disassemblerDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_modulesDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_outputDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_registerDock); + m_mainWindow->tabifyDockWidget(m_watchDock, m_threadsDock); + + // They are rarely used even in ordinary debugging. Hiding them also saves + // cycles since the corresponding information won't be retrieved. + m_registerDock->hide(); + m_disassemblerDock->hide(); + m_modulesDock->hide(); + m_outputDock->hide(); +} + +void DebuggerManager::setLocked(bool locked) +{ + const QDockWidget::DockWidgetFeatures features = + (locked) ? QDockWidget::NoDockWidgetFeatures : + QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable; + + foreach (QDockWidget *dockWidget, m_dockWidgets) { + QWidget *titleBarWidget = dockWidget->titleBarWidget(); + if (locked && !titleBarWidget) + titleBarWidget = new QWidget(dockWidget); + else if (!locked && titleBarWidget) { + delete titleBarWidget; + titleBarWidget = 0; + } + dockWidget->setTitleBarWidget(titleBarWidget); + dockWidget->setFeatures(features); + } +} + +void DebuggerManager::dockToggled(bool on) +{ + QDockWidget *dw = qobject_cast<QDockWidget *>(sender()->parent()); + if (on && dw) + dw->raise(); +} + +QAbstractItemModel *DebuggerManager::threadsModel() +{ + return qobject_cast<ThreadsWindow*>(m_threadsWindow)->model(); +} + +void DebuggerManager::showStatusMessage(const QString &msg, int timeout) +{ + Q_UNUSED(timeout) + //qDebug() << "STATUS: " << msg; + showDebuggerOutput("status:", msg); + mainWindow()->statusBar()->showMessage(msg, timeout); +#if 0 + QString currentTime = QTime::currentTime().toString("hh:mm:ss.zzz"); + + ICore *core = m_pm->getObject<Core::ICore>(); + //qDebug() << qPrintable(currentTime) << "Setting status: " << msg; + if (msg.isEmpty()) + core->messageManager()->displayStatusBarMessage(msg); + else if (timeout == -1) + core->messageManager()->displayStatusBarMessage(tr("Debugger: ") + msg); + else + core->messageManager()->displayStatusBarMessage(tr("Debugger: ") + msg, timeout); +#endif +} + +void DebuggerManager::notifyStartupFinished() +{ + setStatus(DebuggerProcessReady); + showStatusMessage(tr("Startup finished. Debugger ready."), -1); + if (m_startMode == attachExternal) { + // we continue the execution + engine()->continueInferior(); + } else { + engine()->runInferior(); + } +} + +void DebuggerManager::notifyInferiorStopped() +{ + resetLocation(); + setStatus(DebuggerInferiorStopped); + showStatusMessage(tr("Stopped."), 5000); +} + +void DebuggerManager::notifyInferiorUpdateFinished() +{ + setStatus(DebuggerInferiorReady); + showStatusMessage(tr("Stopped."), 5000); +} + +void DebuggerManager::notifyInferiorRunningRequested() +{ + setStatus(DebuggerInferiorRunningRequested); + showStatusMessage(tr("Running..."), 5000); +} + +void DebuggerManager::notifyInferiorRunning() +{ + setStatus(DebuggerInferiorRunning); + showStatusMessage(tr("Running..."), 5000); +} + +void DebuggerManager::notifyInferiorExited() +{ + setStatus(DebuggerProcessReady); + showStatusMessage(tr("Stopped."), 5000); +} + +void DebuggerManager::notifyInferiorPidChanged(int pid) +{ + //QMessageBox::warning(0, "PID", "PID: " + QString::number(pid)); + //qDebug() << "PID: " << pid; + emit inferiorPidChanged(pid); +} + +void DebuggerManager::showApplicationOutput(const QString &prefix, const QString &str) +{ + applicationOutputAvailable(prefix, str); +} + +void DebuggerManager::shutdown() +{ + //qDebug() << "DEBUGGER_MANAGER SHUTDOWN START"; + engine()->shutdown(); + // Delete these manually before deleting the manager + // (who will delete the models for most views) + delete m_breakWindow; + delete m_disassemblerWindow; + delete m_modulesWindow; + delete m_outputWindow; + delete m_registerWindow; + delete m_stackWindow; + delete m_threadsWindow; + delete m_tooltipWindow; + delete m_watchersWindow; + delete m_localsWindow; + // These widgets are all in some layout which will take care of deletion. + m_breakWindow = 0; + m_disassemblerWindow = 0; + m_modulesWindow = 0; + m_outputWindow = 0; + m_registerWindow = 0; + m_stackWindow = 0; + m_threadsWindow = 0; + m_tooltipWindow = 0; + m_watchersWindow = 0; + m_localsWindow = 0; + + delete m_breakHandler; + delete m_disassemblerHandler; + delete m_modulesHandler; + delete m_registerHandler; + delete m_stackHandler; + delete m_watchHandler; + m_breakHandler = 0; + m_disassemblerHandler = 0; + m_modulesHandler = 0; + m_registerHandler = 0; + m_stackHandler = 0; + m_watchHandler = 0; + //qDebug() << "DEBUGGER_MANAGER SHUTDOWN END"; +} + +void DebuggerManager::toggleBreakpoint() +{ + QString fileName; + int lineNumber = -1; + queryCurrentTextEditor(&fileName, &lineNumber, 0); + if (lineNumber == -1) + return; + toggleBreakpoint(fileName, lineNumber); +} + +void DebuggerManager::toggleBreakpoint(const QString &fileName, int lineNumber) +{ + int index = m_breakHandler->indexOf(fileName, lineNumber); + if (index == -1) + breakHandler()->setBreakpoint(fileName, lineNumber); + else + breakHandler()->removeBreakpoint(index); + engine()->attemptBreakpointSynchronization(); +} + +void DebuggerManager::setToolTipExpression(const QPoint &pos, const QString &exp) +{ + engine()->setToolTipExpression(pos, exp); +} + +void DebuggerManager::updateWatchModel() +{ + engine()->updateWatchModel(); +} + +void DebuggerManager::expandChildren(const QModelIndex &idx) +{ + watchHandler()->expandChildren(idx); +} + +void DebuggerManager::collapseChildren(const QModelIndex &idx) +{ + watchHandler()->collapseChildren(idx); +} + +void DebuggerManager::removeWatchExpression(const QString &iname) +{ + watchHandler()->removeWatchExpression(iname); +} + +QVariant DebuggerManager::sessionValue(const QString &name) +{ + QVariant value; + emit sessionValueRequested(name, &value); + return value; +} + +void DebuggerManager::querySessionValue(const QString &name, QVariant *value) +{ + emit sessionValueRequested(name, value); +} + +void DebuggerManager::setSessionValue(const QString &name, const QVariant &value) +{ + emit setSessionValueRequested(name, value); +} + +QVariant DebuggerManager::configValue(const QString &name) +{ + QVariant value; + emit configValueRequested(name, &value); + return value; +} + +void DebuggerManager::queryConfigValue(const QString &name, QVariant *value) +{ + emit configValueRequested(name, value); +} + +void DebuggerManager::setConfigValue(const QString &name, const QVariant &value) +{ + emit setConfigValueRequested(name, value); +} + +void DebuggerManager::startExternalApplication() +{ + if (!startNewDebugger(startExternal)) + emit debuggingFinished(); +} + +void DebuggerManager::attachExternalApplication() +{ + if (!startNewDebugger(attachExternal)) + emit debuggingFinished(); +} + +bool DebuggerManager::startNewDebugger(StartMode mode) +{ + m_startMode = mode; + // FIXME: Clean up + + if (startMode() == startExternal) { + StartExternalDialog dlg(mainWindow()); + dlg.setExecutableFile( + configValue(QLatin1String("LastExternalExecutableFile")).toString()); + dlg.setExecutableArguments( + configValue(QLatin1String("LastExternalExecutableArguments")).toString()); + if (dlg.exec() != QDialog::Accepted) + return false; + setConfigValue(QLatin1String("LastExternalExecutableFile"), + dlg.executableFile()); + setConfigValue(QLatin1String("LastExternalExecutableArguments"), + dlg.executableArguments()); + m_executable = dlg.executableFile(); + m_processArgs = dlg.executableArguments().split(' '); + m_workingDir = QString(); + m_attachedPID = -1; + } else if (startMode() == attachExternal) { + QString pid; + AttachExternalDialog dlg(mainWindow(), pid); + if (dlg.exec() != QDialog::Accepted) + return false; + m_executable = QString(); + m_processArgs = QStringList(); + m_workingDir = QString(); + m_attachedPID = dlg.attachPID(); + } else if (startMode() == startInternal) { + if (m_executable.isEmpty()) { + QString startDirectory = m_executable; + if (m_executable.isEmpty()) { + QString fileName; + emit currentTextEditorRequested(&fileName, 0, 0); + if (!fileName.isEmpty()) { + const QFileInfo editorFile(fileName); + startDirectory = editorFile.dir().absolutePath(); + } + } + StartExternalDialog dlg(mainWindow()); + dlg.setExecutableFile(startDirectory); + if (dlg.exec() != QDialog::Accepted) + return false; + m_executable = dlg.executableFile(); + m_processArgs = dlg.executableArguments().split(' '); + m_workingDir = QString(); + m_attachedPID = 0; + } else { + //m_executable = QDir::convertSeparators(m_executable); + //m_processArgs = sd.processArgs.join(QLatin1String(" ")); + m_attachedPID = 0; + } + } + + emit debugModeRequested(); + + if (m_executable.endsWith(".js")) + setDebuggerType(ScriptDebugger); + else + setDebuggerType(GdbDebugger); + + if (!engine()->startDebugger()) + return false; + + m_busy = false; + setStatus(DebuggerProcessStartingUp); + return true; +} + +void DebuggerManager::cleanupViews() +{ + resetLocation(); + breakHandler()->setAllPending(); + stackHandler()->removeAll(); + threadsHandler()->removeAll(); + disassemblerHandler()->removeAll(); + modulesHandler()->removeAll(); + watchHandler()->cleanup(); +} + +void DebuggerManager::exitDebugger() +{ + engine()->exitDebugger(); + cleanupViews(); + setStatus(DebuggerProcessNotReady); + setBusyCursor(false); + emit debuggingFinished(); +} + +void DebuggerManager::assignValueInDebugger(const QString &expr, const QString &value) +{ + engine()->assignValueInDebugger(expr, value); +} + +void DebuggerManager::activateFrame(int index) +{ + engine()->activateFrame(index); +} + +void DebuggerManager::selectThread(int index) +{ + engine()->selectThread(index); +} + +void DebuggerManager::loadAllSymbols() +{ + engine()->loadAllSymbols(); +} + +void DebuggerManager::loadSymbols(const QString &module) +{ + engine()->loadSymbols(module); +} + +void DebuggerManager::stepExec() +{ + resetLocation(); + engine()->stepExec(); +} + +void DebuggerManager::stepOutExec() +{ + resetLocation(); + engine()->stepOutExec(); +} + +void DebuggerManager::nextExec() +{ + resetLocation(); + engine()->nextExec(); +} + +void DebuggerManager::stepIExec() +{ + resetLocation(); + engine()->stepIExec(); +} + +void DebuggerManager::nextIExec() +{ + resetLocation(); + engine()->nextIExec(); +} + +void DebuggerManager::executeDebuggerCommand(const QString &command) +{ + engine()->executeDebuggerCommand(command); +} + +void DebuggerManager::sessionLoaded() +{ + exitDebugger(); + loadSessionData(); +} + +void DebuggerManager::aboutToSaveSession() +{ + saveSessionData(); +} + +void DebuggerManager::loadSessionData() +{ + m_breakHandler->loadSessionData(); + + QVariant value; + querySessionValue(QLatin1String("UseFastStart"), &value); + m_useFastStartAction->setChecked(value.toBool()); + querySessionValue(QLatin1String("UseCustomDumpers"), &value); + m_useCustomDumpersAction->setChecked(!value.isValid() || value.toBool()); + querySessionValue(QLatin1String("SkipKnownFrames"), &value); + m_skipKnownFramesAction->setChecked(value.toBool()); + engine()->loadSessionData(); +} + +void DebuggerManager::saveSessionData() +{ + m_breakHandler->saveSessionData(); + + setSessionValue(QLatin1String("UseFastStart"), + m_useFastStartAction->isChecked()); + setSessionValue(QLatin1String("UseCustomDumpers"), + m_useCustomDumpersAction->isChecked()); + setSessionValue(QLatin1String("SkipKnownFrames"), + m_skipKnownFramesAction->isChecked()); + engine()->saveSessionData(); +} + +void DebuggerManager::dumpLog() +{ + QString fileName = QFileDialog::getSaveFileName(mainWindow(), + tr("Save Debugger Log"), QDir::tempPath()); + if (fileName.isEmpty()) + return; + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly)) + return; + QTextStream ts(&file); + ts << m_outputWindow->inputContents(); + ts << "\n\n=======================================\n\n"; + ts << m_outputWindow->combinedContents(); +} + +#if 0 +// call after m_gdbProc exited. +void GdbEngine::procFinished() +{ + //qDebug() << "GDB PROCESS FINISHED"; + setStatus(DebuggerProcessNotReady); + showStatusMessage(tr("Done"), 5000); + q->m_breakHandler->procFinished(); + q->m_watchHandler->cleanup(); + m_stackHandler->m_stackFrames.clear(); + m_stackHandler->resetModel(); + m_threadsHandler->resetModel(); + if (q->m_modulesHandler) + q->m_modulesHandler->procFinished(); + q->resetLocation(); + setStatus(DebuggerProcessNotReady); + emit q->previousModeRequested(); + emit q->debuggingFinished(); + //exitDebugger(); + //showStatusMessage("Gdb killed"); + m_shortToFullName.clear(); + m_fullToShortName.clear(); + m_shared = 0; + q->m_busy = false; +} +#endif + +void DebuggerManager::addToWatchWindow() +{ + // requires a selection, but that's the only case we want... + QObject *ob = 0; + queryCurrentTextEditor(0, 0, &ob); + QPlainTextEdit *editor = qobject_cast<QPlainTextEdit*>(ob); + if (!editor) + return; + QTextCursor tc = editor->textCursor(); + watchExpression(tc.selectedText()); +} + +void DebuggerManager::watchExpression(const QString &expression) +{ + watchHandler()->watchExpression(expression); + //engine()->updateWatchModel(); +} + +void DebuggerManager::setBreakpoint(const QString &fileName, int lineNumber) +{ + breakHandler()->setBreakpoint(fileName, lineNumber); + engine()->attemptBreakpointSynchronization(); +} + +void DebuggerManager::breakByFunction(const QString &functionName) +{ + breakHandler()->breakByFunction(functionName); + engine()->attemptBreakpointSynchronization(); +} + +void DebuggerManager::breakByFunction() +{ + BreakByFunctionDialog dlg(m_mainWindow); + if (dlg.exec()) + breakByFunction(dlg.functionName()); +} + +void DebuggerManager::breakAtMain() +{ +#ifdef Q_OS_WIN + breakByFunction("qMain"); +#else + breakByFunction("main"); +#endif +} + +void DebuggerManager::setStatus(int status) +{ + //qDebug() << "STATUS CHANGE: from" << m_status << "to" << status; + + if (status == m_status) + return; + + m_status = status; + + const bool started = status == DebuggerInferiorRunning + || status == DebuggerInferiorRunningRequested + || status == DebuggerInferiorStopRequested + || status == DebuggerInferiorStopped + || status == DebuggerInferiorUpdating + || status == DebuggerInferiorUpdateFinishing + || status == DebuggerInferiorReady; + + const bool starting = status == DebuggerProcessStartingUp; + const bool running = status == DebuggerInferiorRunning; + const bool ready = status == DebuggerInferiorStopped + || status == DebuggerInferiorReady + || status == DebuggerProcessReady; + + m_startExternalAction->setEnabled(!started && !starting); + m_attachExternalAction->setEnabled(!started && !starting); + m_watchAction->setEnabled(ready); + m_breakAction->setEnabled(true); + + bool interruptIsExit = !running; + if (interruptIsExit) { + m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_stop_small.png")); + m_stopAction->setText(tr("Stop Debugger")); + } else { + m_stopAction->setIcon(QIcon(":/gdbdebugger/images/debugger_interrupt_small.png")); + m_stopAction->setText(tr("Interrupt")); + } + + m_stopAction->setEnabled(started); + m_resetAction->setEnabled(true); + + m_stepAction->setEnabled(ready); + m_stepOutAction->setEnabled(ready); + m_runToLineAction->setEnabled(ready); + m_runToFunctionAction->setEnabled(ready); + m_jumpToLineAction->setEnabled(ready); + m_nextAction->setEnabled(ready); + m_stepIAction->setEnabled(ready); + m_nextIAction->setEnabled(ready); + //showStatusMessage(QString("started: %1, running: %2").arg(started).arg(running)); + emit statusChanged(m_status); + const bool notbusy = ready || status == DebuggerProcessNotReady; + setBusyCursor(!notbusy); +} + +void DebuggerManager::setBusyCursor(bool busy) +{ + if (busy == m_busy) + return; + //qDebug() << "BUSY: " << busy; + m_busy = busy; + + QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor); + m_breakWindow->setCursor(cursor); + m_disassemblerWindow->setCursor(cursor); + m_localsWindow->setCursor(cursor); + m_modulesWindow->setCursor(cursor); + m_outputWindow->setCursor(cursor); + m_registerWindow->setCursor(cursor); + m_stackWindow->setCursor(cursor); + m_threadsWindow->setCursor(cursor); + m_tooltipWindow->setCursor(cursor); + m_watchersWindow->setCursor(cursor); +} + +bool DebuggerManager::skipKnownFrames() const +{ + return m_skipKnownFramesAction->isChecked(); +} + +bool DebuggerManager::debugDumpers() const +{ + return m_debugDumpersAction->isChecked(); +} + +bool DebuggerManager::useCustomDumpers() const +{ + return m_useCustomDumpersAction->isChecked(); +} + +bool DebuggerManager::useFastStart() const +{ + return 0; // && m_useFastStartAction->isChecked(); +} + +void DebuggerManager::queryCurrentTextEditor(QString *fileName, int *lineNumber, + QObject **object) +{ + emit currentTextEditorRequested(fileName, lineNumber, object); +} + +void DebuggerManager::continueExec() +{ + engine()->continueInferior(); +} + +void DebuggerManager::interruptDebuggingRequest() +{ + //qDebug() << "INTERRUPTING AT" << status(); + bool interruptIsExit = (status() != DebuggerInferiorRunning); + if (interruptIsExit) + exitDebugger(); + else { + setStatus(DebuggerInferiorStopRequested); + engine()->interruptInferior(); + } +} + + +void DebuggerManager::runToLineExec() +{ + QString fileName; + int lineNumber = -1; + emit currentTextEditorRequested(&fileName, &lineNumber, 0); + if (!fileName.isEmpty()) + engine()->runToLineExec(fileName, lineNumber); +} + +void DebuggerManager::runToFunctionExec() +{ + QString fileName; + int lineNumber = -1; + QObject *object = 0; + emit currentTextEditorRequested(&fileName, &lineNumber, &object); + QPlainTextEdit *ed = qobject_cast<QPlainTextEdit*>(object); + if (!ed) + return; + QTextCursor cursor = ed->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; + } + } + } + //qDebug() << "RUN TO FUNCTION " << functionName; + if (!functionName.isEmpty()) + engine()->runToFunctionExec(functionName); +} + +void DebuggerManager::jumpToLineExec() +{ + QString fileName; + int lineNumber = -1; + emit currentTextEditorRequested(&fileName, &lineNumber, 0); + if (!fileName.isEmpty()) + engine()->jumpToLineExec(fileName, lineNumber); +} + +void DebuggerManager::resetLocation() +{ + //m_watchHandler->removeMouseMoveCatcher(editor->widget()); + emit resetLocationRequested(); +} + +void DebuggerManager::gotoLocation(const QString &fileName, int line, + bool setMarker) +{ + emit gotoLocationRequested(fileName, line, setMarker); + //m_watchHandler->installMouseMoveCatcher(editor->widget()); +} + + +////////////////////////////////////////////////////////////////////// +// +// Disassembler specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::reloadDisassembler() +{ + if (!m_disassemblerDock || !m_disassemblerDock->isVisible()) + return; + engine()->reloadDisassembler(); +} + +void DebuggerManager::disassemblerDockToggled(bool on) +{ + if (on) + reloadDisassembler(); +} + + +////////////////////////////////////////////////////////////////////// +// +// Modules specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::reloadModules() +{ + if (!m_modulesDock || !m_modulesDock->isVisible()) + return; + engine()->reloadModules(); +} + +void DebuggerManager::modulesDockToggled(bool on) +{ + if (on) + reloadModules(); +} + + +////////////////////////////////////////////////////////////////////// +// +// Output specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::showDebuggerOutput(const QString &prefix, const QString &msg) +{ + m_outputWindow->showOutput(prefix, msg); +} + +void DebuggerManager::showDebuggerInput(const QString &prefix, const QString &msg) +{ + m_outputWindow->showInput(prefix, msg); +} + + +////////////////////////////////////////////////////////////////////// +// +// Register specific stuff +// +////////////////////////////////////////////////////////////////////// + +void DebuggerManager::registerDockToggled(bool on) +{ + if (on) + reloadRegisters(); +} + +void DebuggerManager::reloadRegisters() +{ + if (!m_registerDock || !m_registerDock->isVisible()) + return; + engine()->reloadRegisters(); +} + + +#include "debuggermanager.moc" |