Files
qt-creator/src/plugins/debugger/debuggermanager.cpp

1736 lines
53 KiB
C++

/**************************************************************************
**
** 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 "debuggermanager.h"
#include "debuggeractions.h"
#include "debuggeragents.h"
#include "debuggerrunner.h"
#include "debuggerconstants.h"
#include "idebuggerengine.h"
#include "debuggerstringutils.h"
#include "breakwindow.h"
#include "debuggeroutputwindow.h"
#include "moduleswindow.h"
#include "registerwindow.h"
#include "stackwindow.h"
#include "sourcefileswindow.h"
#include "threadswindow.h"
#include "watchwindow.h"
#include "breakhandler.h"
#include "moduleshandler.h"
#include "registerhandler.h"
#include "stackhandler.h"
#include "stackframe.h"
#include "watchhandler.h"
#include "debuggerdialogs.h"
#ifdef Q_OS_WIN
# include "shared/peutils.h"
#endif
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <utils/fancymainwindow.h>
#include <projectexplorer/toolchain.h>
#include <QtCore/QDebug>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QTextStream>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtGui/QApplication>
#include <QtGui/QAction>
#include <QtGui/QComboBox>
#include <QtGui/QDockWidget>
#include <QtGui/QErrorMessage>
#include <QtGui/QFileDialog>
#include <QtGui/QLabel>
#include <QtGui/QMessageBox>
#include <QtGui/QPlainTextEdit>
#include <QtGui/QPushButton>
#include <QtGui/QStatusBar>
#include <QtGui/QTextBlock>
#include <QtGui/QTextCursor>
#include <QtGui/QToolButton>
#include <QtGui/QToolTip>
#define DEBUG_STATE 1
#ifdef DEBUG_STATE
// use Q_FUNC_INFO?
# define STATE_DEBUG(s) \
{ QString msg; QTextStream ts(&msg); ts << s; \
showDebuggerOutput(LogDebug, msg); }
#else
# define STATE_DEBUG(s)
#endif
// Note: the Debugger process itself and any helper processes like
// gdbserver, the trk client etc are referred to as 'Adapter',
// whereas the debugged process is referred to as 'Inferior'.
//
// 0 == DebuggerNotReady
// |
// EngineStarting
// |
// AdapterStarting --> AdapterStartFailed --> 0
// |
// AdapterStarted
// |
// InferiorPreparing --> InferiorPreparationFailed --> 0
// |
// InferiorPrepared
// |
// InferiorStarting --> InferiorStartFailed --> 0
// |
// (core) | (attach) (remote)
// .-----------------<-|->--------------------.
// | v |
// InferiorUnrunnable | |
// | | v
// | | (plain)
// | | (trk)
// | |
// | | .------------------------------------.
// | | v |
// | InferiorRunningRequested v |
// | | | |
// | .---- InferiorRunning | |
// | | | | |
// | | InferiorStopping | |
// | | | | |
// | v v | |
// | |<--- InferiorStopped <-----------' |
// | | | |
// | | `---------------------------------------'
// | |
// | '---> InferiorShuttingDown -> InferiorShutdownFailed
// | |
// | InferiorShutDown
// | |
// | v
// '------------> AdapterShuttingDown -> AdapterShutdownFailed --> 0
// |
// 0
//
// Allowed actions:
// [R] : Run
// [C] : Continue
// [N] : Step, Next
namespace Debugger {
namespace Internal {
IDebuggerEngine *createGdbEngine(DebuggerManager *parent);
IDebuggerEngine *createScriptEngine(DebuggerManager *parent);
IDebuggerEngine *createTcfEngine(DebuggerManager *parent);
// The createWinEngine function takes a list of options pages it can add to.
// This allows for having a "enabled" toggle on the page independently
// of the engine. That's good for not enabling the related ActiveX control
// unnecessarily.
IDebuggerEngine *createWinEngine(DebuggerManager *, bool /* cmdLineEnabled */, QList<Core::IOptionsPage*> *)
#ifdef CDB_ENABLED
;
#else
{ return 0; }
#endif
} // namespace Internal
DEBUGGER_EXPORT QDebug operator<<(QDebug str, const DebuggerStartParameters &p)
{
QDebug nospace = str.nospace();
const QString sep = QString(QLatin1Char(','));
nospace << "executable=" << p.executable << " coreFile=" << p.coreFile
<< " processArgs=" << p.processArgs.join(sep)
<< " environment=<" << p.environment.size() << " variables>"
<< " workingDir=" << p.workingDir << " buildDir=" << p.buildDir
<< " attachPID=" << p.attachPID << " useTerminal=" << p.useTerminal
<< " remoteChannel=" << p.remoteChannel
<< " remoteArchitecture=" << p.remoteArchitecture
<< " symbolFileName=" << p.symbolFileName
<< " serverStartScript=" << p.serverStartScript
<< " toolchain=" << p.toolChainType << '\n';
return str;
}
using namespace Constants;
using namespace Debugger::Internal;
static const QString tooltipIName = "tooltip";
static const char *stateName(int s)
{
#define SN(x) case x: return #x;
switch (s) {
SN(DebuggerNotReady)
SN(EngineStarting)
SN(AdapterStarting)
SN(AdapterStarted)
SN(AdapterStartFailed)
SN(InferiorPreparing)
SN(InferiorPrepared)
SN(InferiorPreparationFailed)
SN(InferiorStarting)
SN(InferiorStartFailed)
SN(InferiorRunningRequested)
SN(InferiorRunning)
SN(InferiorUnrunnable)
SN(InferiorStopping)
SN(InferiorStopped)
SN(InferiorStopFailed)
SN(InferiorShuttingDown)
SN(InferiorShutDown)
SN(InferiorShutdownFailed)
SN(AdapterShuttingDown)
SN(AdapterShutdownFailed)
}
return "<unknown>";
#undef SN
}
///////////////////////////////////////////////////////////////////////
//
// DebuggerStartParameters
//
///////////////////////////////////////////////////////////////////////
DebuggerStartParameters::DebuggerStartParameters()
: attachPID(-1),
useTerminal(false),
toolChainType(ProjectExplorer::ToolChain::UNKNOWN),
startMode(NoStartMode)
{}
void DebuggerStartParameters::clear()
{
executable.clear();
coreFile.clear();
processArgs.clear();
environment.clear();
workingDir.clear();
buildDir.clear();
attachPID = -1;
useTerminal = false;
crashParameter.clear();
remoteChannel.clear();
remoteArchitecture.clear();
serverStartScript.clear();
toolChainType = ProjectExplorer::ToolChain::UNKNOWN;
startMode = NoStartMode;
}
///////////////////////////////////////////////////////////////////////
//
// DebuggerManager
//
///////////////////////////////////////////////////////////////////////
static Debugger::Internal::IDebuggerEngine *gdbEngine = 0;
static Debugger::Internal::IDebuggerEngine *scriptEngine = 0;
static Debugger::Internal::IDebuggerEngine *tcfEngine = 0;
static Debugger::Internal::IDebuggerEngine *winEngine = 0;
struct DebuggerManagerPrivate
{
DebuggerManagerPrivate(DebuggerManager *manager);
static DebuggerManager *instance;
// FIXME: Remove engine-specific state
DebuggerStartParametersPtr m_startParameters;
qint64 m_inferiorPid;
/// Views
Core::Utils::FancyMainWindow *m_mainWindow;
QLabel *m_statusLabel;
QDockWidget *m_breakDock;
QDockWidget *m_modulesDock;
QDockWidget *m_outputDock;
QDockWidget *m_registerDock;
QDockWidget *m_stackDock;
QDockWidget *m_sourceFilesDock;
QDockWidget *m_threadsDock;
QDockWidget *m_watchDock;
BreakHandler *m_breakHandler;
ModulesHandler *m_modulesHandler;
RegisterHandler *m_registerHandler;
StackHandler *m_stackHandler;
ThreadsHandler *m_threadsHandler;
WatchHandler *m_watchHandler;
SourceFilesWindow *m_sourceFilesWindow;
DebuggerManagerActions m_actions;
QWidget *m_breakWindow;
QWidget *m_localsWindow;
QWidget *m_registerWindow;
QWidget *m_modulesWindow;
QWidget *m_stackWindow;
QWidget *m_threadsWindow;
QWidget *m_watchersWindow;
DebuggerOutputWindow *m_outputWindow;
bool m_busy;
QTimer *m_statusTimer;
QString m_lastPermanentStatusMessage;
DisassemblerViewAgent m_disassemblerViewAgent;
IDebuggerEngine *m_engine;
DebuggerState m_state;
};
DebuggerManager *DebuggerManagerPrivate::instance = 0;
DebuggerManagerPrivate::DebuggerManagerPrivate(DebuggerManager *manager)
: m_startParameters(new DebuggerStartParameters),
m_disassemblerViewAgent(manager)
{
m_inferiorPid = 0;
}
DebuggerManager::DebuggerManager()
: d(new DebuggerManagerPrivate(this))
{
DebuggerManagerPrivate::instance = this;
init();
}
DebuggerManager::~DebuggerManager()
{
#define doDelete(ptr) delete ptr; ptr = 0
doDelete(gdbEngine);
doDelete(scriptEngine);
doDelete(tcfEngine);
doDelete(winEngine);
#undef doDelete
DebuggerManagerPrivate::instance = 0;
delete d;
}
DebuggerManager *DebuggerManager::instance()
{
return DebuggerManagerPrivate::instance;
}
void DebuggerManager::init()
{
d->m_state = DebuggerState(-1);
d->m_busy = false;
d->m_modulesHandler = 0;
d->m_registerHandler = 0;
d->m_statusLabel = new QLabel;
d->m_statusLabel->setMinimumSize(QSize(30, 10));
d->m_breakWindow = new BreakWindow;
d->m_modulesWindow = new ModulesWindow(this);
d->m_outputWindow = new DebuggerOutputWindow;
d->m_registerWindow = new RegisterWindow(this);
d->m_stackWindow = new StackWindow(this);
d->m_sourceFilesWindow = new SourceFilesWindow;
d->m_threadsWindow = new ThreadsWindow;
d->m_localsWindow = new WatchWindow(WatchWindow::LocalsType, this);
d->m_watchersWindow = new WatchWindow(WatchWindow::WatchersType, this);
d->m_statusTimer = new QTimer(this);
d->m_mainWindow = new Core::Utils::FancyMainWindow;
d->m_mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::North);
d->m_mainWindow->setDocumentMode(true);
// Stack
d->m_stackHandler = new StackHandler;
QAbstractItemView *stackView =
qobject_cast<QAbstractItemView *>(d->m_stackWindow);
stackView->setModel(d->m_stackHandler->stackModel());
connect(stackView, SIGNAL(frameActivated(int)),
this, SLOT(activateFrame(int)));
connect(theDebuggerAction(ExpandStack), SIGNAL(triggered()),
this, SLOT(reloadFullStack()));
connect(theDebuggerAction(MaximalStackDepth), SIGNAL(triggered()),
this, SLOT(reloadFullStack()));
// Threads
d->m_threadsHandler = new ThreadsHandler;
QAbstractItemView *threadsView =
qobject_cast<QAbstractItemView *>(d->m_threadsWindow);
threadsView->setModel(d->m_threadsHandler->threadsModel());
connect(threadsView, SIGNAL(threadSelected(int)),
this, SLOT(selectThread(int)));
// Breakpoints
d->m_breakHandler = new BreakHandler(this);
QAbstractItemView *breakView =
qobject_cast<QAbstractItemView *>(d->m_breakWindow);
breakView->setModel(d->m_breakHandler->model());
connect(breakView, SIGNAL(breakpointActivated(int)),
d->m_breakHandler, SLOT(activateBreakpoint(int)));
connect(breakView, SIGNAL(breakpointDeleted(int)),
d->m_breakHandler, SLOT(removeBreakpoint(int)));
connect(breakView, SIGNAL(breakpointSynchronizationRequested()),
this, SLOT(attemptBreakpointSynchronization()));
connect(breakView, SIGNAL(breakByFunctionRequested(QString)),
this, SLOT(breakByFunction(QString)), Qt::QueuedConnection);
connect(breakView, SIGNAL(breakByFunctionMainRequested()),
this, SLOT(breakByFunctionMain()), Qt::QueuedConnection);
// Modules
QAbstractItemView *modulesView =
qobject_cast<QAbstractItemView *>(d->m_modulesWindow);
d->m_modulesHandler = new ModulesHandler;
modulesView->setModel(d->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()));
connect(modulesView, SIGNAL(fileOpenRequested(QString)),
this, SLOT(fileOpen(QString)));
connect(modulesView, SIGNAL(newDockRequested(QWidget*)),
this, SLOT(createNewDock(QWidget*)));
// Source Files
//d->m_sourceFilesHandler = new SourceFilesHandler;
QAbstractItemView *sourceFilesView =
qobject_cast<QAbstractItemView *>(d->m_sourceFilesWindow);
//sourceFileView->setModel(d->m_stackHandler->stackModel());
connect(sourceFilesView, SIGNAL(reloadSourceFilesRequested()),
this, SLOT(reloadSourceFiles()));
connect(sourceFilesView, SIGNAL(fileOpenRequested(QString)),
this, SLOT(fileOpen(QString)));
// Registers
QAbstractItemView *registerView =
qobject_cast<QAbstractItemView *>(d->m_registerWindow);
d->m_registerHandler = new RegisterHandler;
registerView->setModel(d->m_registerHandler->model());
// Locals
d->m_watchHandler = new WatchHandler(this);
QTreeView *localsView = qobject_cast<QTreeView *>(d->m_localsWindow);
localsView->setModel(d->m_watchHandler->model(LocalsWatch));
// Watchers
QTreeView *watchersView = qobject_cast<QTreeView *>(d->m_watchersWindow);
watchersView->setModel(d->m_watchHandler->model(WatchersWatch));
connect(theDebuggerAction(AssignValue), SIGNAL(triggered()),
this, SLOT(assignValueInDebugger()), Qt::QueuedConnection);
// Tooltip
//QTreeView *tooltipView = qobject_cast<QTreeView *>(d->m_tooltipWindow);
//tooltipView->setModel(d->m_watchHandler->model(TooltipsWatch));
qRegisterMetaType<WatchData>("WatchData");
d->m_actions.continueAction = new QAction(tr("Continue"), this);
d->m_actions.continueAction->setIcon(QIcon(":/debugger/images/debugger_continue_small.png"));
d->m_actions.stopAction = new QAction(tr("Interrupt"), this);
d->m_actions.stopAction->setIcon(QIcon(":/debugger/images/debugger_interrupt_small.png"));
d->m_actions.resetAction = new QAction(tr("Reset Debugger"), this);
d->m_actions.nextAction = new QAction(tr("Step Over"), this);
d->m_actions.nextAction->setIcon(QIcon(":/debugger/images/debugger_stepover_small.png"));
d->m_actions.stepAction = new QAction(tr("Step Into"), this);
d->m_actions.stepAction->setIcon(QIcon(":/debugger/images/debugger_stepinto_small.png"));
d->m_actions.stepOutAction = new QAction(tr("Step Out"), this);
d->m_actions.stepOutAction->setIcon(QIcon(":/debugger/images/debugger_stepout_small.png"));
d->m_actions.runToLineAction = new QAction(tr("Run to Line"), this);
d->m_actions.runToFunctionAction = new QAction(tr("Run to Outermost Function"), this);
d->m_actions.jumpToLineAction = new QAction(tr("Jump to Line"), this);
d->m_actions.breakAction = new QAction(tr("Toggle Breakpoint"), this);
d->m_actions.watchAction = new QAction(tr("Add to Watch Window"), this);
d->m_actions.reverseDirectionAction = new QAction(tr("Reverse Direction"), this);
d->m_actions.reverseDirectionAction->setCheckable(true);
d->m_actions.reverseDirectionAction->setChecked(false);
connect(d->m_actions.continueAction, SIGNAL(triggered()),
this, SLOT(continueExec()));
connect(d->m_actions.stopAction, SIGNAL(triggered()),
this, SLOT(interruptDebuggingRequest()));
connect(d->m_actions.resetAction, SIGNAL(triggered()),
this, SLOT(exitDebugger()));
connect(d->m_actions.nextAction, SIGNAL(triggered()),
this, SLOT(nextExec()));
connect(d->m_actions.stepAction, SIGNAL(triggered()),
this, SLOT(stepExec()));
connect(d->m_actions.stepOutAction, SIGNAL(triggered()),
this, SLOT(stepOutExec()));
connect(d->m_actions.runToLineAction, SIGNAL(triggered()),
this, SLOT(runToLineExec()));
connect(d->m_actions.runToFunctionAction, SIGNAL(triggered()),
this, SLOT(runToFunctionExec()));
connect(d->m_actions.jumpToLineAction, SIGNAL(triggered()),
this, SLOT(jumpToLineExec()));
connect(d->m_actions.watchAction, SIGNAL(triggered()),
this, SLOT(addToWatchWindow()));
connect(d->m_actions.breakAction, SIGNAL(triggered()),
this, SLOT(toggleBreakpoint()));
connect(d->m_statusTimer, SIGNAL(timeout()),
this, SLOT(clearStatusMessage()));
connect(theDebuggerAction(ExecuteCommand), SIGNAL(triggered()),
this, SLOT(executeDebuggerCommand()));
connect(theDebuggerAction(WatchPoint), SIGNAL(triggered()),
this, SLOT(watchPoint()));
connect(theDebuggerAction(OperateByInstruction), SIGNAL(triggered()),
this, SLOT(operateByInstructionTriggered()));
d->m_breakDock = d->m_mainWindow->addDockForWidget(d->m_breakWindow);
d->m_modulesDock = d->m_mainWindow->addDockForWidget(d->m_modulesWindow);
connect(d->m_modulesDock->toggleViewAction(), SIGNAL(toggled(bool)),
this, SLOT(reloadModules()), Qt::QueuedConnection);
d->m_registerDock = d->m_mainWindow->addDockForWidget(d->m_registerWindow);
connect(d->m_registerDock->toggleViewAction(), SIGNAL(toggled(bool)),
this, SLOT(reloadRegisters()), Qt::QueuedConnection);
d->m_outputDock = d->m_mainWindow->addDockForWidget(d->m_outputWindow);
d->m_stackDock = d->m_mainWindow->addDockForWidget(d->m_stackWindow);
d->m_sourceFilesDock = d->m_mainWindow->addDockForWidget(d->m_sourceFilesWindow);
connect(d->m_sourceFilesDock->toggleViewAction(), SIGNAL(toggled(bool)),
this, SLOT(reloadSourceFiles()), Qt::QueuedConnection);
d->m_threadsDock = d->m_mainWindow->addDockForWidget(d->m_threadsWindow);
QSplitter *localsAndWatchers = new QSplitter(Qt::Vertical, 0);
localsAndWatchers->setWindowTitle(d->m_localsWindow->windowTitle());
localsAndWatchers->addWidget(d->m_localsWindow);
localsAndWatchers->addWidget(d->m_watchersWindow);
//localsAndWatchers->addWidget(d->m_tooltipWindow);
localsAndWatchers->setStretchFactor(0, 3);
localsAndWatchers->setStretchFactor(1, 1);
localsAndWatchers->setStretchFactor(2, 1);
d->m_watchDock = d->m_mainWindow->addDockForWidget(localsAndWatchers);
setState(DebuggerNotReady);
}
QList<Core::IOptionsPage*> DebuggerManager::initializeEngines(unsigned enabledTypeFlags)
{
QList<Core::IOptionsPage*> rc;
if (enabledTypeFlags & GdbEngineType) {
gdbEngine = createGdbEngine(this);
gdbEngine->addOptionPages(&rc);
}
winEngine = createWinEngine(this, (enabledTypeFlags & CdbEngineType), &rc);
if (enabledTypeFlags & ScriptEngineType) {
scriptEngine = createScriptEngine(this);
scriptEngine->addOptionPages(&rc);
}
if (enabledTypeFlags & TcfEngineType) {
tcfEngine = createTcfEngine(this);
tcfEngine->addOptionPages(&rc);
}
d->m_engine = 0;
STATE_DEBUG(gdbEngine << winEngine << scriptEngine << rc.size());
return rc;
}
DebuggerManagerActions DebuggerManager::debuggerManagerActions() const
{
return d->m_actions;
}
Core::Utils::FancyMainWindow *DebuggerManager::mainWindow() const
{
return d->m_mainWindow;
}
QLabel *DebuggerManager::statusLabel() const
{
return d->m_statusLabel;
}
IDebuggerEngine *DebuggerManager::currentEngine() const
{
return d->m_engine;
}
ModulesHandler *DebuggerManager::modulesHandler() const
{
return d->m_modulesHandler;
}
BreakHandler *DebuggerManager::breakHandler() const
{
return d->m_breakHandler;
}
RegisterHandler *DebuggerManager::registerHandler() const
{
return d->m_registerHandler;
}
StackHandler *DebuggerManager::stackHandler() const
{
return d->m_stackHandler;
}
ThreadsHandler *DebuggerManager::threadsHandler() const
{
return d->m_threadsHandler;
}
WatchHandler *DebuggerManager::watchHandler() const
{
return d->m_watchHandler;
}
SourceFilesWindow *DebuggerManager::sourceFileWindow() const
{
return d->m_sourceFilesWindow;
}
QWidget *DebuggerManager::threadsWindow() const
{
return d->m_threadsWindow;
}
void DebuggerManager::createNewDock(QWidget *widget)
{
QDockWidget *dockWidget = new QDockWidget(widget->windowTitle(), d->m_mainWindow);
dockWidget->setObjectName(widget->windowTitle());
dockWidget->setFeatures(QDockWidget::DockWidgetClosable);
dockWidget->setWidget(widget);
d->m_mainWindow->addDockWidget(Qt::TopDockWidgetArea, dockWidget);
dockWidget->show();
}
void DebuggerManager::setSimpleDockWidgetArrangement()
{
d->m_mainWindow->setTrackingEnabled(false);
QList<QDockWidget *> dockWidgets = d->m_mainWindow->dockWidgets();
foreach (QDockWidget *dockWidget, dockWidgets) {
dockWidget->setFloating(false);
d->m_mainWindow->removeDockWidget(dockWidget);
}
foreach (QDockWidget *dockWidget, dockWidgets) {
d->m_mainWindow->addDockWidget(Qt::BottomDockWidgetArea, dockWidget);
dockWidget->show();
}
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_breakDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_modulesDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_outputDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_registerDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_threadsDock);
d->m_mainWindow->tabifyDockWidget(d->m_watchDock, d->m_sourceFilesDock);
// They following views are rarely used in ordinary debugging. Hiding them
// saves cycles since the corresponding information won't be retrieved.
d->m_sourceFilesDock->hide();
d->m_registerDock->hide();
d->m_modulesDock->hide();
d->m_outputDock->hide();
d->m_mainWindow->setTrackingEnabled(true);
}
QAbstractItemModel *DebuggerManager::threadsModel()
{
return qobject_cast<ThreadsWindow*>(d->m_threadsWindow)->model();
}
void DebuggerManager::clearStatusMessage()
{
d->m_statusLabel->setText(d->m_lastPermanentStatusMessage);
}
void DebuggerManager::showStatusMessage(const QString &msg, int timeout)
{
Q_UNUSED(timeout)
showDebuggerOutput(LogStatus, msg);
d->m_statusLabel->setText(QLatin1String(" ") + msg);
if (timeout > 0) {
d->m_statusTimer->setSingleShot(true);
d->m_statusTimer->start(timeout);
} else {
d->m_lastPermanentStatusMessage = msg;
d->m_statusTimer->stop();
}
}
void DebuggerManager::notifyInferiorStopped()
{
resetLocation();
setState(InferiorStopped);
showStatusMessage(tr("Stopped."), 5000);
}
void DebuggerManager::notifyInferiorRunning()
{
setState(InferiorRunning);
showStatusMessage(tr("Running..."), 5000);
}
void DebuggerManager::notifyInferiorExited()
{
setState(DebuggerNotReady);
showStatusMessage(tr("Exited."), 5000);
}
void DebuggerManager::notifyInferiorPidChanged(qint64 pid)
{
STATE_DEBUG(d->m_inferiorPid << pid);
if (d->m_inferiorPid != pid) {
d->m_inferiorPid = pid;
emit inferiorPidChanged(pid);
}
}
void DebuggerManager::showApplicationOutput(const QString &str)
{
emit applicationOutputAvailable(str);
}
void DebuggerManager::shutdown()
{
STATE_DEBUG(d->m_engine);
if (d->m_engine)
d->m_engine->shutdown();
d->m_engine = 0;
#define doDelete(ptr) delete ptr; ptr = 0
doDelete(scriptEngine);
doDelete(gdbEngine);
doDelete(winEngine);
doDelete(tcfEngine);
// Delete these manually before deleting the manager
// (who will delete the models for most views)
doDelete(d->m_breakWindow);
doDelete(d->m_modulesWindow);
doDelete(d->m_outputWindow);
doDelete(d->m_registerWindow);
doDelete(d->m_stackWindow);
doDelete(d->m_sourceFilesWindow);
doDelete(d->m_threadsWindow);
//doDelete(d->m_tooltipWindow);
doDelete(d->m_watchersWindow);
doDelete(d->m_localsWindow);
doDelete(d->m_breakHandler);
doDelete(d->m_threadsHandler);
doDelete(d->m_modulesHandler);
doDelete(d->m_registerHandler);
doDelete(d->m_stackHandler);
doDelete(d->m_watchHandler);
#undef doDelete
}
BreakpointData *DebuggerManager::findBreakpoint(const QString &fileName, int lineNumber)
{
if (!d->m_breakHandler)
return 0;
int index = d->m_breakHandler->findBreakpoint(fileName, lineNumber);
return index == -1 ? 0 : d->m_breakHandler->at(index);
}
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)
{
STATE_DEBUG(fileName << lineNumber);
QTC_ASSERT(d->m_breakHandler, return);
if (state() != InferiorRunning
&& state() != InferiorStopped
&& state() != DebuggerNotReady) {
showStatusMessage(tr("Changing breakpoint state requires either a "
"fully running or fully stopped application."));
return;
}
int index = d->m_breakHandler->findBreakpoint(fileName, lineNumber);
if (index == -1)
d->m_breakHandler->setBreakpoint(fileName, lineNumber);
else
d->m_breakHandler->removeBreakpoint(index);
attemptBreakpointSynchronization();
}
void DebuggerManager::toggleBreakpointEnabled(const QString &fileName, int lineNumber)
{
STATE_DEBUG(fileName << lineNumber);
QTC_ASSERT(d->m_breakHandler, return);
if (state() != InferiorRunning
&& state() != InferiorStopped
&& state() != DebuggerNotReady) {
showStatusMessage(tr("Changing breakpoint state requires either a "
"fully running or fully stopped application."));
return;
}
d->m_breakHandler->toggleBreakpointEnabled(fileName, lineNumber);
attemptBreakpointSynchronization();
}
void DebuggerManager::attemptBreakpointSynchronization()
{
if (d->m_engine)
d->m_engine->attemptBreakpointSynchronization();
}
void DebuggerManager::setToolTipExpression(const QPoint &mousePos, TextEditor::ITextEditor *editor, int cursorPos)
{
if (d->m_engine)
d->m_engine->setToolTipExpression(mousePos, editor, cursorPos);
}
void DebuggerManager::updateWatchData(const Debugger::Internal::WatchData &data)
{
if (d->m_engine)
d->m_engine->updateWatchData(data);
}
static QString msgEngineNotAvailable(const char *engine)
{
return DebuggerManager::tr("The application requires the debugger engine '%1', "
"which is disabled.").arg(QLatin1String(engine));
}
static IDebuggerEngine *debuggerEngineForToolChain(ProjectExplorer::ToolChain::ToolChainType tc)
{
IDebuggerEngine *rc = 0;
switch (tc) {
case ProjectExplorer::ToolChain::LinuxICC:
case ProjectExplorer::ToolChain::MinGW:
case ProjectExplorer::ToolChain::GCC:
rc = gdbEngine;
break;
case ProjectExplorer::ToolChain::MSVC:
case ProjectExplorer::ToolChain::WINCE:
rc = winEngine;
break;
case ProjectExplorer::ToolChain::WINSCW: // S60
case ProjectExplorer::ToolChain::GCCE:
case ProjectExplorer::ToolChain::RVCT_ARMV5:
case ProjectExplorer::ToolChain::RVCT_ARMV6:
rc = gdbEngine;
break;
case ProjectExplorer::ToolChain::OTHER:
case ProjectExplorer::ToolChain::UNKNOWN:
case ProjectExplorer::ToolChain::INVALID:
default:
break;
}
return rc;
}
// Figure out the debugger type of an executable. Analyze executable
// unless the toolchain provides a hint.
static IDebuggerEngine *determineDebuggerEngine(const QString &executable,
int toolChainType,
QString *errorMessage,
QString *settingsIdHint)
{
if (executable.endsWith(_(".js"))) {
if (!scriptEngine) {
*errorMessage = msgEngineNotAvailable("Script Engine");
return 0;
}
return scriptEngine;
}
/*
if (executable.endsWith(_(".sym"))) {
if (!gdbEngine) {
*errorMessage = msgEngineNotAvailable("Gdb Engine");
return 0;
}
return gdbEngine;
}
*/
if (IDebuggerEngine *tce = debuggerEngineForToolChain(
static_cast<ProjectExplorer::ToolChain::ToolChainType>(toolChainType)))
return tce;
#ifndef Q_OS_WIN
Q_UNUSED(settingsIdHint)
if (!gdbEngine) {
*errorMessage = msgEngineNotAvailable("Gdb Engine");
return 0;
}
return gdbEngine;
#else
// A remote executable?
if (!executable.endsWith(_(".exe")))
return gdbEngine;
// If a file has PDB files, it has been compiled by VS.
QStringList pdbFiles;
if (!getPDBFiles(executable, &pdbFiles, errorMessage))
return 0;
if (pdbFiles.empty())
return gdbEngine;
// We need the CDB debugger in order to be able to debug VS
// executables
if (!winEngine) {
*errorMessage = DebuggerManager::tr("Debugging VS executables is currently not enabled.");
*settingsIdHint = QLatin1String("Cdb");
return 0;
}
return winEngine;
#endif
}
// Figure out the debugger type of a PID
static IDebuggerEngine *determineDebuggerEngine(int /* pid */,
int toolChainType,
QString *errorMessage)
{
if (IDebuggerEngine *tce = debuggerEngineForToolChain(
static_cast<ProjectExplorer::ToolChain::ToolChainType>(toolChainType)))
return tce;
#ifdef Q_OS_WIN
// Preferably Windows debugger
if (winEngine)
return winEngine;
if (gdbEngine)
return gdbEngine;
*errorMessage = msgEngineNotAvailable("Gdb Engine");
return 0;
#else
if (!gdbEngine) {
*errorMessage = msgEngineNotAvailable("Gdb Engine");
return 0;
}
return gdbEngine;
#endif
}
void DebuggerManager::startNewDebugger(const DebuggerStartParametersPtr &sp)
{
d->m_startParameters = sp;
d->m_inferiorPid = d->m_startParameters->attachPID > 0
? d->m_startParameters->attachPID : 0;
const QString toolChainName = ProjectExplorer::ToolChain::toolChainName(static_cast<ProjectExplorer::ToolChain::ToolChainType>(d->m_startParameters->toolChainType));
emit debugModeRequested();
showDebuggerOutput(LogStatus,
tr("Starting debugger for tool chain '%1'...").arg(toolChainName));
showDebuggerOutput(LogDebug, DebuggerSettings::instance()->dump());
QString errorMessage;
QString settingsIdHint;
switch (d->m_startParameters->startMode) {
case AttachExternal:
case AttachCrashedExternal:
d->m_engine = determineDebuggerEngine(d->m_startParameters->attachPID,
d->m_startParameters->toolChainType, &errorMessage);
break;
case AttachTcf:
d->m_engine = tcfEngine;
break;
default:
d->m_engine = determineDebuggerEngine(d->m_startParameters->executable,
d->m_startParameters->toolChainType, &errorMessage, &settingsIdHint);
break;
}
if (!d->m_engine) {
emit debuggingFinished();
// Create Message box with possibility to go to settings
QAbstractButton *settingsButton = 0;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"),
tr("Cannot debug '%1' (tool chain: '%2'): %3").
arg(d->m_startParameters->executable, toolChainName, errorMessage),
QMessageBox::Ok);
if (!settingsIdHint.isEmpty())
settingsButton = msgBox.addButton(tr("Settings..."), QMessageBox::AcceptRole);
msgBox.exec();
if (msgBox.clickedButton() == settingsButton)
Core::ICore::instance()->showOptionsDialog(
_(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY), settingsIdHint);
return;
}
STATE_DEBUG(d->m_startParameters->executable << d->m_engine);
setBusyCursor(false);
setState(EngineStarting);
connect(d->m_engine, SIGNAL(startFailed()), this, SLOT(startFailed()));
d->m_engine->startDebugger(d->m_startParameters);
}
void DebuggerManager::startFailed()
{
disconnect(d->m_engine, SIGNAL(startFailed()), this, SLOT(startFailed()));
setState(DebuggerNotReady);
emit debuggingFinished();
}
void DebuggerManager::cleanupViews()
{
resetLocation();
breakHandler()->setAllPending();
stackHandler()->removeAll();
threadsHandler()->removeAll();
modulesHandler()->removeAll();
watchHandler()->cleanup();
registerHandler()->removeAll();
d->m_sourceFilesWindow->removeAll();
d->m_disassemblerViewAgent.cleanup();
}
void DebuggerManager::exitDebugger()
{
// The engine will finally call setState(DebuggerNotReady) which
// in turn will handle the cleanup.
if (d->m_engine && state() != DebuggerNotReady)
d->m_engine->exitDebugger();
}
DebuggerStartParametersPtr DebuggerManager::startParameters() const
{
return d->m_startParameters;
}
qint64 DebuggerManager::inferiorPid() const
{
return d->m_inferiorPid;
}
void DebuggerManager::assignValueInDebugger()
{
if (QAction *action = qobject_cast<QAction *>(sender())) {
QString str = action->data().toString();
int i = str.indexOf('=');
if (i != -1)
assignValueInDebugger(str.left(i), str.mid(i + 1));
}
}
void DebuggerManager::assignValueInDebugger(const QString &expr, const QString &value)
{
QTC_ASSERT(d->m_engine, return);
d->m_engine->assignValueInDebugger(expr, value);
}
void DebuggerManager::activateFrame(int index)
{
QTC_ASSERT(d->m_engine, return);
d->m_engine->activateFrame(index);
}
void DebuggerManager::selectThread(int index)
{
QTC_ASSERT(d->m_engine, return);
d->m_engine->selectThread(index);
}
void DebuggerManager::loadAllSymbols()
{
QTC_ASSERT(d->m_engine, return);
d->m_engine->loadAllSymbols();
}
void DebuggerManager::loadSymbols(const QString &module)
{
QTC_ASSERT(d->m_engine, return);
d->m_engine->loadSymbols(module);
}
QList<Symbol> DebuggerManager::moduleSymbols(const QString &moduleName)
{
QTC_ASSERT(d->m_engine, return QList<Symbol>());
return d->m_engine->moduleSymbols(moduleName);
}
void DebuggerManager::stepExec()
{
QTC_ASSERT(d->m_engine, return);
resetLocation();
if (theDebuggerBoolSetting(OperateByInstruction))
d->m_engine->stepIExec();
else
d->m_engine->stepExec();
}
void DebuggerManager::stepOutExec()
{
QTC_ASSERT(d->m_engine, return);
resetLocation();
d->m_engine->stepOutExec();
}
void DebuggerManager::nextExec()
{
QTC_ASSERT(d->m_engine, return);
resetLocation();
if (theDebuggerBoolSetting(OperateByInstruction))
d->m_engine->nextIExec();
else
d->m_engine->nextExec();
}
void DebuggerManager::watchPoint()
{
if (QAction *action = qobject_cast<QAction *>(sender()))
if (d->m_engine)
d->m_engine->watchPoint(action->data().toPoint());
}
void DebuggerManager::executeDebuggerCommand()
{
if (QAction *action = qobject_cast<QAction *>(sender()))
executeDebuggerCommand(action->data().toString());
}
void DebuggerManager::executeDebuggerCommand(const QString &command)
{
STATE_DEBUG(command);
QTC_ASSERT(d->m_engine, return);
d->m_engine->executeDebuggerCommand(command);
}
void DebuggerManager::sessionLoaded()
{
loadSessionData();
}
void DebuggerManager::aboutToUnloadSession()
{
if (d->m_engine)
d->m_engine->shutdown();
}
void DebuggerManager::aboutToSaveSession()
{
saveSessionData();
}
void DebuggerManager::loadSessionData()
{
d->m_breakHandler->loadSessionData();
d->m_watchHandler->loadSessionData();
}
void DebuggerManager::saveSessionData()
{
d->m_breakHandler->saveSessionData();
d->m_watchHandler->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 << d->m_outputWindow->inputContents();
ts << "\n\n=======================================\n\n";
ts << d->m_outputWindow->combinedContents();
}
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();
theDebuggerAction(WatchExpression)->setValue(tc.selectedText());
}
void DebuggerManager::setBreakpoint(const QString &fileName, int lineNumber)
{
STATE_DEBUG(Q_FUNC_INFO << fileName << lineNumber);
QTC_ASSERT(d->m_breakHandler, return);
d->m_breakHandler->setBreakpoint(fileName, lineNumber);
attemptBreakpointSynchronization();
}
void DebuggerManager::breakByFunctionMain()
{
#ifdef Q_OS_WIN
// FIXME: wrong on non-Qt based binaries
emit breakByFunction("qMain");
#else
emit breakByFunction("main");
#endif
}
void DebuggerManager::breakByFunction(const QString &functionName)
{
QTC_ASSERT(d->m_breakHandler, return);
d->m_breakHandler->breakByFunction(functionName);
attemptBreakpointSynchronization();
}
void DebuggerManager::setBusyCursor(bool busy)
{
//STATE_DEBUG("BUSY FROM: " << d->m_busy << " TO: " << d->m_busy);
if (busy == d->m_busy)
return;
d->m_busy = busy;
QCursor cursor(busy ? Qt::BusyCursor : Qt::ArrowCursor);
d->m_breakWindow->setCursor(cursor);
d->m_localsWindow->setCursor(cursor);
d->m_modulesWindow->setCursor(cursor);
d->m_outputWindow->setCursor(cursor);
d->m_registerWindow->setCursor(cursor);
d->m_stackWindow->setCursor(cursor);
d->m_sourceFilesWindow->setCursor(cursor);
d->m_threadsWindow->setCursor(cursor);
//d->m_tooltipWindow->setCursor(cursor);
d->m_watchersWindow->setCursor(cursor);
}
void DebuggerManager::queryCurrentTextEditor(QString *fileName, int *lineNumber,
QObject **object)
{
emit currentTextEditorRequested(fileName, lineNumber, object);
}
void DebuggerManager::continueExec()
{
if (d->m_engine)
d->m_engine->continueInferior();
}
void DebuggerManager::detachDebugger()
{
if (d->m_engine)
d->m_engine->detachDebugger();
}
void DebuggerManager::interruptDebuggingRequest()
{
STATE_DEBUG(state());
if (!d->m_engine)
return;
bool interruptIsExit = (state() != InferiorRunning);
if (interruptIsExit) {
exitDebugger();
} else {
d->m_engine->interruptInferior();
}
}
void DebuggerManager::runToLineExec()
{
QString fileName;
int lineNumber = -1;
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
if (d->m_engine && !fileName.isEmpty()) {
STATE_DEBUG(fileName << lineNumber);
d->m_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;
}
}
}
STATE_DEBUG(functionName);
if (d->m_engine && !functionName.isEmpty())
d->m_engine->runToFunctionExec(functionName);
}
void DebuggerManager::jumpToLineExec()
{
QString fileName;
int lineNumber = -1;
emit currentTextEditorRequested(&fileName, &lineNumber, 0);
if (d->m_engine && !fileName.isEmpty()) {
STATE_DEBUG(fileName << lineNumber);
d->m_engine->jumpToLineExec(fileName, lineNumber);
}
}
void DebuggerManager::resetLocation()
{
d->m_disassemblerViewAgent.resetLocation();
d->m_stackHandler->setCurrentIndex(-1);
// Connected to the plugin.
emit resetLocationRequested();
}
void DebuggerManager::gotoLocation(const Debugger::Internal::StackFrame &frame, bool setMarker)
{
if (theDebuggerBoolSetting(OperateByInstruction) || !frame.isUsable()) {
if (setMarker)
resetLocation();
d->m_disassemblerViewAgent.setFrame(frame);
} else {
// Connected to the plugin.
emit gotoLocationRequested(frame.file, frame.line, setMarker);
}
}
void DebuggerManager::fileOpen(const QString &fileName)
{
StackFrame frame;
frame.file = fileName;
frame.line = -1;
gotoLocation(frame, false);
}
void DebuggerManager::operateByInstructionTriggered()
{
QTC_ASSERT(d->m_stackHandler, return);
StackFrame frame = d->m_stackHandler->currentFrame();
gotoLocation(frame, true);
}
//////////////////////////////////////////////////////////////////////
//
// Source files specific stuff
//
//////////////////////////////////////////////////////////////////////
void DebuggerManager::reloadSourceFiles()
{
if (d->m_engine && d->m_sourceFilesDock && d->m_sourceFilesDock->isVisible())
d->m_engine->reloadSourceFiles();
}
void DebuggerManager::sourceFilesDockToggled(bool on)
{
if (on)
reloadSourceFiles();
}
//////////////////////////////////////////////////////////////////////
//
// Modules specific stuff
//
//////////////////////////////////////////////////////////////////////
void DebuggerManager::reloadModules()
{
if (d->m_engine && d->m_modulesDock && d->m_modulesDock->isVisible())
d->m_engine->reloadModules();
}
void DebuggerManager::modulesDockToggled(bool on)
{
if (on)
reloadModules();
}
//////////////////////////////////////////////////////////////////////
//
// Output specific stuff
//
//////////////////////////////////////////////////////////////////////
void DebuggerManager::showDebuggerOutput(int channel, const QString &msg)
{
if (d->m_outputWindow)
d->m_outputWindow->showOutput(channel, msg);
else
qDebug() << "OUTPUT: " << channel << msg;
}
void DebuggerManager::showDebuggerInput(int channel, const QString &msg)
{
if (d->m_outputWindow)
d->m_outputWindow->showInput(channel, msg);
else
qDebug() << "INPUT: " << channel << msg;
}
//////////////////////////////////////////////////////////////////////
//
// Register specific stuff
//
//////////////////////////////////////////////////////////////////////
void DebuggerManager::registerDockToggled(bool on)
{
if (on)
reloadRegisters();
}
void DebuggerManager::reloadRegisters()
{
if (d->m_engine && d->m_registerDock && d->m_registerDock->isVisible())
d->m_engine->reloadRegisters();
}
//////////////////////////////////////////////////////////////////////
//
// Dumpers. "Custom dumpers" are a library compiled against the current
// Qt containing functions to evaluate values of Qt classes
// (such as QString, taking pointers to their addresses).
// The library must be loaded into the debuggee.
//
//////////////////////////////////////////////////////////////////////
bool DebuggerManager::qtDumperLibraryEnabled() const
{
return theDebuggerBoolSetting(UseDebuggingHelpers);
}
QString DebuggerManager::qtDumperLibraryName() const
{
if (theDebuggerAction(UseCustomDebuggingHelperLocation)->value().toBool())
return theDebuggerAction(CustomDebuggingHelperLocation)->value().toString();
return d->m_startParameters->dumperLibrary;
}
QStringList DebuggerManager::qtDumperLibraryLocations() const
{
if (theDebuggerAction(UseCustomDebuggingHelperLocation)->value().toBool()) {
const QString customLocation =
theDebuggerAction(CustomDebuggingHelperLocation)->value().toString();
const QString location =
tr("%1 (explicitly set in the Debugger Options)").arg(customLocation);
return QStringList(location);
}
return d->m_startParameters->dumperLibraryLocations;
}
void DebuggerManager::showQtDumperLibraryWarning(const QString &details)
{
QMessageBox dialog(mainWindow());
QPushButton *qtPref = dialog.addButton(tr("Open Qt preferences"),
QMessageBox::ActionRole);
QPushButton *helperOff = dialog.addButton(tr("Turn helper usage off"),
QMessageBox::ActionRole);
QPushButton *justContinue = dialog.addButton(tr("Continue anyway"),
QMessageBox::AcceptRole);
dialog.setDefaultButton(justContinue);
dialog.setWindowTitle(tr("Debugging helper missing"));
dialog.setText(tr("The debugger did not find the debugging helper library."));
dialog.setInformativeText(tr(
"The debugging helper is used to nicely format the values of some Qt "
"and Standard Library data types. "
"It must be compiled for each Qt version which "
"you can do in the Qt preferences page by selecting "
"a Qt installation and clicking on 'Rebuild' for the debugging "
"helper."));
if (!details.isEmpty())
dialog.setDetailedText(details);
dialog.exec();
if (dialog.clickedButton() == qtPref) {
Core::ICore::instance()->showOptionsDialog(_("Qt4"), _("Qt Versions"));
} else if (dialog.clickedButton() == helperOff) {
theDebuggerAction(UseDebuggingHelpers)->setValue(qVariantFromValue(false), false);
}
}
void DebuggerManager::reloadFullStack()
{
if (d->m_engine)
d->m_engine->reloadFullStack();
}
void DebuggerManager::setRegisterValue(int nr, const QString &value)
{
if (d->m_engine)
d->m_engine->setRegisterValue(nr, value);
}
bool DebuggerManager::isReverseDebugging() const
{
return d->m_actions.reverseDirectionAction->isChecked();
}
QVariant DebuggerManager::sessionValue(const QString &name)
{
// this is answered by the plugin
QVariant value;
emit sessionValueRequested(name, &value);
return value;
}
void DebuggerManager::setSessionValue(const QString &name, const QVariant &value)
{
emit setSessionValueRequested(name, value);
}
void DebuggerManager::showMessageBox(int icon,
const QString &title, const QString &text)
{
QMessageBox *mb = new QMessageBox(QMessageBox::Icon(icon),
title, text, QMessageBox::NoButton, mainWindow());
mb->setAttribute(Qt::WA_DeleteOnClose);
mb->show();
}
DebuggerState DebuggerManager::state() const
{
return d->m_state;
}
static bool isAllowedTransition(int from, int to)
{
switch (from) {
case -1:
return to == DebuggerNotReady;
case DebuggerNotReady:
return to == EngineStarting || to == DebuggerNotReady;
case EngineStarting:
return to == AdapterStarting || to == DebuggerNotReady;
case AdapterStarting:
return to == AdapterStarted || to == AdapterStartFailed;
case AdapterStarted:
return to == InferiorPreparing;
case AdapterStartFailed:
return to == DebuggerNotReady;
case InferiorPreparing:
return to == InferiorPrepared || to == InferiorPreparationFailed;
case InferiorPrepared:
return to == InferiorStarting;
case InferiorPreparationFailed:
return to == DebuggerNotReady;
case InferiorStarting:
return to == InferiorRunningRequested || to == InferiorStopped
|| to == InferiorStartFailed || to == InferiorUnrunnable;
case InferiorStartFailed:
return to == DebuggerNotReady;
case InferiorRunningRequested:
return to == InferiorRunning;
case InferiorRunning:
return to == InferiorStopping || to == InferiorShuttingDown;
case InferiorStopping:
return to == InferiorStopped || to == InferiorStopFailed;
case InferiorStopped:
return to == InferiorRunningRequested || to == InferiorShuttingDown;
case InferiorStopFailed:
return to == DebuggerNotReady;
case InferiorUnrunnable:
return to == AdapterShuttingDown;
case InferiorShuttingDown:
return to == InferiorShutDown || to == InferiorShutdownFailed;
case InferiorShutDown:
return to == AdapterShuttingDown;
case AdapterShuttingDown:
return to == DebuggerNotReady;
default:
qDebug() << "UNKNOWN STATE: " << from;
}
return false;
}
void DebuggerManager::setState(DebuggerState state)
{
//STATE_DEBUG("STATUS CHANGE: FROM " << stateName(d->m_state)
// << " TO " << stateName(state));
QString msg = _("State changed from %1(%2) to %3(%4).")
.arg(stateName(d->m_state)).arg(d->m_state).arg(stateName(state)).arg(state);
//if (!((d->m_state == -1 && state == 0) || (d->m_state == 0 && state == 0)))
// qDebug() << msg;
if (!isAllowedTransition(d->m_state, state))
qDebug() << "UNEXPECTED STATE TRANSITION: " << msg;
showDebuggerOutput(LogDebug, msg);
resetLocation();
if (state == d->m_state)
return;
d->m_state = state;
if (d->m_state == InferiorStopped)
resetLocation();
if (d->m_state == DebuggerNotReady) {
setBusyCursor(false);
cleanupViews();
emit debuggingFinished();
}
const bool stoppable = state == InferiorRunning
|| state == InferiorRunningRequested
|| state == InferiorStopping
|| state == InferiorStopped
|| state == InferiorUnrunnable;
const bool running = state == InferiorRunning;
const bool stopped = state == InferiorStopped;
if (stopped)
QApplication::alert(mainWindow(), 3000);
d->m_actions.watchAction->setEnabled(stopped);
d->m_actions.breakAction->setEnabled(true);
bool interruptIsExit = !running;
if (interruptIsExit) {
static QIcon icon(":/debugger/images/debugger_stop_small.png");
d->m_actions.stopAction->setIcon(icon);
d->m_actions.stopAction->setText(tr("Stop Debugger"));
} else {
static QIcon icon(":/debugger/images/debugger_interrupt_small.png");
d->m_actions.stopAction->setIcon(icon);
d->m_actions.stopAction->setText(tr("Interrupt"));
}
d->m_actions.stopAction->setEnabled(stoppable);
d->m_actions.resetAction->setEnabled(true);
d->m_actions.stepAction->setEnabled(stopped);
d->m_actions.stepOutAction->setEnabled(stopped);
d->m_actions.runToLineAction->setEnabled(stopped);
d->m_actions.runToFunctionAction->setEnabled(stopped);
d->m_actions.jumpToLineAction->setEnabled(stopped);
d->m_actions.nextAction->setEnabled(stopped);
emit stateChanged(d->m_state);
const bool notbusy = state == InferiorStopped
|| state == DebuggerNotReady
|| state == InferiorUnrunnable;
setBusyCursor(!notbusy);
}
QDebug operator<<(QDebug d, DebuggerState state)
{
return d << stateName(state) << '(' << int(state) << ')';
}
//////////////////////////////////////////////////////////////////////
//
// AbstractDebuggerEngine
//
//////////////////////////////////////////////////////////////////////
void IDebuggerEngine::showStatusMessage(const QString &msg, int timeout)
{
m_manager->showStatusMessage(msg, timeout);
}
DebuggerState IDebuggerEngine::state() const
{
return m_manager->state();
}
void IDebuggerEngine::setState(DebuggerState state)
{
m_manager->setState(state);
}
//////////////////////////////////////////////////////////////////////
//
// Testing
//
//////////////////////////////////////////////////////////////////////
void DebuggerManager::runTest(const QString &fileName)
{
d->m_startParameters->executable = fileName;
d->m_startParameters->processArgs = QStringList() << "--run-debuggee";
d->m_startParameters->workingDir.clear();
//startNewDebugger(StartInternal);
}
} // namespace Debugger