forked from qt-creator/qt-creator
Make it possible to trigger a debug-attach in a running instance of Qt Creator by means of -client, use that in qtcdebugger, thus enabling it to attaching to crashing executables in run mode (Windows/CDB). Modify IPlugin and application so that the complete command line is serialized and transmitted to a running instance via QtSingleApplication if -client is specified. Introduce IPlugin::remoteArgument and use that in core for opening files and in the debugger for attaching. Use -client in qtcdebugger with some logic to keep it alive as long as the debuggee, make CDB break in that case as the events are not replayed correctly in case the debugger is not spawned by the registered handler. Rubber-stamped-by: con <qtc-committer@nokia.com>
1432 lines
50 KiB
C++
1432 lines
50 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 "debuggerplugin.h"
|
|
|
|
#include "breakhandler.h"
|
|
#include "debuggeractions.h"
|
|
#include "debuggerdialogs.h"
|
|
#include "debuggerconstants.h"
|
|
#include "debuggermanager.h"
|
|
#include "debuggerrunner.h"
|
|
#include "debuggerstringutils.h"
|
|
|
|
#include "ui_commonoptionspage.h"
|
|
#include "ui_dumperoptionpage.h"
|
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
|
#include <coreplugin/basemode.h>
|
|
#include <coreplugin/coreconstants.h>
|
|
#include <coreplugin/dialogs/ioptionspage.h>
|
|
#include <coreplugin/editormanager/editormanager.h>
|
|
#include <coreplugin/findplaceholder.h>
|
|
#include <coreplugin/icore.h>
|
|
#include <coreplugin/icorelistener.h>
|
|
#include <coreplugin/messagemanager.h>
|
|
#include <coreplugin/minisplitter.h>
|
|
#include <coreplugin/modemanager.h>
|
|
#include <coreplugin/navigationwidget.h>
|
|
#include <coreplugin/outputpane.h>
|
|
#include <coreplugin/rightpane.h>
|
|
#include <coreplugin/uniqueidmanager.h>
|
|
|
|
#include <cplusplus/ExpressionUnderCursor.h>
|
|
|
|
#include <cppeditor/cppeditorconstants.h>
|
|
|
|
#include <extensionsystem/pluginmanager.h>
|
|
|
|
#include <coreplugin/manhattanstyle.h>
|
|
#include <projectexplorer/projectexplorer.h>
|
|
#include <projectexplorer/projectexplorerconstants.h>
|
|
#include <projectexplorer/session.h>
|
|
#include <projectexplorer/project.h>
|
|
|
|
#include <texteditor/basetexteditor.h>
|
|
#include <texteditor/basetextmark.h>
|
|
#include <texteditor/itexteditor.h>
|
|
#include <texteditor/texteditorconstants.h>
|
|
|
|
#include <utils/qtcassert.h>
|
|
#include <utils/styledbar.h>
|
|
#include <utils/fancymainwindow.h>
|
|
|
|
#include <QtCore/QDebug>
|
|
#include <QtCore/QObject>
|
|
#include <QtCore/QPoint>
|
|
#include <QtCore/QSettings>
|
|
#include <QtCore/QtPlugin>
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtCore/QTimer>
|
|
|
|
#include <QtGui/QLineEdit>
|
|
#include <QtGui/QDockWidget>
|
|
#include <QtGui/QPlainTextEdit>
|
|
#include <QtGui/QTextBlock>
|
|
#include <QtGui/QTextCursor>
|
|
#include <QtGui/QToolButton>
|
|
#include <QtGui/QMessageBox>
|
|
|
|
#include <climits>
|
|
|
|
using namespace Core;
|
|
using namespace Debugger::Constants;
|
|
using namespace Debugger::Internal;
|
|
using namespace ProjectExplorer;
|
|
using namespace TextEditor;
|
|
|
|
|
|
namespace Debugger {
|
|
namespace Constants {
|
|
|
|
const char * const M_DEBUG_START_DEBUGGING = "QtCreator.Menu.Debug.StartDebugging";
|
|
|
|
const char * const STARTEXTERNAL = "Debugger.StartExternal";
|
|
const char * const ATTACHEXTERNAL = "Debugger.AttachExternal";
|
|
const char * const ATTACHCORE = "Debugger.AttachCore";
|
|
const char * const ATTACHREMOTE = "Debugger.AttachRemote";
|
|
const char * const DETACH = "Debugger.Detach";
|
|
|
|
const char * const RUN_TO_LINE = "Debugger.RunToLine";
|
|
const char * const RUN_TO_FUNCTION = "Debugger.RunToFunction";
|
|
const char * const JUMP_TO_LINE = "Debugger.JumpToLine";
|
|
const char * const TOGGLE_BREAK = "Debugger.ToggleBreak";
|
|
const char * const BREAK_BY_FUNCTION = "Debugger.BreakByFunction";
|
|
const char * const BREAK_AT_MAIN = "Debugger.BreakAtMain";
|
|
const char * const ADD_TO_WATCH1 = "Debugger.AddToWatch1";
|
|
const char * const ADD_TO_WATCH2 = "Debugger.AddToWatch2";
|
|
const char * const OPERATE_BY_INSTRUCTION = "Debugger.OperateByInstruction";
|
|
|
|
#ifdef Q_WS_MAC
|
|
const char * const INTERRUPT_KEY = "Shift+F5";
|
|
const char * const RESET_KEY = "Ctrl+Shift+F5";
|
|
const char * const STEP_KEY = "F7";
|
|
const char * const STEPOUT_KEY = "Shift+F7";
|
|
const char * const NEXT_KEY = "F6";
|
|
const char * const REVERSE_KEY = "";
|
|
const char * const RUN_TO_LINE_KEY = "Shift+F8";
|
|
const char * const RUN_TO_FUNCTION_KEY = "Ctrl+F6";
|
|
const char * const JUMP_TO_LINE_KEY = "Alt+D,Alt+L";
|
|
const char * const TOGGLE_BREAK_KEY = "F8";
|
|
const char * const BREAK_BY_FUNCTION_KEY = "Alt+D,Alt+F";
|
|
const char * const BREAK_AT_MAIN_KEY = "Alt+D,Alt+M";
|
|
const char * const ADD_TO_WATCH_KEY = "Alt+D,Alt+W";
|
|
#else
|
|
const char * const INTERRUPT_KEY = "Shift+F5";
|
|
const char * const RESET_KEY = "Ctrl+Shift+F5";
|
|
const char * const STEP_KEY = "F11";
|
|
const char * const STEPOUT_KEY = "Shift+F11";
|
|
const char * const NEXT_KEY = "F10";
|
|
const char * const REVERSE_KEY = "F12";
|
|
const char * const RUN_TO_LINE_KEY = "";
|
|
const char * const RUN_TO_FUNCTION_KEY = "";
|
|
const char * const JUMP_TO_LINE_KEY = "";
|
|
const char * const TOGGLE_BREAK_KEY = "F9";
|
|
const char * const BREAK_BY_FUNCTION_KEY = "";
|
|
const char * const BREAK_AT_MAIN_KEY = "";
|
|
const char * const ADD_TO_WATCH_KEY = "Ctrl+Alt+Q";
|
|
#endif
|
|
|
|
} // namespace Constants
|
|
} // namespace Debugger
|
|
|
|
|
|
static ProjectExplorer::SessionManager *sessionManager()
|
|
{
|
|
return ProjectExplorer::ProjectExplorerPlugin::instance()->session();
|
|
}
|
|
|
|
static QSettings *settings()
|
|
{
|
|
return ICore::instance()->settings();
|
|
}
|
|
|
|
static QToolButton *toolButton(QAction *action)
|
|
{
|
|
QToolButton *button = new QToolButton;
|
|
button->setDefaultAction(action);
|
|
return button;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DebugMode
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
namespace Debugger {
|
|
namespace Internal {
|
|
|
|
class DebugMode : public Core::BaseMode
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
DebugMode(QObject *parent = 0);
|
|
~DebugMode();
|
|
};
|
|
|
|
DebugMode::DebugMode(QObject *parent)
|
|
: BaseMode(parent)
|
|
{
|
|
setName(tr("Debug"));
|
|
setUniqueModeName(Constants::MODE_DEBUG);
|
|
setIcon(QIcon(":/fancyactionbar/images/mode_Debug.png"));
|
|
setPriority(Constants::P_MODE_DEBUG);
|
|
}
|
|
|
|
DebugMode::~DebugMode()
|
|
{
|
|
// Make sure the editor manager does not get deleted
|
|
EditorManager::instance()->setParent(0);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DebuggerListener: Close the debugging session if running.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
class DebuggerListener : public Core::ICoreListener {
|
|
Q_OBJECT
|
|
public:
|
|
explicit DebuggerListener(QObject *parent = 0);
|
|
virtual bool coreAboutToClose();
|
|
};
|
|
|
|
DebuggerListener::DebuggerListener(QObject *parent) :
|
|
Core::ICoreListener(parent)
|
|
{
|
|
}
|
|
|
|
bool DebuggerListener::coreAboutToClose()
|
|
{
|
|
DebuggerManager *mgr = DebuggerManager::instance();
|
|
if (!mgr)
|
|
return true;
|
|
// Ask to terminate the session.
|
|
const QString title = tr("Close Debugging Session");
|
|
bool cleanTermination = false;
|
|
switch (mgr->state()) {
|
|
case DebuggerNotReady:
|
|
return true;
|
|
case AdapterStarted: // Most importantly, terminating a running
|
|
case AdapterStartFailed: // debuggee can cause problems.
|
|
case InferiorUnrunnable:
|
|
case InferiorStartFailed:
|
|
case InferiorStopped:
|
|
case InferiorShutDown:
|
|
cleanTermination = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
const QString question = cleanTermination ?
|
|
tr("A debugging session is still in progress.\nWould you like to terminate it?") :
|
|
tr("A debugging session is still in progress. Terminating the session in the current"
|
|
" state (%1) can leave the target in an inconsistent state."
|
|
" Would you still like to terminate it?")
|
|
.arg(QLatin1String(DebuggerManager::stateName(mgr->state())));
|
|
QMessageBox::StandardButton answer = QMessageBox::question(mgr->mainWindow(), title, question,
|
|
QMessageBox::Yes|QMessageBox::No, QMessageBox::Yes);
|
|
if (answer != QMessageBox::Yes)
|
|
return false;
|
|
mgr->exitDebugger();
|
|
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
|
return true;
|
|
}
|
|
|
|
} // namespace Internal
|
|
} // namespace Debugger
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LocationMark
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
namespace Debugger {
|
|
namespace Internal {
|
|
|
|
static QIcon locationMarkIcon()
|
|
{
|
|
static const QIcon icon(":/debugger/images/location.svg");
|
|
return icon;
|
|
}
|
|
|
|
// Used in "real" editors
|
|
class LocationMark : public TextEditor::BaseTextMark
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
LocationMark(const QString &fileName, int linenumber)
|
|
: BaseTextMark(fileName, linenumber)
|
|
{}
|
|
|
|
QIcon icon() const { return locationMarkIcon(); }
|
|
void updateLineNumber(int /*lineNumber*/) {}
|
|
void updateBlock(const QTextBlock & /*block*/) {}
|
|
void removedFromEditor() {}
|
|
};
|
|
|
|
} // namespace Internal
|
|
} // namespace Debugger
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CommonOptionsPage
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
namespace Debugger {
|
|
namespace Internal {
|
|
|
|
class CommonOptionsPage : public Core::IOptionsPage
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
CommonOptionsPage() {}
|
|
|
|
// IOptionsPage
|
|
QString id() const
|
|
{ return QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_ID); }
|
|
QString trName() const
|
|
{ return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_COMMON_SETTINGS_NAME); }
|
|
QString category() const
|
|
{ return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); }
|
|
QString trCategory() const
|
|
{ return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_TR_CATEGORY); }
|
|
|
|
QWidget *createPage(QWidget *parent);
|
|
void apply() { m_group.apply(settings()); }
|
|
void finish() { m_group.finish(); }
|
|
virtual bool matches(const QString &s) const;
|
|
|
|
private:
|
|
Ui::CommonOptionsPage m_ui;
|
|
Utils::SavedActionSet m_group;
|
|
QString m_searchKeywords;
|
|
};
|
|
|
|
QWidget *CommonOptionsPage::createPage(QWidget *parent)
|
|
{
|
|
QWidget *w = new QWidget(parent);
|
|
m_ui.setupUi(w);
|
|
m_group.clear();
|
|
|
|
m_group.insert(theDebuggerAction(ListSourceFiles),
|
|
m_ui.checkBoxListSourceFiles);
|
|
m_group.insert(theDebuggerAction(UseAlternatingRowColors),
|
|
m_ui.checkBoxUseAlternatingRowColors);
|
|
m_group.insert(theDebuggerAction(UseMessageBoxForSignals),
|
|
m_ui.checkBoxUseMessageBoxForSignals);
|
|
m_group.insert(theDebuggerAction(SkipKnownFrames),
|
|
m_ui.checkBoxSkipKnownFrames);
|
|
m_group.insert(theDebuggerAction(UseToolTipsInMainEditor),
|
|
m_ui.checkBoxUseToolTipsInMainEditor);
|
|
m_group.insert(theDebuggerAction(AutoDerefPointers), 0);
|
|
m_group.insert(theDebuggerAction(UseToolTipsInLocalsView), 0);
|
|
m_group.insert(theDebuggerAction(UseToolTipsInBreakpointsView), 0);
|
|
m_group.insert(theDebuggerAction(UseAddressInBreakpointsView), 0);
|
|
m_group.insert(theDebuggerAction(UseAddressInStackView), 0);
|
|
m_group.insert(theDebuggerAction(EnableReverseDebugging),
|
|
m_ui.checkBoxEnableReverseDebugging);
|
|
m_group.insert(theDebuggerAction(MaximalStackDepth),
|
|
m_ui.spinBoxMaximalStackDepth);
|
|
m_group.insert(theDebuggerAction(GdbWatchdogTimeout), 0);
|
|
m_group.insert(theDebuggerAction(ShowStdNamespace), 0);
|
|
m_group.insert(theDebuggerAction(ShowQtNamespace), 0);
|
|
m_group.insert(theDebuggerAction(LogTimeStamps), 0);
|
|
m_group.insert(theDebuggerAction(UsePreciseBreakpoints), 0);
|
|
|
|
#ifdef USE_REVERSE_DEBUGGING
|
|
m_ui.checkBoxEnableReverseDebugging->hide();
|
|
#endif
|
|
|
|
if (m_searchKeywords.isEmpty()) {
|
|
QTextStream(&m_searchKeywords) << ' ' << m_ui.checkBoxListSourceFiles->text()
|
|
<< ' ' << m_ui.checkBoxUseMessageBoxForSignals->text()
|
|
<< ' ' << m_ui.checkBoxUseAlternatingRowColors->text()
|
|
<< ' ' << m_ui.checkBoxUseToolTipsInMainEditor->text()
|
|
<< ' ' << m_ui.checkBoxSkipKnownFrames->text()
|
|
<< ' ' << m_ui.checkBoxEnableReverseDebugging->text()
|
|
<< ' ' << m_ui.labelMaximalStackDepth->text();
|
|
m_searchKeywords.remove(QLatin1Char('&'));
|
|
}
|
|
|
|
return w;
|
|
}
|
|
|
|
bool CommonOptionsPage::matches(const QString &s) const
|
|
{
|
|
return m_searchKeywords.contains(s, Qt::CaseInsensitive);
|
|
}
|
|
|
|
} // namespace Internal
|
|
} // namespace Debugger
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DebuggingHelperOptionPage
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
static inline bool oxygenStyle()
|
|
{
|
|
if (const ManhattanStyle *ms = qobject_cast<const ManhattanStyle *>(qApp->style()))
|
|
return !qstrcmp("OxygenStyle", ms->systemStyle()->metaObject()->className());
|
|
return false;
|
|
}
|
|
|
|
namespace Debugger {
|
|
namespace Internal {
|
|
|
|
class DebuggingHelperOptionPage : public Core::IOptionsPage
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
DebuggingHelperOptionPage() {}
|
|
|
|
// IOptionsPage
|
|
QString id() const { return QLatin1String("B.DebuggingHelper"); }
|
|
QString trName() const { return tr("Debugging Helper"); }
|
|
QString category() const { return QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY); }
|
|
QString trCategory() const { return QCoreApplication::translate("Debugger", Debugger::Constants::DEBUGGER_SETTINGS_TR_CATEGORY); }
|
|
|
|
QWidget *createPage(QWidget *parent);
|
|
void apply() { m_group.apply(settings()); }
|
|
void finish() { m_group.finish(); }
|
|
virtual bool matches(const QString &s) const;
|
|
|
|
private:
|
|
Ui::DebuggingHelperOptionPage m_ui;
|
|
Utils::SavedActionSet m_group;
|
|
QString m_searchKeywords;
|
|
};
|
|
|
|
QWidget *DebuggingHelperOptionPage::createPage(QWidget *parent)
|
|
{
|
|
QWidget *w = new QWidget(parent);
|
|
m_ui.setupUi(w);
|
|
|
|
m_ui.dumperLocationChooser->setExpectedKind(Utils::PathChooser::Command);
|
|
m_ui.dumperLocationChooser->setPromptDialogTitle(tr("Choose DebuggingHelper Location"));
|
|
m_ui.dumperLocationChooser->setInitialBrowsePathBackup(
|
|
Core::ICore::instance()->resourcePath() + "../../lib");
|
|
|
|
m_group.clear();
|
|
m_group.insert(theDebuggerAction(UseDebuggingHelpers),
|
|
m_ui.debuggingHelperGroupBox);
|
|
m_group.insert(theDebuggerAction(UseCustomDebuggingHelperLocation),
|
|
m_ui.customLocationGroupBox);
|
|
// Suppress Oxygen style's giving flat group boxes bold titles
|
|
if (oxygenStyle())
|
|
m_ui.customLocationGroupBox->setStyleSheet(QLatin1String("QGroupBox::title { font: ; }"));
|
|
|
|
m_group.insert(theDebuggerAction(CustomDebuggingHelperLocation),
|
|
m_ui.dumperLocationChooser);
|
|
|
|
m_group.insert(theDebuggerAction(UseCodeModel),
|
|
m_ui.checkBoxUseCodeModel);
|
|
|
|
#ifdef QT_DEBUG
|
|
m_group.insert(theDebuggerAction(DebugDebuggingHelpers),
|
|
m_ui.checkBoxDebugDebuggingHelpers);
|
|
#else
|
|
m_ui.checkBoxDebugDebuggingHelpers->hide();
|
|
#endif
|
|
|
|
#ifndef QT_DEBUG
|
|
#if 0
|
|
cmd = am->registerAction(m_manager->m_dumpLogAction,
|
|
Constants::DUMP_LOG, globalcontext);
|
|
//cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+D,Ctrl+L")));
|
|
cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Shift+F11")));
|
|
mdebug->addAction(cmd);
|
|
#endif
|
|
#endif
|
|
|
|
if (m_searchKeywords.isEmpty()) {
|
|
QTextStream(&m_searchKeywords)
|
|
<< ' ' << m_ui.debuggingHelperGroupBox->title()
|
|
<< ' ' << m_ui.customLocationGroupBox->title()
|
|
<< ' ' << m_ui.dumperLocationLabel->text()
|
|
<< ' ' << m_ui.checkBoxUseCodeModel->text()
|
|
<< ' ' << m_ui.checkBoxDebugDebuggingHelpers->text();
|
|
m_searchKeywords.remove(QLatin1Char('&'));
|
|
}
|
|
return w;
|
|
}
|
|
|
|
bool DebuggingHelperOptionPage::matches(const QString &s) const
|
|
{
|
|
return m_searchKeywords.contains(s, Qt::CaseInsensitive);
|
|
}
|
|
|
|
} // namespace Internal
|
|
} // namespace Debugger
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DebuggerPlugin
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
DebuggerPlugin::AttachRemoteParameters::AttachRemoteParameters() :
|
|
attachPid(0),
|
|
winCrashEvent(0)
|
|
{
|
|
}
|
|
|
|
DebuggerPlugin::DebuggerPlugin()
|
|
: m_manager(0),
|
|
m_debugMode(0),
|
|
m_locationMark(0),
|
|
m_gdbRunningContext(0),
|
|
m_cmdLineEnabledEngines(AllEngineTypes),
|
|
m_toggleLockedAction(0)
|
|
{}
|
|
|
|
DebuggerPlugin::~DebuggerPlugin()
|
|
{}
|
|
|
|
void DebuggerPlugin::shutdown()
|
|
{
|
|
QTC_ASSERT(m_manager, /**/);
|
|
if (m_manager)
|
|
m_manager->shutdown();
|
|
|
|
writeSettings();
|
|
delete DebuggerSettings::instance();
|
|
|
|
//qDebug() << "DebuggerPlugin::~DebuggerPlugin";
|
|
removeObject(m_debugMode);
|
|
|
|
// FIXME: when using the line below, BreakWindow etc gets deleted twice.
|
|
// so better leak for now...
|
|
delete m_debugMode;
|
|
m_debugMode = 0;
|
|
|
|
delete m_locationMark;
|
|
m_locationMark = 0;
|
|
|
|
removeObject(m_manager);
|
|
delete m_manager;
|
|
m_manager = 0;
|
|
}
|
|
|
|
static QString msgParameterMissing(const QString &a)
|
|
{
|
|
return DebuggerPlugin::tr("Option '%1' is missing the parameter.").arg(a);
|
|
}
|
|
|
|
static QString msgInvalidNumericParameter(const QString &a, const QString &number)
|
|
{
|
|
return DebuggerPlugin::tr("The parameter '%1' of option '%2' is not a number.").arg(number, a);
|
|
}
|
|
|
|
// Parse arguments
|
|
static bool parseArgument(QStringList::const_iterator &it,
|
|
const QStringList::const_iterator &cend,
|
|
DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters,
|
|
unsigned *enabledEngines, QString *errorMessage)
|
|
{
|
|
const QString &option = *it;
|
|
// '-debug <pid>'
|
|
if (*it == QLatin1String("-debug")) {
|
|
++it;
|
|
if (it == cend) {
|
|
*errorMessage = msgParameterMissing(*it);
|
|
return false;
|
|
}
|
|
bool ok;
|
|
attachRemoteParameters->attachPid = it->toULongLong(&ok);
|
|
if (!ok) {
|
|
attachRemoteParameters->attachPid = 0;
|
|
attachRemoteParameters->attachCore = *it;
|
|
}
|
|
return true;
|
|
}
|
|
// -wincrashevent <event-handle>. A handle used for
|
|
// a handshake when attaching to a crashed Windows process.
|
|
if (*it == QLatin1String("-wincrashevent")) {
|
|
++it;
|
|
if (it == cend) {
|
|
*errorMessage = msgParameterMissing(*it);
|
|
return false;
|
|
}
|
|
bool ok;
|
|
attachRemoteParameters->winCrashEvent = it->toULongLong(&ok);
|
|
if (!ok) {
|
|
*errorMessage = msgInvalidNumericParameter(option, *it);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
// engine disabling
|
|
if (option == QLatin1String("-disable-cdb")) {
|
|
*enabledEngines &= ~Debugger::CdbEngineType;
|
|
return true;
|
|
}
|
|
if (option == QLatin1String("-disable-gdb")) {
|
|
*enabledEngines &= ~Debugger::GdbEngineType;
|
|
return true;
|
|
}
|
|
if (option == QLatin1String("-disable-sdb")) {
|
|
*enabledEngines &= ~Debugger::ScriptEngineType;
|
|
return true;
|
|
}
|
|
|
|
*errorMessage = DebuggerPlugin::tr("Invalid debugger option: %1").arg(option);
|
|
return false;
|
|
}
|
|
|
|
static bool parseArguments(const QStringList &args,
|
|
DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters,
|
|
unsigned *enabledEngines, QString *errorMessage)
|
|
{
|
|
const QStringList::const_iterator cend = args.constEnd();
|
|
for (QStringList::const_iterator it = args.constBegin(); it != cend; ++it)
|
|
if (!parseArgument(it, cend, attachRemoteParameters, enabledEngines, errorMessage))
|
|
return false;
|
|
if (Debugger::Constants::Internal::debug)
|
|
qDebug().nospace() << args << "engines=0x"
|
|
<< QString::number(*enabledEngines, 16)
|
|
<< " pid" << attachRemoteParameters->attachPid
|
|
<< " core" << attachRemoteParameters->attachCore << '\n';
|
|
return true;
|
|
}
|
|
|
|
void DebuggerPlugin::remoteCommand(const QStringList &options, const QStringList &)
|
|
{
|
|
QString errorMessage;
|
|
AttachRemoteParameters parameters;
|
|
unsigned dummy = 0;
|
|
// Did we receive a request for debugging (unless it is ourselves)?
|
|
if (parseArguments(options, ¶meters, &dummy, &errorMessage)
|
|
&& parameters.attachPid != quint64(QCoreApplication::applicationPid())) {
|
|
m_attachRemoteParameters = parameters;
|
|
attachCmdLine();
|
|
}
|
|
}
|
|
|
|
bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
|
{
|
|
// Do not fail the whole plugin if something goes wrong here
|
|
if (!parseArguments(arguments, &m_attachRemoteParameters, &m_cmdLineEnabledEngines, errorMessage)) {
|
|
*errorMessage = tr("Error evaluating command line arguments: %1")
|
|
.arg(*errorMessage);
|
|
qWarning("%s\n", qPrintable(*errorMessage));
|
|
errorMessage->clear();
|
|
}
|
|
|
|
m_manager = new DebuggerManager;
|
|
ExtensionSystem::PluginManager::instance()->addObject(m_manager);
|
|
const QList<Core::IOptionsPage *> engineOptionPages =
|
|
m_manager->initializeEngines(m_cmdLineEnabledEngines);
|
|
|
|
ICore *core = ICore::instance();
|
|
QTC_ASSERT(core, return false);
|
|
|
|
Core::ActionManager *am = core->actionManager();
|
|
QTC_ASSERT(am, return false);
|
|
|
|
Core::UniqueIDManager *uidm = core->uniqueIDManager();
|
|
QTC_ASSERT(uidm, return false);
|
|
|
|
QList<int> globalcontext;
|
|
globalcontext << Core::Constants::C_GLOBAL_ID;
|
|
|
|
QList<int> cppcontext;
|
|
cppcontext << uidm->uniqueIdentifier(ProjectExplorer::Constants::LANG_CXX);
|
|
|
|
QList<int> debuggercontext;
|
|
debuggercontext << uidm->uniqueIdentifier(C_GDBDEBUGGER);
|
|
|
|
QList<int> cppeditorcontext;
|
|
cppeditorcontext << uidm->uniqueIdentifier(CppEditor::Constants::C_CPPEDITOR);
|
|
|
|
QList<int> texteditorcontext;
|
|
texteditorcontext << uidm->uniqueIdentifier(TextEditor::Constants::C_TEXTEDITOR);
|
|
|
|
m_gdbRunningContext = uidm->uniqueIdentifier(Constants::GDBRUNNING);
|
|
|
|
//Core::ActionContainer *mcppcontext =
|
|
// am->actionContainer(CppEditor::Constants::M_CONTEXT);
|
|
|
|
// External apps
|
|
m_startExternalAction = new QAction(this);
|
|
m_startExternalAction->setText(tr("Start and Debug External Application..."));
|
|
connect(m_startExternalAction, SIGNAL(triggered()),
|
|
this, SLOT(startExternalApplication()));
|
|
|
|
m_attachExternalAction = new QAction(this);
|
|
m_attachExternalAction->setText(tr("Attach to Running External Application..."));
|
|
connect(m_attachExternalAction, SIGNAL(triggered()),
|
|
this, SLOT(attachExternalApplication()));
|
|
|
|
m_attachCoreAction = new QAction(this);
|
|
m_attachCoreAction->setText(tr("Attach to Core..."));
|
|
connect(m_attachCoreAction, SIGNAL(triggered()), this, SLOT(attachCore()));
|
|
|
|
|
|
m_startRemoteAction = new QAction(this);
|
|
m_startRemoteAction->setText(tr("Start and Attach to Remote Application..."));
|
|
connect(m_startRemoteAction, SIGNAL(triggered()),
|
|
this, SLOT(startRemoteApplication()));
|
|
|
|
|
|
m_detachAction = new QAction(this);
|
|
m_detachAction->setText(tr("Detach Debugger"));
|
|
connect(m_detachAction, SIGNAL(triggered()),
|
|
m_manager, SLOT(detachDebugger()));
|
|
|
|
Core::ActionContainer *mdebug =
|
|
am->actionContainer(ProjectExplorer::Constants::M_DEBUG);
|
|
|
|
Core::ActionContainer *mstart =
|
|
am->actionContainer(ProjectExplorer::Constants::M_DEBUG_STARTDEBUGGING);
|
|
|
|
Core::Command *cmd = 0;
|
|
const DebuggerManagerActions actions = m_manager->debuggerManagerActions();
|
|
cmd = am->registerAction(actions.continueAction,
|
|
ProjectExplorer::Constants::DEBUG, QList<int>() << m_gdbRunningContext);
|
|
mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
cmd = am->registerAction(m_startExternalAction,
|
|
Constants::STARTEXTERNAL, globalcontext);
|
|
mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
cmd = am->registerAction(m_attachExternalAction,
|
|
Constants::ATTACHEXTERNAL, globalcontext);
|
|
mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
cmd = am->registerAction(m_attachCoreAction,
|
|
Constants::ATTACHCORE, globalcontext);
|
|
mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
cmd = am->registerAction(m_startRemoteAction,
|
|
Constants::ATTACHREMOTE, globalcontext);
|
|
mstart->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
cmd = am->registerAction(m_detachAction,
|
|
Constants::DETACH, globalcontext);
|
|
mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
cmd = am->registerAction(actions.stopAction,
|
|
Constants::INTERRUPT, globalcontext);
|
|
cmd->setAttribute(Core::Command::CA_UpdateText);
|
|
cmd->setAttribute(Core::Command::CA_UpdateIcon);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::INTERRUPT_KEY));
|
|
cmd->setDefaultText(tr("Stop Debugger/Interrupt Debugger"));
|
|
mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
cmd = am->registerAction(actions.resetAction,
|
|
Constants::RESET, globalcontext);
|
|
cmd->setAttribute(Core::Command::CA_UpdateText);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::RESET_KEY));
|
|
cmd->setDefaultText(tr("Reset Debugger"));
|
|
//disabled mdebug->addAction(cmd, Core::Constants::G_DEFAULT_ONE);
|
|
|
|
QAction *sep = new QAction(this);
|
|
sep->setSeparator(true);
|
|
cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Step"), globalcontext);
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.nextAction,
|
|
Constants::NEXT, debuggercontext);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::NEXT_KEY));
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.stepAction,
|
|
Constants::STEP, debuggercontext);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::STEP_KEY));
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.stepOutAction,
|
|
Constants::STEPOUT, debuggercontext);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::STEPOUT_KEY));
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.runToLineAction,
|
|
Constants::RUN_TO_LINE, debuggercontext);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_LINE_KEY));
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.runToFunctionAction,
|
|
Constants::RUN_TO_FUNCTION, debuggercontext);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::RUN_TO_FUNCTION_KEY));
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.jumpToLineAction,
|
|
Constants::JUMP_TO_LINE, debuggercontext);
|
|
mdebug->addAction(cmd);
|
|
|
|
#ifdef USE_REVERSE_DEBUGGING
|
|
cmd = am->registerAction(actions.reverseDirectionAction,
|
|
Constants::REVERSE, debuggercontext);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::REVERSE_KEY));
|
|
mdebug->addAction(cmd);
|
|
#endif
|
|
|
|
sep = new QAction(this);
|
|
sep->setSeparator(true);
|
|
cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Break"), globalcontext);
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(theDebuggerAction(OperateByInstruction),
|
|
Constants::OPERATE_BY_INSTRUCTION, debuggercontext);
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.breakAction,
|
|
Constants::TOGGLE_BREAK, cppeditorcontext);
|
|
cmd->setDefaultKeySequence(QKeySequence(Constants::TOGGLE_BREAK_KEY));
|
|
mdebug->addAction(cmd);
|
|
//mcppcontext->addAction(cmd);
|
|
|
|
sep = new QAction(this);
|
|
sep->setSeparator(true);
|
|
cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Watch"), globalcontext);
|
|
mdebug->addAction(cmd);
|
|
|
|
cmd = am->registerAction(actions.watchAction1,
|
|
Constants::ADD_TO_WATCH1, cppeditorcontext);
|
|
cmd->action()->setEnabled(true);
|
|
//cmd->setDefaultKeySequence(QKeySequence(tr("ALT+D,ALT+W")));
|
|
mdebug->addAction(cmd);
|
|
|
|
ActionContainer *editorContextMenu =
|
|
am->actionContainer(CppEditor::Constants::M_CONTEXT);
|
|
cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Views"),
|
|
debuggercontext);
|
|
editorContextMenu->addAction(cmd);
|
|
cmd->setAttribute(Command::CA_Hide);
|
|
cmd = am->registerAction(actions.watchAction2,
|
|
Constants::ADD_TO_WATCH2, debuggercontext);
|
|
cmd->action()->setEnabled(true);
|
|
//cmd->setDefaultKeySequence(QKeySequence(tr("ALT+D,ALT+W")));
|
|
editorContextMenu->addAction(cmd);
|
|
cmd->setAttribute(Command::CA_Hide);
|
|
|
|
// Views menu
|
|
cmd = am->registerAction(sep, QLatin1String("Debugger.Sep.Views"), globalcontext);
|
|
mdebug->addAction(cmd);
|
|
ActionContainer *viewsMenu = am->createMenu(Constants::M_DEBUG_VIEWS);
|
|
QMenu *m = viewsMenu->menu();
|
|
m->setEnabled(true);
|
|
m->setTitle(tr("&Views"));
|
|
mdebug->addMenu(viewsMenu, Core::Constants::G_DEFAULT_THREE);
|
|
|
|
m_toggleLockedAction = new QAction(tr("Locked"), this);
|
|
m_toggleLockedAction->setCheckable(true);
|
|
m_toggleLockedAction->setChecked(true);
|
|
connect(m_toggleLockedAction, SIGNAL(toggled(bool)),
|
|
m_manager->mainWindow(), SLOT(setLocked(bool)));
|
|
foreach (QDockWidget *dockWidget, m_manager->mainWindow()->dockWidgets()) {
|
|
cmd = am->registerAction(dockWidget->toggleViewAction(),
|
|
"Debugger." + dockWidget->objectName(), debuggercontext);
|
|
viewsMenu->addAction(cmd);
|
|
//m->addAction(dockWidget->toggleViewAction());
|
|
}
|
|
m->addSeparator();
|
|
m->addAction(m_toggleLockedAction);
|
|
m->addSeparator();
|
|
|
|
QAction *resetToSimpleAction = viewsMenu->menu()->addAction(tr("Reset to default layout"));
|
|
connect(resetToSimpleAction, SIGNAL(triggered()),
|
|
m_manager, SLOT(setSimpleDockWidgetArrangement()));
|
|
|
|
// FIXME:
|
|
addAutoReleasedObject(new CommonOptionsPage);
|
|
addAutoReleasedObject(new DebuggingHelperOptionPage);
|
|
foreach (Core::IOptionsPage* op, engineOptionPages)
|
|
addAutoReleasedObject(op);
|
|
addAutoReleasedObject(new DebuggerListener);
|
|
m_locationMark = 0;
|
|
|
|
|
|
//
|
|
// Debug mode setup
|
|
//
|
|
m_debugMode = new DebugMode(this);
|
|
//addAutoReleasedObject(m_debugMode);
|
|
|
|
// register factory of DebuggerRunControl
|
|
m_debuggerRunControlFactory = new DebuggerRunControlFactory(m_manager);
|
|
addAutoReleasedObject(m_debuggerRunControlFactory);
|
|
|
|
QList<int> context;
|
|
context.append(uidm->uniqueIdentifier(Core::Constants::C_EDITORMANAGER));
|
|
context.append(uidm->uniqueIdentifier(Debugger::Constants::C_GDBDEBUGGER));
|
|
context.append(uidm->uniqueIdentifier(Core::Constants::C_NAVIGATION_PANE));
|
|
m_debugMode->setContext(context);
|
|
|
|
QBoxLayout *editorHolderLayout = new QVBoxLayout;
|
|
editorHolderLayout->setMargin(0);
|
|
editorHolderLayout->setSpacing(0);
|
|
|
|
QWidget *editorAndFindWidget = new QWidget;
|
|
editorAndFindWidget->setLayout(editorHolderLayout);
|
|
editorHolderLayout->addWidget(new EditorManagerPlaceHolder(m_debugMode));
|
|
editorHolderLayout->addWidget(new FindToolBarPlaceHolder(editorAndFindWidget));
|
|
|
|
MiniSplitter *rightPaneSplitter = new MiniSplitter;
|
|
rightPaneSplitter->addWidget(editorAndFindWidget);
|
|
rightPaneSplitter->addWidget(new RightPanePlaceHolder(m_debugMode));
|
|
rightPaneSplitter->setStretchFactor(0, 1);
|
|
rightPaneSplitter->setStretchFactor(1, 0);
|
|
|
|
QWidget *centralWidget = new QWidget;
|
|
|
|
m_manager->mainWindow()->setCentralWidget(centralWidget);
|
|
|
|
MiniSplitter *splitter = new MiniSplitter;
|
|
splitter->addWidget(m_manager->mainWindow());
|
|
splitter->addWidget(new OutputPanePlaceHolder(m_debugMode, splitter));
|
|
splitter->setStretchFactor(0, 10);
|
|
splitter->setStretchFactor(1, 0);
|
|
splitter->setOrientation(Qt::Vertical);
|
|
|
|
MiniSplitter *splitter2 = new MiniSplitter;
|
|
splitter2->addWidget(new NavigationWidgetPlaceHolder(m_debugMode));
|
|
splitter2->addWidget(splitter);
|
|
splitter2->setStretchFactor(0, 0);
|
|
splitter2->setStretchFactor(1, 1);
|
|
|
|
m_debugMode->setWidget(splitter2);
|
|
|
|
Utils::StyledBar *debugToolBar = new Utils::StyledBar;
|
|
debugToolBar->setProperty("topBorder", true);
|
|
QHBoxLayout *debugToolBarLayout = new QHBoxLayout(debugToolBar);
|
|
debugToolBarLayout->setMargin(0);
|
|
debugToolBarLayout->setSpacing(0);
|
|
debugToolBarLayout->addWidget(toolButton(am->command(ProjectExplorer::Constants::DEBUG)->action()));
|
|
debugToolBarLayout->addWidget(toolButton(am->command(Constants::INTERRUPT)->action()));
|
|
debugToolBarLayout->addWidget(toolButton(am->command(Constants::NEXT)->action()));
|
|
debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEP)->action()));
|
|
debugToolBarLayout->addWidget(toolButton(am->command(Constants::STEPOUT)->action()));
|
|
debugToolBarLayout->addWidget(toolButton(am->command(Constants::OPERATE_BY_INSTRUCTION)->action()));
|
|
#ifdef USE_REVERSE_DEBUGGING
|
|
debugToolBarLayout->addWidget(new Utils::StyledSeparator);
|
|
debugToolBarLayout->addWidget(toolButton(am->command(Constants::REVERSE)->action()));
|
|
#endif
|
|
debugToolBarLayout->addWidget(new Utils::StyledSeparator);
|
|
debugToolBarLayout->addWidget(new QLabel(tr("Threads:")));
|
|
|
|
QComboBox *threadBox = new QComboBox;
|
|
threadBox->setModel(m_manager->threadsModel());
|
|
connect(threadBox, SIGNAL(activated(int)),
|
|
m_manager->threadsWindow(), SIGNAL(threadSelected(int)));
|
|
debugToolBarLayout->addWidget(threadBox);
|
|
debugToolBarLayout->addWidget(m_manager->statusLabel(), 10);
|
|
|
|
QBoxLayout *toolBarAddingLayout = new QVBoxLayout(centralWidget);
|
|
toolBarAddingLayout->setMargin(0);
|
|
toolBarAddingLayout->setSpacing(0);
|
|
toolBarAddingLayout->addWidget(rightPaneSplitter);
|
|
toolBarAddingLayout->addWidget(debugToolBar);
|
|
|
|
m_manager->setSimpleDockWidgetArrangement();
|
|
readSettings();
|
|
|
|
connect(ModeManager::instance(), SIGNAL(currentModeChanged(Core::IMode*)),
|
|
this, SLOT(onModeChanged(Core::IMode*)));
|
|
m_debugMode->widget()->setFocusProxy(EditorManager::instance());
|
|
addObject(m_debugMode);
|
|
|
|
//
|
|
// Connections
|
|
//
|
|
|
|
// ProjectExplorer
|
|
connect(sessionManager(), SIGNAL(sessionLoaded()),
|
|
m_manager, SLOT(sessionLoaded()));
|
|
connect(sessionManager(), SIGNAL(aboutToSaveSession()),
|
|
m_manager, SLOT(aboutToSaveSession()));
|
|
connect(sessionManager(), SIGNAL(aboutToUnloadSession()),
|
|
m_manager, SLOT(aboutToUnloadSession()));
|
|
|
|
// EditorManager
|
|
QObject *editorManager = core->editorManager();
|
|
connect(editorManager, SIGNAL(editorAboutToClose(Core::IEditor*)),
|
|
this, SLOT(editorAboutToClose(Core::IEditor*)));
|
|
connect(editorManager, SIGNAL(editorOpened(Core::IEditor*)),
|
|
this, SLOT(editorOpened(Core::IEditor*)));
|
|
|
|
// Application interaction
|
|
connect(m_manager, SIGNAL(currentTextEditorRequested(QString*,int*,QObject**)),
|
|
this, SLOT(queryCurrentTextEditor(QString*,int*,QObject**)));
|
|
|
|
connect(m_manager, SIGNAL(setSessionValueRequested(QString,QVariant)),
|
|
this, SLOT(setSessionValue(QString,QVariant)));
|
|
connect(m_manager, SIGNAL(sessionValueRequested(QString,QVariant*)),
|
|
this, SLOT(querySessionValue(QString,QVariant*)));
|
|
|
|
connect(m_manager, SIGNAL(resetLocationRequested()),
|
|
this, SLOT(resetLocation()));
|
|
connect(m_manager, SIGNAL(gotoLocationRequested(QString,int,bool)),
|
|
this, SLOT(gotoLocation(QString,int,bool)));
|
|
connect(m_manager, SIGNAL(stateChanged(int)),
|
|
this, SLOT(handleStateChanged(int)));
|
|
connect(m_manager, SIGNAL(previousModeRequested()),
|
|
this, SLOT(activatePreviousMode()));
|
|
connect(m_manager, SIGNAL(debugModeRequested()),
|
|
this, SLOT(activateDebugMode()));
|
|
|
|
connect(theDebuggerAction(SettingsDialog), SIGNAL(triggered()),
|
|
this, SLOT(showSettingsDialog()));
|
|
|
|
handleStateChanged(DebuggerNotReady);
|
|
return true;
|
|
}
|
|
|
|
void DebuggerPlugin::extensionsInitialized()
|
|
{
|
|
// time gdb -i mi -ex 'debuggerplugin.cpp:800' -ex r -ex q bin/qtcreator.bin
|
|
const QByteArray env = qgetenv("QTC_DEBUGGER_TEST");
|
|
//qDebug() << "EXTENSIONS INITIALIZED:" << env;
|
|
if (!env.isEmpty())
|
|
m_manager->runTest(QString::fromLocal8Bit(env));
|
|
if (m_attachRemoteParameters.attachPid || !m_attachRemoteParameters.attachCore.isEmpty())
|
|
QTimer::singleShot(0, this, SLOT(attachCmdLine()));
|
|
}
|
|
|
|
void DebuggerPlugin::attachCmdLine()
|
|
{
|
|
if (m_manager->state() != DebuggerNotReady)
|
|
return;
|
|
if (m_attachRemoteParameters.attachPid) {
|
|
m_manager->showStatusMessage(tr("Attaching to PID %1.").arg(m_attachRemoteParameters.attachPid));
|
|
const QString crashParameter =
|
|
m_attachRemoteParameters.winCrashEvent ? QString::number(m_attachRemoteParameters.winCrashEvent) : QString();
|
|
attachExternalApplication(m_attachRemoteParameters.attachPid, crashParameter);
|
|
return;
|
|
}
|
|
if (!m_attachRemoteParameters.attachCore.isEmpty()) {
|
|
m_manager->showStatusMessage(tr("Attaching to core %1.").arg(m_attachRemoteParameters.attachCore));
|
|
attachCore(m_attachRemoteParameters.attachCore, QString());
|
|
}
|
|
}
|
|
|
|
/*! Activates the previous mode when the current mode is the debug mode. */
|
|
void DebuggerPlugin::activatePreviousMode()
|
|
{
|
|
Core::ModeManager *const modeManager = ICore::instance()->modeManager();
|
|
|
|
if (modeManager->currentMode() == modeManager->mode(Constants::MODE_DEBUG)
|
|
&& !m_previousMode.isEmpty()) {
|
|
modeManager->activateMode(m_previousMode);
|
|
m_previousMode.clear();
|
|
}
|
|
}
|
|
|
|
void DebuggerPlugin::activateDebugMode()
|
|
{
|
|
ModeManager *modeManager = ModeManager::instance();
|
|
m_previousMode = QLatin1String(modeManager->currentMode()->uniqueModeName());
|
|
modeManager->activateMode(QLatin1String(MODE_DEBUG));
|
|
}
|
|
|
|
void DebuggerPlugin::queryCurrentTextEditor(QString *fileName, int *lineNumber, QObject **object)
|
|
{
|
|
EditorManager *editorManager = EditorManager::instance();
|
|
if (!editorManager)
|
|
return;
|
|
Core::IEditor *editor = editorManager->currentEditor();
|
|
ITextEditor *textEditor = qobject_cast<ITextEditor*>(editor);
|
|
if (!textEditor)
|
|
return;
|
|
if (fileName)
|
|
*fileName = textEditor->file()->fileName();
|
|
if (lineNumber)
|
|
*lineNumber = textEditor->currentLine();
|
|
if (object)
|
|
*object = textEditor->widget();
|
|
}
|
|
|
|
void DebuggerPlugin::editorOpened(Core::IEditor *editor)
|
|
{
|
|
if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) {
|
|
connect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)),
|
|
this, SLOT(requestMark(TextEditor::ITextEditor*,int)));
|
|
connect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)),
|
|
this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int)));
|
|
connect(textEditor, SIGNAL(markContextMenuRequested(TextEditor::ITextEditor*,int,QMenu*)),
|
|
this, SLOT(requestContextMenu(TextEditor::ITextEditor*,int,QMenu*)));
|
|
}
|
|
}
|
|
|
|
void DebuggerPlugin::editorAboutToClose(Core::IEditor *editor)
|
|
{
|
|
if (ITextEditor *textEditor = qobject_cast<ITextEditor *>(editor)) {
|
|
disconnect(textEditor, SIGNAL(markRequested(TextEditor::ITextEditor*,int)),
|
|
this, SLOT(requestMark(TextEditor::ITextEditor*,int)));
|
|
disconnect(editor, SIGNAL(tooltipRequested(TextEditor::ITextEditor*,QPoint,int)),
|
|
this, SLOT(showToolTip(TextEditor::ITextEditor*,QPoint,int)));
|
|
disconnect(textEditor, SIGNAL(markContextMenuRequested(TextEditor::ITextEditor*,int,QMenu*)),
|
|
this, SLOT(requestContextMenu(TextEditor::ITextEditor*,int,QMenu*)));
|
|
}
|
|
}
|
|
|
|
void DebuggerPlugin::requestContextMenu(TextEditor::ITextEditor *editor,
|
|
int lineNumber, QMenu *menu)
|
|
{
|
|
QString fileName = editor->file()->fileName();
|
|
QString position = fileName + QString(":%1").arg(lineNumber);
|
|
BreakpointData *data = m_manager->findBreakpoint(fileName, lineNumber);
|
|
|
|
if (data) {
|
|
// existing breakpoint
|
|
QAction *act = new QAction(tr("Remove Breakpoint"), menu);
|
|
act->setData(position);
|
|
connect(act, SIGNAL(triggered()),
|
|
this, SLOT(breakpointSetRemoveMarginActionTriggered()));
|
|
menu->addAction(act);
|
|
|
|
QAction *act2;
|
|
if (data->enabled)
|
|
act2 = new QAction(tr("Disable Breakpoint"), menu);
|
|
else
|
|
act2 = new QAction(tr("Enable Breakpoint"), menu);
|
|
act2->setData(position);
|
|
connect(act2, SIGNAL(triggered()),
|
|
this, SLOT(breakpointEnableDisableMarginActionTriggered()));
|
|
menu->addAction(act2);
|
|
} else {
|
|
// non-existing
|
|
QAction *act = new QAction(tr("Set Breakpoint"), menu);
|
|
act->setData(position);
|
|
connect(act, SIGNAL(triggered()),
|
|
this, SLOT(breakpointSetRemoveMarginActionTriggered()));
|
|
menu->addAction(act);
|
|
}
|
|
}
|
|
|
|
void DebuggerPlugin::breakpointSetRemoveMarginActionTriggered()
|
|
{
|
|
if (QAction *act = qobject_cast<QAction *>(sender())) {
|
|
QString str = act->data().toString();
|
|
int pos = str.lastIndexOf(':');
|
|
m_manager->toggleBreakpoint(str.left(pos), str.mid(pos + 1).toInt());
|
|
}
|
|
}
|
|
|
|
void DebuggerPlugin::breakpointEnableDisableMarginActionTriggered()
|
|
{
|
|
if (QAction *act = qobject_cast<QAction *>(sender())) {
|
|
QString str = act->data().toString();
|
|
int pos = str.lastIndexOf(':');
|
|
m_manager->toggleBreakpointEnabled(str.left(pos), str.mid(pos + 1).toInt());
|
|
}
|
|
}
|
|
|
|
void DebuggerPlugin::requestMark(TextEditor::ITextEditor *editor, int lineNumber)
|
|
{
|
|
m_manager->toggleBreakpoint(editor->file()->fileName(), lineNumber);
|
|
}
|
|
|
|
void DebuggerPlugin::showToolTip(TextEditor::ITextEditor *editor,
|
|
const QPoint &point, int pos)
|
|
{
|
|
if (!theDebuggerBoolSetting(UseToolTipsInMainEditor)
|
|
|| m_manager->state() == DebuggerNotReady)
|
|
return;
|
|
|
|
m_manager->setToolTipExpression(point, editor, pos);
|
|
}
|
|
|
|
void DebuggerPlugin::setSessionValue(const QString &name, const QVariant &value)
|
|
{
|
|
//qDebug() << "SET SESSION VALUE" << name << value;
|
|
QTC_ASSERT(sessionManager(), return);
|
|
sessionManager()->setValue(name, value);
|
|
}
|
|
|
|
void DebuggerPlugin::querySessionValue(const QString &name, QVariant *value)
|
|
{
|
|
QTC_ASSERT(sessionManager(), return);
|
|
*value = sessionManager()->value(name);
|
|
//qDebug() << "GET SESSION VALUE: " << name << value;
|
|
}
|
|
|
|
|
|
void DebuggerPlugin::setConfigValue(const QString &name, const QVariant &value)
|
|
{
|
|
QTC_ASSERT(m_debugMode, return);
|
|
settings()->setValue(name, value);
|
|
}
|
|
|
|
QVariant DebuggerPlugin::configValue(const QString &name) const
|
|
{
|
|
QTC_ASSERT(m_debugMode, return QVariant());
|
|
return settings()->value(name);
|
|
}
|
|
|
|
void DebuggerPlugin::queryConfigValue(const QString &name, QVariant *value)
|
|
{
|
|
QTC_ASSERT(m_debugMode, return);
|
|
*value = settings()->value(name);
|
|
}
|
|
|
|
void DebuggerPlugin::resetLocation()
|
|
{
|
|
//qDebug() << "RESET_LOCATION: current:" << currentTextEditor();
|
|
//qDebug() << "RESET_LOCATION: locations:" << m_locationMark;
|
|
//qDebug() << "RESET_LOCATION: stored:" << m_locationMark->editor();
|
|
delete m_locationMark;
|
|
m_locationMark = 0;
|
|
}
|
|
|
|
void DebuggerPlugin::gotoLocation(const QString &file, int line, bool setMarker)
|
|
{
|
|
TextEditor::BaseTextEditor::openEditorAt(file, line);
|
|
if (setMarker) {
|
|
resetLocation();
|
|
m_locationMark = new LocationMark(file, line);
|
|
}
|
|
}
|
|
|
|
void DebuggerPlugin::handleStateChanged(int state)
|
|
{
|
|
const bool startIsContinue = (state == InferiorStopped);
|
|
ICore *core = ICore::instance();
|
|
if (startIsContinue) {
|
|
core->addAdditionalContext(m_gdbRunningContext);
|
|
core->updateContext();
|
|
} else {
|
|
core->removeAdditionalContext(m_gdbRunningContext);
|
|
core->updateContext();
|
|
}
|
|
|
|
const bool started = state == InferiorRunning
|
|
|| state == InferiorRunningRequested
|
|
|| state == InferiorStopping
|
|
|| state == InferiorStopped;
|
|
|
|
const bool starting = state == EngineStarting;
|
|
//const bool running = state == InferiorRunning;
|
|
|
|
const bool detachable = state == InferiorStopped
|
|
&& m_manager->startParameters()->startMode != AttachCore;
|
|
|
|
m_startExternalAction->setEnabled(!started && !starting);
|
|
m_attachExternalAction->setEnabled(!started && !starting);
|
|
#ifdef Q_OS_WIN
|
|
m_attachCoreAction->setEnabled(false);
|
|
#else
|
|
m_attachCoreAction->setEnabled(!started && !starting);
|
|
#endif
|
|
m_startRemoteAction->setEnabled(!started && !starting);
|
|
m_detachAction->setEnabled(detachable);
|
|
}
|
|
|
|
void DebuggerPlugin::writeSettings() const
|
|
{
|
|
QTC_ASSERT(m_manager, return);
|
|
QTC_ASSERT(m_manager->mainWindow(), return);
|
|
|
|
QSettings *s = settings();
|
|
DebuggerSettings::instance()->writeSettings(s);
|
|
s->beginGroup(QLatin1String("DebugMode"));
|
|
m_manager->mainWindow()->saveSettings(s);
|
|
s->endGroup();
|
|
}
|
|
|
|
void DebuggerPlugin::readSettings()
|
|
{
|
|
QSettings *s = settings();
|
|
DebuggerSettings::instance()->readSettings(s);
|
|
|
|
QString defaultCommand("gdb");
|
|
#ifdef Q_OS_WIN
|
|
defaultCommand.append(".exe");
|
|
#endif
|
|
//QString defaultScript = ICore::instance()->resourcePath() +
|
|
// QLatin1String("/gdb/qt4macros");
|
|
QString defaultScript;
|
|
|
|
s->beginGroup(QLatin1String("DebugMode"));
|
|
m_manager->mainWindow()->restoreSettings(s);
|
|
m_toggleLockedAction->setChecked(m_manager->mainWindow()->isLocked());
|
|
s->endGroup();
|
|
}
|
|
|
|
void DebuggerPlugin::onModeChanged(IMode *mode)
|
|
{
|
|
// FIXME: This one gets always called, even if switching between modes
|
|
// different then the debugger mode. E.g. Welcome and Help mode and
|
|
// also on shutdown.
|
|
|
|
if (mode != m_debugMode)
|
|
return;
|
|
|
|
EditorManager *editorManager = EditorManager::instance();
|
|
if (editorManager->currentEditor())
|
|
editorManager->currentEditor()->widget()->setFocus();
|
|
}
|
|
|
|
void DebuggerPlugin::showSettingsDialog()
|
|
{
|
|
Core::ICore::instance()->showOptionsDialog(
|
|
QLatin1String(Debugger::Constants::DEBUGGER_SETTINGS_CATEGORY),
|
|
QLatin1String(Debugger::Constants::DEBUGGER_COMMON_SETTINGS_ID));
|
|
}
|
|
|
|
void DebuggerPlugin::startExternalApplication()
|
|
{
|
|
const DebuggerStartParametersPtr sp(new DebuggerStartParameters);
|
|
StartExternalDialog dlg(m_manager->mainWindow());
|
|
dlg.setExecutableFile(
|
|
configValue(_("LastExternalExecutableFile")).toString());
|
|
dlg.setExecutableArguments(
|
|
configValue(_("LastExternalExecutableArguments")).toString());
|
|
if (dlg.exec() != QDialog::Accepted)
|
|
return;
|
|
|
|
setConfigValue(_("LastExternalExecutableFile"),
|
|
dlg.executableFile());
|
|
setConfigValue(_("LastExternalExecutableArguments"),
|
|
dlg.executableArguments());
|
|
sp->executable = dlg.executableFile();
|
|
sp->startMode = StartExternal;
|
|
if (!dlg.executableArguments().isEmpty())
|
|
sp->processArgs = dlg.executableArguments().split(QLatin1Char(' '));
|
|
|
|
if (dlg.breakAtMain())
|
|
m_manager->breakByFunctionMain();
|
|
|
|
if (RunControl *runControl = m_debuggerRunControlFactory->create(sp))
|
|
ProjectExplorerPlugin::instance()->startRunControl(runControl, ProjectExplorer::Constants::DEBUGMODE);
|
|
}
|
|
|
|
void DebuggerPlugin::attachExternalApplication()
|
|
{
|
|
AttachExternalDialog dlg(m_manager->mainWindow());
|
|
if (dlg.exec() == QDialog::Accepted)
|
|
attachExternalApplication(dlg.attachPID());
|
|
}
|
|
|
|
void DebuggerPlugin::attachExternalApplication(qint64 pid, const QString &crashParameter)
|
|
{
|
|
if (pid == 0) {
|
|
QMessageBox::warning(m_manager->mainWindow(), tr("Warning"), tr("Cannot attach to PID 0"));
|
|
return;
|
|
}
|
|
const DebuggerStartParametersPtr sp(new DebuggerStartParameters);
|
|
sp->attachPID = pid;
|
|
sp->crashParameter = crashParameter;
|
|
sp->startMode = crashParameter.isEmpty() ? AttachExternal : AttachCrashedExternal;
|
|
if (RunControl *runControl = m_debuggerRunControlFactory->create(sp))
|
|
ProjectExplorerPlugin::instance()->startRunControl(runControl, ProjectExplorer::Constants::DEBUGMODE);
|
|
}
|
|
|
|
void DebuggerPlugin::attachCore()
|
|
{
|
|
AttachCoreDialog dlg(m_manager->mainWindow());
|
|
dlg.setExecutableFile(
|
|
configValue(_("LastExternalExecutableFile")).toString());
|
|
dlg.setCoreFile(
|
|
configValue(_("LastExternalCoreFile")).toString());
|
|
if (dlg.exec() != QDialog::Accepted)
|
|
return;
|
|
setConfigValue(_("LastExternalExecutableFile"),
|
|
dlg.executableFile());
|
|
setConfigValue(_("LastExternalCoreFile"),
|
|
dlg.coreFile());
|
|
attachCore(dlg.coreFile(), dlg.executableFile());
|
|
}
|
|
|
|
void DebuggerPlugin::attachCore(const QString &core, const QString &exe)
|
|
{
|
|
const DebuggerStartParametersPtr sp(new DebuggerStartParameters);
|
|
sp->executable = exe;
|
|
sp->coreFile = core;
|
|
sp->startMode = AttachCore;
|
|
if (RunControl *runControl = m_debuggerRunControlFactory->create(sp))
|
|
ProjectExplorerPlugin::instance()->
|
|
startRunControl(runControl, ProjectExplorer::Constants::DEBUGMODE);
|
|
}
|
|
|
|
void DebuggerPlugin::startRemoteApplication()
|
|
{
|
|
const DebuggerStartParametersPtr sp(new DebuggerStartParameters);
|
|
StartRemoteDialog dlg(m_manager->mainWindow());
|
|
QStringList arches;
|
|
arches.append(_("i386:x86-64:intel"));
|
|
arches.append(_("i386"));
|
|
QString lastUsed = configValue(_("LastRemoteArchitecture")).toString();
|
|
if (!arches.contains(lastUsed))
|
|
arches.prepend(lastUsed);
|
|
dlg.setRemoteArchitectures(arches);
|
|
dlg.setRemoteChannel(
|
|
configValue(_("LastRemoteChannel")).toString());
|
|
dlg.setLocalExecutable(
|
|
configValue(_("LastLocalExecutable")).toString());
|
|
dlg.setDebugger(configValue(_("LastDebugger")).toString());
|
|
dlg.setRemoteArchitecture(lastUsed);
|
|
dlg.setServerStartScript(
|
|
configValue(_("LastServerStartScript")).toString());
|
|
dlg.setUseServerStartScript(
|
|
configValue(_("LastUseServerStartScript")).toBool());
|
|
dlg.setSysRoot(configValue(_("LastSysroot")).toString());
|
|
if (dlg.exec() != QDialog::Accepted)
|
|
return;
|
|
setConfigValue(_("LastRemoteChannel"), dlg.remoteChannel());
|
|
setConfigValue(_("LastLocalExecutable"), dlg.localExecutable());
|
|
setConfigValue(_("LastDebugger"), dlg.debugger());
|
|
setConfigValue(_("LastRemoteArchitecture"), dlg.remoteArchitecture());
|
|
setConfigValue(_("LastServerStartScript"), dlg.serverStartScript());
|
|
setConfigValue(_("LastUseServerStartScript"), dlg.useServerStartScript());
|
|
setConfigValue(_("LastSysroot"), dlg.sysRoot());
|
|
sp->remoteChannel = dlg.remoteChannel();
|
|
sp->remoteArchitecture = dlg.remoteArchitecture();
|
|
sp->executable = dlg.localExecutable();
|
|
sp->debuggerCommand = dlg.debugger();
|
|
sp->startMode = StartRemote;
|
|
if (dlg.useServerStartScript())
|
|
sp->serverStartScript = dlg.serverStartScript();
|
|
sp->sysRoot = dlg.sysRoot();
|
|
|
|
if (RunControl *runControl = m_debuggerRunControlFactory->create(sp))
|
|
ProjectExplorerPlugin::instance()
|
|
->startRunControl(runControl, ProjectExplorer::Constants::DEBUGMODE);
|
|
}
|
|
|
|
#include "debuggerplugin.moc"
|
|
|
|
Q_EXPORT_PLUGIN(DebuggerPlugin)
|