Commit b557c58e authored by Lasse Holmstedt's avatar Lasse Holmstedt

Qml Debugger: Enable debugging qml+cpp standalone applications

A new debugger engine, QmlCppEngine, is introduced, which wraps gdb
and qml engines into one. Alternatively, if using Windows, Cdb is used
instead of Gdb.

Most of the debugger ui switcher is now rewritten, and it is tailored
for the QML and CPP layout case, the only one supported anyway.

Reviewed-by: hjk
parent ef11c4e7
......@@ -58,6 +58,7 @@ public:
QToolBar *toolbar() const;
static QString idStringForObject(QObject *obj);
QRectF adjustToScreenBoundaries(const QRectF &boundingRectInSceneSpace);
void setDebugMode(bool isDebugMode);
public Q_SLOTS:
void setDesignModeBehavior(bool value);
......
......@@ -37,6 +37,7 @@
#include "boundingrecthighlighter.h"
#include "subcomponenteditortool.h"
#include "qmltoolbar.h"
#include "jsdebuggeragent.h"
#include <QDeclarativeItem>
#include <QDeclarativeEngine>
......@@ -48,6 +49,7 @@
#include <QApplication>
#include <QAbstractAnimation>
#include <private/qdeclarativeengine_p.h>
#include <private/qabstractanimation_p.h>
namespace QmlViewer {
......@@ -61,6 +63,7 @@ QDeclarativeDesignViewPrivate::QDeclarativeDesignViewPrivate(QDeclarativeDesignV
designModeBehavior(false),
executionPaused(false),
slowdownFactor(1.0f),
jsDebuggerAgent(0),
toolbar(0)
{
sceneChangedTimer.setInterval(SceneChangeUpdateInterval);
......@@ -116,6 +119,9 @@ QDeclarativeDesignView::QDeclarativeDesignView(QWidget *parent) :
data->createToolbar();
data->_q_changeToSingleSelectTool();
// always start debug mode - that's what this design view is for.
setDebugMode(true);
}
QDeclarativeDesignView::~QDeclarativeDesignView()
......@@ -763,6 +769,12 @@ void QDeclarativeDesignViewPrivate::createToolbar()
QObject::connect(q, SIGNAL(marqueeSelectToolActivated()), toolbar, SLOT(activateMarqueeSelectTool()));
}
void QDeclarativeDesignView::setDebugMode(bool isDebugMode)
{
if (isDebugMode && !data->jsDebuggerAgent)
data->jsDebuggerAgent = new JSDebuggerAgent(QDeclarativeEnginePrivate::getScriptEngine(engine()));
}
} //namespace QmlViewer
#include <moc_qdeclarativedesignview.cpp>
......@@ -36,6 +36,8 @@
#include "qdeclarativedesignview.h"
QT_FORWARD_DECLARE_CLASS(JSDebuggerAgent)
namespace QmlViewer {
class QDeclarativeDesignView;
......@@ -82,6 +84,8 @@ public:
bool executionPaused;
qreal slowdownFactor;
JSDebuggerAgent *jsDebuggerAgent;
QmlToolbar *toolbar;
QTimer sceneChangedTimer;
QSet<QGraphicsObject *> sceneGraphicsObjects;
......
......@@ -38,19 +38,19 @@ namespace Constants {
// modes and their priorities
const char * const MODE_DEBUG = "Debugger.Mode.Debug";
const int P_MODE_DEBUG = 85;
const char * const LANG_CPP = "C++";
// common actions
const char * const STOP = "Debugger.Interrupt";
const char * const STOP = "Debugger.Interrupt";
const char * const RESET = "Debugger.Reset";
const char * const STEP = "Debugger.StepLine";
const char * const STEPOUT = "Debugger.StepOut";
const char * const NEXT = "Debugger.NextLine";
const char * const REVERSE = "Debugger.ReverseDirection";
const char * const M_DEBUG_LANGUAGES = "Debugger.Menu.View.Languages";
const char * const M_DEBUG_VIEWS = "Debugger.Menu.View.Debug";
const char * const M_DEBUG_DEBUGGING_LANGUAGES = "Debugger.Menu.View.DebugLanguages";
const char * const M_DEBUG_VIEWS = "Debugger.Menu.View.Debug";
const char * const C_DEBUGMODE = "Debugger.DebugMode";
const char * const C_DEBUGMODE = "Debugger.DebugMode";
const char * const C_CPPDEBUGGER = "Gdb Debugger";
const char * const DEBUGGER_COMMON_SETTINGS_ID = "A.Common";
......@@ -62,6 +62,22 @@ const char * const DEBUGGER_SETTINGS_TR_CATEGORY =
const char * const DEBUGGER_COMMON_SETTINGS_CATEGORY_ICON =
":/core/images/category_debug.png";
const int QML_DEFAULT_DEBUG_SERVER_PORT = 3768;
const char * const E_QML_DEBUG_SERVER_PORT = "QML_DEBUG_SERVER_PORT";
// dock widget names
const char * const DW_BREAK = "Debugger.Docks.Break";
const char * const DW_MODULES = "Debugger.Docks.Modules";
const char * const DW_REGISTER = "Debugger.Docks.Register";
const char * const DW_OUTPUT = "Debugger.Docks.Output";
const char * const DW_SNAPSHOTS = "Debugger.Docks.Snapshots";
const char * const DW_STACK = "Debugger.Docks.Stack";
const char * const DW_SOURCE_FILES = "Debugger.Docks.SourceFiles";
const char * const DW_THREADS = "Debugger.Docks.Threads";
const char * const DW_WATCHERS = "Debugger.Docks.LocalsAndWatchers";
const char * const DW_QML_INSPECTOR = "Debugger.Docks.QmlInspector";
namespace Internal {
enum { debug = 0 };
#ifdef Q_OS_MAC
......@@ -71,6 +87,7 @@ namespace Internal {
#endif
} // namespace Internal
} // namespace Constants
......@@ -267,13 +284,23 @@ enum DebuggerEngineType
PdbEngineType = 0x08,
TcfEngineType = 0x10,
QmlEngineType = 0x20,
QmlCppEngineType = 0x40,
AllEngineTypes = GdbEngineType
| ScriptEngineType
| CdbEngineType
| PdbEngineType
| TcfEngineType
| QmlEngineType
| QmlCppEngineType
};
enum DebuggerLanguage
{
Lang_None = 0x0,
Lang_Cpp = 0x1,
Lang_Qml = 0x2
};
Q_DECLARE_FLAGS(DebuggerLanguages, DebuggerLanguage)
} // namespace Debugger
......
......@@ -106,12 +106,14 @@ using namespace TextEditor;
///////////////////////////////////////////////////////////////////////
DebuggerStartParameters::DebuggerStartParameters()
: attachPID(-1),
useTerminal(false),
breakAtMain(false),
toolChainType(ToolChain::UNKNOWN),
startMode(NoStartMode),
executableUid(0)
: attachPID(-1)
, useTerminal(false)
, breakAtMain(false)
, qmlServerAddress("127.0.0.1")
, qmlServerPort(0)
, toolChainType(ToolChain::UNKNOWN)
, executableUid(0)
, startMode(NoStartMode)
{}
void DebuggerStartParameters::clear()
......@@ -233,7 +235,8 @@ public:
m_stackHandler(engine),
m_threadsHandler(engine),
m_watchHandler(engine),
m_disassemblerViewAgent(engine)
m_disassemblerViewAgent(engine),
m_runInWrapperEngine(false)
{}
~DebuggerEnginePrivate() {}
......@@ -312,6 +315,8 @@ public:
WatchHandler m_watchHandler;
DisassemblerViewAgent m_disassemblerViewAgent;
QFutureInterface<void> m_progress;
bool m_runInWrapperEngine;
};
void DebuggerEnginePrivate::breakpointSetRemoveMarginActionTriggered()
......@@ -1329,7 +1334,11 @@ void DebuggerEngine::notifyEngineShutdownOk()
showMessage(_("NOTE: ENGINE SHUTDOWN OK"));
QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
setState(EngineShutdownOk);
d->queueFinishDebugger();
if (!d->m_runInWrapperEngine) {
d->queueFinishDebugger();
} else {
setState(DebuggerFinished);
}
}
void DebuggerEngine::notifyEngineShutdownFailed()
......@@ -1337,7 +1346,11 @@ void DebuggerEngine::notifyEngineShutdownFailed()
showMessage(_("NOTE: ENGINE SHUTDOWN FAILED"));
QTC_ASSERT(state() == EngineShutdownRequested, qDebug() << state());
setState(EngineShutdownFailed);
d->queueFinishDebugger();
if (!d->m_runInWrapperEngine) {
d->queueFinishDebugger();
} else {
setState(DebuggerFinished);
}
}
void DebuggerEnginePrivate::doFinishDebugger()
......@@ -1395,7 +1408,7 @@ void DebuggerEngine::notifyInferiorExited()
void DebuggerEngine::setState(DebuggerState state, bool forced)
{
//qDebug() << "STATUS CHANGE: FROM " << stateName(d->m_state)
// << " TO " << stateName(state);
// << " TO " << stateName(state);
DebuggerState oldState = d->m_state;
d->m_state = state;
......@@ -1412,6 +1425,13 @@ void DebuggerEngine::setState(DebuggerState state, bool forced)
showMessage(msg, LogDebug);
plugin()->updateState(this);
emit stateChanged(d->m_state);
}
void DebuggerEngine::setRunInWrapperEngine(bool value)
{
d->m_runInWrapperEngine = value;
}
bool DebuggerEngine::debuggerActionsEnabled() const
......@@ -1528,6 +1548,11 @@ QMessageBox *DebuggerEngine::showMessageBox(int icon, const QString &title,
return plugin()->showMessageBox(icon, title, text, buttons);
}
DebuggerRunControl *DebuggerEngine::runControl() const
{
return d->m_runControl;
}
} // namespace Internal
} // namespace Debugger
......
......@@ -79,6 +79,7 @@ public:
// for qml debugging
QString qmlServerAddress;
quint16 qmlServerPort;
DebuggerEngineType cppEngineType; // for cpp+qml debugging
// for remote debugging
QString remoteChannel;
......@@ -218,17 +219,17 @@ public:
WatchHandler *watchHandler() const;
SourceFilesHandler *sourceFilesHandler() const;
QAbstractItemModel *commandModel() const;
QAbstractItemModel *modulesModel() const;
QAbstractItemModel *breakModel() const;
QAbstractItemModel *registerModel() const;
QAbstractItemModel *stackModel() const;
QAbstractItemModel *threadsModel() const;
QAbstractItemModel *localsModel() const;
QAbstractItemModel *watchersModel() const;
QAbstractItemModel *returnModel() const;
virtual QAbstractItemModel *commandModel() const;
virtual QAbstractItemModel *modulesModel() const;
virtual QAbstractItemModel *breakModel() const;
virtual QAbstractItemModel *registerModel() const;
virtual QAbstractItemModel *stackModel() const;
virtual QAbstractItemModel *threadsModel() const;
virtual QAbstractItemModel *localsModel() const;
virtual QAbstractItemModel *watchersModel() const;
virtual QAbstractItemModel *returnModel() const;
//QAbstractItemModel *snapshotModel() const;
QAbstractItemModel *sourceFilesModel() const;
virtual QAbstractItemModel *sourceFilesModel() const;
void progressPing();
void handleFinished();
......@@ -275,6 +276,9 @@ public:
void gotoLocation(const StackFrame &frame, bool setMarker);
virtual void quitDebugger(); // called by DebuggerRunControl
signals:
void stateChanged(const DebuggerState &state);
protected:
// The base notify*() function implementation should be sufficient
// in most cases, but engines are free to override them to do some
......@@ -318,8 +322,11 @@ protected:
virtual void shutdownInferior() = 0;
virtual void shutdownEngine() = 0;
private:
void setState(DebuggerState state, bool forced = false);
void setRunInWrapperEngine(bool value);
private:
DebuggerRunControl *runControl() const;
private:
void executeRunToLine();
......@@ -327,6 +334,9 @@ private:
void executeJumpToLine();
void addToWatchWindow();
// wrapper engine needs access to state of its subengines
friend class QmlCppEngine;
friend class DebuggerEnginePrivate;
DebuggerEnginePrivate *d;
};
......
......@@ -71,9 +71,7 @@ QMenu* DebuggerMainWindow::createPopupMenu()
for (int i = 0; i < dockwidgets.size(); ++i) {
QDockWidget *dockWidget = dockwidgets.at(i)->m_dockWidget;
if (dockWidget->parentWidget() == this &&
dockwidgets.at(i)->m_languageId == m_uiSwitcher->activeLanguageId()) {
if (dockWidget->parentWidget() == this) {
menu->addAction(dockWidget->toggleViewAction());
}
}
......
......@@ -31,6 +31,7 @@
#define DEBUGGERMAINWINDOW_H
#include <utils/fancymainwindow.h>
#include "debuggerconstants.h"
QT_FORWARD_DECLARE_CLASS(QMenu);
......@@ -42,10 +43,9 @@ namespace Internal {
class DebugToolWindow
{
public:
DebugToolWindow() : m_dockWidget(0), m_languageId(-1), m_visible(false) {}
DebugToolWindow() : m_dockWidget(0), m_visible(false) {}
QDockWidget *m_dockWidget;
int m_languageId;
bool m_visible;
};
......
This diff is collapsed.
......@@ -31,6 +31,7 @@
#define DEBUGGERPLUGIN_H
#include "debugger_global.h"
#include "debuggerconstants.h"
#include <extensionsystem/iplugin.h>
......@@ -73,6 +74,7 @@ public:
ProjectExplorer::RunConfiguration *rc = 0);
static void startDebugger(ProjectExplorer::RunControl *runControl);
static void displayDebugger(ProjectExplorer::RunControl *runControl);
static void displayDebugger(Internal::DebuggerEngine *engine, bool updateEngine = true);
QVariant sessionValue(const QString &name);
void setSessionValue(const QString &name, const QVariant &value);
......@@ -110,6 +112,7 @@ private:
void createNewDock(QWidget *widget);
void runControlStarted(DebuggerRunControl *runControl);
void runControlFinished(DebuggerRunControl *runControl);
DebuggerLanguages activeLanguages() const;
// This contains per-session data like breakpoints and watched
// expression. It serves as a template for new engine instantiations.
......
......@@ -43,6 +43,7 @@
#include <projectexplorer/debugginghelper.h>
#include <projectexplorer/environment.h>
#include <projectexplorer/project.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/target.h>
#include <projectexplorer/buildconfiguration.h>
......@@ -74,6 +75,7 @@ DebuggerEngine *createScriptEngine(const DebuggerStartParameters &);
DebuggerEngine *createPdbEngine(const DebuggerStartParameters &);
DebuggerEngine *createTcfEngine(const DebuggerStartParameters &);
DebuggerEngine *createQmlEngine(const DebuggerStartParameters &);
DebuggerEngine *createQmlCppEngine(const DebuggerStartParameters &);
bool checkGdbConfiguration(int toolChain, QString *errorMsg, QString *settingsPage);
......@@ -148,6 +150,17 @@ static DebuggerStartParameters localStartParameters(RunConfiguration *runConfigu
sp.useTerminal = rc->runMode() == LocalApplicationRunConfiguration::Console;
sp.dumperLibrary = rc->dumperLibrary();
sp.dumperLibraryLocations = rc->dumperLibraryLocations();
if (DebuggerRunControl::isQmlProject(runConfiguration)) {
sp.qmlServerAddress = QLatin1String("127.0.0.1");
sp.qmlServerPort = rc->environment().value("QML_DEBUG_SERVER_PORT").toUInt();
if (sp.qmlServerPort == 0)
sp.qmlServerPort = Constants::QML_DEFAULT_DEBUG_SERVER_PORT;
sp.environment << QString(Constants::E_QML_DEBUG_SERVER_PORT)
+ QLatin1Char('=') + QString::number(sp.qmlServerPort);
}
// FIXME: If it's not yet build this will be empty and not filled
// when rebuild as the runConfiguration is not stored and therefore
// cannot be used to retrieve the dumper location.
......@@ -208,14 +221,15 @@ QWidget *DebuggerRunControlFactory::createConfigurationWidget
////////////////////////////////////////////////////////////////////////
DebuggerRunControl::DebuggerRunControl(RunConfiguration *runConfiguration,
DebuggerEngineType enabledEngines, const DebuggerStartParameters &sp) :
RunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE),
m_engine(0),
m_myRunConfiguration(runConfiguration),
m_running(false),
m_enabledEngines(enabledEngines)
DebuggerEngineType enabledEngines, const DebuggerStartParameters &sp)
: RunControl(runConfiguration, ProjectExplorer::Constants::DEBUGMODE)
, m_myRunConfiguration(runConfiguration)
, m_running(false)
, m_started(false)
{
connect(this, SIGNAL(finished()), this, SLOT(handleFinished()));
m_isQmlProject = isQmlProject(runConfiguration);
createEngine(sp);
}
......@@ -332,10 +346,13 @@ DebuggerEngineType DebuggerRunControl::engineForMode(DebuggerStartMode startMode
#endif
}
void DebuggerRunControl::createEngine(const DebuggerStartParameters &sp)
void DebuggerRunControl::createEngine(const DebuggerStartParameters &startParams)
{
DebuggerStartParameters sp = startParams;
// Figure out engine according to toolchain, executable, attach or default.
DebuggerEngineType engineType = NoEngineType;
DebuggerLanguages activeLangs = DebuggerPlugin::instance()->activeLanguages();
bool isQmlExecutable = sp.executable.endsWith(_("qmlviewer")) || sp.executable.endsWith(_("qmlobserver"));
#ifdef Q_OS_MAC
isQmlExecutable = sp.executable.endsWith(_("QMLViewer.app")) || sp.executable.endsWith(_("QMLObserver.app"));
......@@ -361,6 +378,15 @@ void DebuggerRunControl::createEngine(const DebuggerStartParameters &sp)
if (!engineType)
engineType = engineForMode(sp.startMode);
if (engineType != QmlEngineType && m_isQmlProject && (activeLangs & Lang_Qml)) {
if (activeLangs & Lang_Cpp) {
sp.cppEngineType = engineType;
engineType = QmlCppEngineType;
} else {
engineType = QmlEngineType;
}
}
// qDebug() << "USING ENGINE : " << engineType;
switch (engineType) {
......@@ -382,6 +408,9 @@ void DebuggerRunControl::createEngine(const DebuggerStartParameters &sp)
case QmlEngineType:
m_engine = createQmlEngine(sp);
break;
case QmlCppEngineType:
m_engine = createQmlCppEngine(sp);
break;
default: {
// Could not find anything suitable.
debuggingFinished();
......@@ -553,5 +582,45 @@ Internal::DebuggerEngine *DebuggerRunControl::engine()
return m_engine;
}
bool DebuggerRunControl::isQmlProject(RunConfiguration *config)
{
if (!config || !config->target() || !config->target()->project())
return false;
QStringList projectFiles = config->target()->project()->files(ProjectExplorer::Project::ExcludeGeneratedFiles);
foreach(const QString &filename, projectFiles) {
if (filename.endsWith(".qml"))
return true;
}
return false;
}
bool DebuggerRunControl::isCurrentProjectQmlCppBased()
{
Project *startupProject = ProjectExplorerPlugin::instance()->startupProject();
if (!startupProject)
return false;
if (!startupProject->activeTarget())
return false;
RunConfiguration *rc = startupProject->activeTarget()->activeRunConfiguration();
return isQmlProject(rc);
}
bool DebuggerRunControl::isCurrentProjectCppBased()
{
Project *startupProject = ProjectExplorerPlugin::instance()->startupProject();
if (!startupProject)
return false;
const QString id = startupProject->id();
return id == _("GenericProjectManager.GenericProject")
|| id == _("CMakeProjectManager.CMakeProject")
|| id == _("Qt4ProjectManager.Qt4Project");
}
} // namespace Debugger
......@@ -117,6 +117,10 @@ public:
QString *settingsCategory = 0,
QString *settingsPage = 0);
static bool isQmlProject(RunConfiguration *config);
static bool isCurrentProjectQmlCppBased();
static bool isCurrentProjectCppBased();
private slots:
void handleFinished();
......@@ -130,6 +134,8 @@ private:
Internal::DebuggerEngine *m_engine;
const QWeakPointer<RunConfiguration> m_myRunConfiguration;
bool m_running;
bool m_started;
bool m_isQmlProject;
DebuggerEngineType m_enabledEngines;
QString m_errorMessage;
QString m_settingsIdHint;
......
This diff is collapsed.
......@@ -31,11 +31,13 @@
#define DEBUGGERUISWITCHER_H
#include "debugger_global.h"
#include "debuggerconstants.h"
#include <QtCore/QObject>
#include <QtCore/QMultiHash>
QT_FORWARD_DECLARE_CLASS(QDockWidget);
QT_FORWARD_DECLARE_CLASS(QEvent)
QT_FORWARD_DECLARE_CLASS(QDockWidget)
namespace Core {
class ActionContainer;
......@@ -50,6 +52,11 @@ class FancyMainWindow;
class SavedAction;
}
namespace ProjectExplorer {
class Project;
class Target;
class RunConfiguration;
}
namespace Debugger {
struct DebuggerUISwitcherPrivate;
......@@ -69,62 +76,105 @@ public:
static DebuggerUISwitcher *instance();
// debuggable languages are registered with this function.
void addLanguage(const QString &langName, const Core::Context &context);
void addLanguage(const DebuggerLanguage &language, const QString &languageName,
const Core::Context &context);
// debugger toolbars are registered with this function
void setToolbar(const QString &langName, QWidget *widget);
void setToolbar(const DebuggerLanguage &language, QWidget *widget);
// menu actions are registered with this function
void addMenuAction(Core::Command *command, const QString &langName,
void addMenuAction(Core::Command *command, const DebuggerLanguage &language,
const QString &group = QString());
QStringList supportedLanguages() const;
// all supported languagess
DebuggerLanguages supportedLanguages() const;
// Changes the active language UI to the one specified by langName.
// Does nothing if automatic switching is toggled off from settings.
void setActiveLanguage(const QString &langName);
int activeLanguageId() const;
// active languages to be debugged.
DebuggerLanguages activeDebugLanguages() const;
// called when all dependent plugins have loaded
void initialize();
void aboutToShutdown();
// most common debugger windows
QDockWidget *breakWindow() const;
QDockWidget *stackWindow() const;
QDockWidget *watchWindow() const;
QDockWidget *snapshotsWindow() const;
QDockWidget *threadsWindow() const;
QDockWidget *outputWindow() const;
QDockWidget *qmlInspectorWindow() const;
QDockWidget *dockWidget(const QString &objectName) const;
// dockwidgets are registered to the main window
QDockWidget *createDockWidget(const QString &langName, QWidget *widget,
QDockWidget *createDockWidget(const DebuggerLanguage &language, QWidget *widget,
Qt::DockWidgetArea area = Qt::TopDockWidgetArea,
bool visibleByDefault = true);
Utils::FancyMainWindow *mainWindow() const;
signals:
void languageChanged(const QString &langName);
// emit when dock needs to be reset
void dockArranged(const QString &activeLanguage);
// emit when user changes active languages from the menu.
// Both UI and debugger startup are affected.
void activeLanguagesChanged(Debugger::DebuggerLanguages activeLanguages);
void dockResetRequested(const Debugger::DebuggerLanguages &activeLanguages);
private slots:
void modeChanged(Core::IMode *mode);
void changeDebuggerUI(const QString &langName);