forked from qt-creator/qt-creator
Debugger: Enable attaching with -client option.
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>
This commit is contained in:
@@ -134,40 +134,6 @@ static inline QString msgSendArgumentFailed()
|
|||||||
return QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. It appears to be not responding.");
|
return QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. It appears to be not responding.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare a remote argument: If it is a relative file, add the current directory
|
|
||||||
// since the the central instance might be running in a different directory.
|
|
||||||
|
|
||||||
static inline QString prepareRemoteArgument(const QString &a)
|
|
||||||
{
|
|
||||||
QFileInfo fi(a);
|
|
||||||
if (!fi.exists())
|
|
||||||
return a;
|
|
||||||
if (fi.isRelative())
|
|
||||||
return fi.absoluteFilePath();
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the arguments to an already running instance of Qt Creator
|
|
||||||
static bool sendArguments(SharedTools::QtSingleApplication &app, const QStringList &arguments)
|
|
||||||
{
|
|
||||||
if (!arguments.empty()) {
|
|
||||||
// Send off arguments
|
|
||||||
const QStringList::const_iterator acend = arguments.constEnd();
|
|
||||||
for (QStringList::const_iterator it = arguments.constBegin(); it != acend; ++it) {
|
|
||||||
if (!app.sendMessage(prepareRemoteArgument(*it))) {
|
|
||||||
displayError(msgSendArgumentFailed());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Special empty argument means: Show and raise (the slot just needs to be triggered)
|
|
||||||
if (!app.sendMessage(QString())) {
|
|
||||||
displayError(msgSendArgumentFailed());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline QStringList getPluginPaths()
|
static inline QStringList getPluginPaths()
|
||||||
{
|
{
|
||||||
QStringList rc;
|
QStringList rc;
|
||||||
@@ -287,8 +253,13 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bool isFirstInstance = !app.isRunning();
|
const bool isFirstInstance = !app.isRunning();
|
||||||
if (!isFirstInstance && foundAppOptions.contains(QLatin1String(CLIENT_OPTION)))
|
if (!isFirstInstance && foundAppOptions.contains(QLatin1String(CLIENT_OPTION))) {
|
||||||
return sendArguments(app, pluginManager.arguments()) ? 0 : -1;
|
if (!app.sendMessage(pluginManager.serializedArguments())) {
|
||||||
|
displayError(msgSendArgumentFailed());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
pluginManager.loadPlugins();
|
pluginManager.loadPlugins();
|
||||||
if (coreplugin->hasError()) {
|
if (coreplugin->hasError()) {
|
||||||
@@ -311,9 +282,10 @@ int main(int argc, char **argv)
|
|||||||
// Silently fallback to unconnected instances for any subsequent
|
// Silently fallback to unconnected instances for any subsequent
|
||||||
// instances.
|
// instances.
|
||||||
app.initialize();
|
app.initialize();
|
||||||
QObject::connect(&app, SIGNAL(messageReceived(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString)));
|
QObject::connect(&app, SIGNAL(messageReceived(QString)),
|
||||||
|
&pluginManager, SLOT(remoteArguments(QString)));
|
||||||
}
|
}
|
||||||
QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString)));
|
QObject::connect(&app, SIGNAL(fileOpenRequest(QString)), coreplugin->plugin(), SLOT(fileOpenRequest(QString)));
|
||||||
|
|
||||||
// Do this after the event loop has started
|
// Do this after the event loop has started
|
||||||
QTimer::singleShot(100, &pluginManager, SLOT(startTests()));
|
QTimer::singleShot(100, &pluginManager, SLOT(startTests()));
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ public:
|
|||||||
virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;
|
virtual bool initialize(const QStringList &arguments, QString *errorString) = 0;
|
||||||
virtual void extensionsInitialized() = 0;
|
virtual void extensionsInitialized() = 0;
|
||||||
virtual void shutdown() { }
|
virtual void shutdown() { }
|
||||||
|
virtual void remoteCommand(const QStringList & /* options */, const QStringList & /* arguments */) { }
|
||||||
|
|
||||||
PluginSpec *pluginSpec() const;
|
PluginSpec *pluginSpec() const;
|
||||||
|
|
||||||
|
|||||||
@@ -320,6 +320,91 @@ QList<PluginSpec *> PluginManager::plugins() const
|
|||||||
return d->pluginSpecs;
|
return d->pluginSpecs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QString PluginManager::serializedArguments() const
|
||||||
|
|
||||||
|
Serialize plugin options and arguments for sending in a single string
|
||||||
|
via QtSingleApplication:
|
||||||
|
":myplugin|-option1|-option2|:arguments|argument1|argument2",
|
||||||
|
as a list of lists started by a keyword with a colon. Arguments are last.
|
||||||
|
|
||||||
|
\sa setPluginPaths()
|
||||||
|
*/
|
||||||
|
|
||||||
|
static const char argumentKeywordC[] = ":arguments";
|
||||||
|
|
||||||
|
QString PluginManager::serializedArguments() const
|
||||||
|
{
|
||||||
|
const QChar separator = QLatin1Char('|');
|
||||||
|
QString rc;
|
||||||
|
foreach (const PluginSpec *ps, plugins()) {
|
||||||
|
if (!ps->arguments().isEmpty()) {
|
||||||
|
if (!rc.isEmpty())
|
||||||
|
rc += separator;
|
||||||
|
rc += QLatin1Char(':');
|
||||||
|
rc += ps->name();
|
||||||
|
rc += separator;
|
||||||
|
rc += ps->arguments().join(QString(separator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!d->arguments.isEmpty()) {
|
||||||
|
if (!rc.isEmpty())
|
||||||
|
rc += separator;
|
||||||
|
rc += QLatin1String(argumentKeywordC);
|
||||||
|
// If the argument appears to be a file, make it absolute
|
||||||
|
// when sending to another instance.
|
||||||
|
foreach(const QString &argument, d->arguments) {
|
||||||
|
rc += separator;
|
||||||
|
const QFileInfo fi(argument);
|
||||||
|
if (fi.exists() && fi.isRelative()) {
|
||||||
|
rc += fi.absoluteFilePath();
|
||||||
|
} else {
|
||||||
|
rc += argument;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Extract a sublist from the serialized arguments
|
||||||
|
* indicated by a keyword starting with a colon indicator:
|
||||||
|
* ":a,i1,i2,:b:i3,i4" with ":a" -> "i1,i2"
|
||||||
|
*/
|
||||||
|
static QStringList subList(const QStringList &in, const QString &key)
|
||||||
|
{
|
||||||
|
QStringList rc;
|
||||||
|
// Find keyword and copy arguments until end or next keyword
|
||||||
|
const QStringList::const_iterator inEnd = in.constEnd();
|
||||||
|
QStringList::const_iterator it = qFind(in.constBegin(), inEnd, key);
|
||||||
|
if (it != inEnd) {
|
||||||
|
const QChar nextIndicator = QLatin1Char(':');
|
||||||
|
for (++it; it != inEnd && !it->startsWith(nextIndicator); ++it)
|
||||||
|
rc.append(*it);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn PluginManager::remoteArguments(const QString &argument)
|
||||||
|
|
||||||
|
Parses the options encoded by serializedArguments() const
|
||||||
|
and passes them on to the respective plugins along with the arguments.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void PluginManager::remoteArguments(const QString &serializedArgument)
|
||||||
|
{
|
||||||
|
if (serializedArgument.isEmpty())
|
||||||
|
return;
|
||||||
|
QStringList serializedArguments = serializedArgument.split(QLatin1Char('|'));
|
||||||
|
const QStringList arguments = subList(serializedArguments, QLatin1String(argumentKeywordC));
|
||||||
|
foreach (const PluginSpec *ps, plugins()) {
|
||||||
|
if (ps->state() == PluginSpec::Running) {
|
||||||
|
const QStringList pluginOptions = subList(serializedArguments, QLatin1Char(':') + ps->name());
|
||||||
|
ps->plugin()->remoteCommand(pluginOptions, arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn bool PluginManager::parseOptions(const QStringList &args, const QMap<QString, bool> &appOptions, QMap<QString, QString> *foundAppOptions, QString *errorString)
|
\fn bool PluginManager::parseOptions(const QStringList &args, const QMap<QString, bool> &appOptions, QMap<QString, QString> *foundAppOptions, QString *errorString)
|
||||||
Takes the list of command line options in \a args and parses them.
|
Takes the list of command line options in \a args and parses them.
|
||||||
|
|||||||
@@ -108,6 +108,8 @@ public:
|
|||||||
void formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) const;
|
void formatPluginOptions(QTextStream &str, int optionIndentation, int descriptionIndentation) const;
|
||||||
void formatPluginVersions(QTextStream &str) const;
|
void formatPluginVersions(QTextStream &str) const;
|
||||||
|
|
||||||
|
QString serializedArguments() const;
|
||||||
|
|
||||||
bool runningTests() const;
|
bool runningTests() const;
|
||||||
QString testDataDirectory() const;
|
QString testDataDirectory() const;
|
||||||
|
|
||||||
@@ -116,6 +118,10 @@ signals:
|
|||||||
void aboutToRemoveObject(QObject *obj);
|
void aboutToRemoveObject(QObject *obj);
|
||||||
|
|
||||||
void pluginsChanged();
|
void pluginsChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void remoteArguments(const QString &serializedArguments);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void startTests();
|
void startTests();
|
||||||
|
|
||||||
|
|||||||
@@ -87,16 +87,15 @@ void CorePlugin::extensionsInitialized()
|
|||||||
m_mainWindow->extensionsInitialized();
|
m_mainWindow->extensionsInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CorePlugin::remoteArgument(const QString& arg)
|
void CorePlugin::remoteCommand(const QStringList & /* options */, const QStringList &args)
|
||||||
{
|
{
|
||||||
// An empty argument is sent to trigger activation
|
m_mainWindow->openFiles(args);
|
||||||
// of the window via QtSingleApplication. It should be
|
m_mainWindow->activateWindow();
|
||||||
// the last of a sequence.
|
}
|
||||||
if (arg.isEmpty()) {
|
|
||||||
m_mainWindow->activateWindow();
|
void CorePlugin::fileOpenRequest(const QString &f)
|
||||||
} else {
|
{
|
||||||
m_mainWindow->openFiles(QStringList(arg));
|
remoteCommand(QStringList(), QStringList(f));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CorePlugin::shutdown()
|
void CorePlugin::shutdown()
|
||||||
|
|||||||
@@ -49,9 +49,10 @@ public:
|
|||||||
virtual bool initialize(const QStringList &arguments, QString *errorMessage = 0);
|
virtual bool initialize(const QStringList &arguments, QString *errorMessage = 0);
|
||||||
virtual void extensionsInitialized();
|
virtual void extensionsInitialized();
|
||||||
virtual void shutdown();
|
virtual void shutdown();
|
||||||
|
virtual void remoteCommand(const QStringList & /* options */, const QStringList &args);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void remoteArgument(const QString&);
|
void fileOpenRequest(const QString&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void parseArguments(const QStringList & arguments);
|
void parseArguments(const QStringList & arguments);
|
||||||
|
|||||||
@@ -812,8 +812,14 @@ void CdbDebugEnginePrivate::processCreatedAttached(ULONG64 processHandle, ULONG6
|
|||||||
if (!crashParameter.isEmpty()) {
|
if (!crashParameter.isEmpty()) {
|
||||||
ULONG64 evtNr = crashParameter.toULongLong();
|
ULONG64 evtNr = crashParameter.toULongLong();
|
||||||
const HRESULT hr = m_cif.debugControl->SetNotifyEventHandle(evtNr);
|
const HRESULT hr = m_cif.debugControl->SetNotifyEventHandle(evtNr);
|
||||||
if (FAILED(hr))
|
// Unless QtCreator is spawned by the debugger and inherits the handles,
|
||||||
|
// the event handling does not work reliably
|
||||||
|
// (that is, the crash event is not delivered).
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
QTimer::singleShot(0, m_engine, SLOT(slotBreakAttachToCrashed()));
|
||||||
|
} else {
|
||||||
m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(msgComFailed("SetNotifyEventHandle", hr)));
|
m_engine->warning(QString::fromLatin1("Handshake failed on event #%1: %2").arg(evtNr).arg(msgComFailed("SetNotifyEventHandle", hr)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
|
m_engine->setState(InferiorRunning, Q_FUNC_INFO, __LINE__);
|
||||||
@@ -1234,6 +1240,17 @@ bool CdbDebugEnginePrivate::interruptInterferiorProcess(QString *errorMessage)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CdbDebugEngine::slotBreakAttachToCrashed()
|
||||||
|
{
|
||||||
|
// Force a break when attaching to crashed process (if Creator was not spawned
|
||||||
|
// from handler).
|
||||||
|
if (state() != InferiorStopped) {
|
||||||
|
manager()->showDebuggerOutput(LogMisc, QLatin1String("Forcing break..."));
|
||||||
|
m_d->m_dumper->disable();
|
||||||
|
interruptInferior();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CdbDebugEngine::interruptInferior()
|
void CdbDebugEngine::interruptInferior()
|
||||||
{
|
{
|
||||||
if (!m_d->m_hDebuggeeProcess || !m_d->isDebuggeeRunning())
|
if (!m_d->m_hDebuggeeProcess || !m_d->isDebuggeeRunning())
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ private slots:
|
|||||||
void slotConsoleStubStarted();
|
void slotConsoleStubStarted();
|
||||||
void slotConsoleStubError(const QString &msg);
|
void slotConsoleStubError(const QString &msg);
|
||||||
void slotConsoleStubTerminated();
|
void slotConsoleStubTerminated();
|
||||||
|
void slotBreakAttachToCrashed();
|
||||||
void warning(const QString &w);
|
void warning(const QString &w);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -505,14 +505,19 @@ bool DebuggingHelperOptionPage::matches(const QString &s) const
|
|||||||
//
|
//
|
||||||
///////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
DebuggerPlugin::AttachRemoteParameters::AttachRemoteParameters() :
|
||||||
|
attachPid(0),
|
||||||
|
winCrashEvent(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
DebuggerPlugin::DebuggerPlugin()
|
DebuggerPlugin::DebuggerPlugin()
|
||||||
: m_manager(0),
|
: m_manager(0),
|
||||||
m_debugMode(0),
|
m_debugMode(0),
|
||||||
m_locationMark(0),
|
m_locationMark(0),
|
||||||
m_gdbRunningContext(0),
|
m_gdbRunningContext(0),
|
||||||
m_cmdLineEnabledEngines(AllEngineTypes),
|
m_cmdLineEnabledEngines(AllEngineTypes),
|
||||||
m_cmdLineAttachPid(0),
|
|
||||||
m_cmdLineWinCrashEvent(0),
|
|
||||||
m_toggleLockedAction(0)
|
m_toggleLockedAction(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@@ -555,9 +560,10 @@ static QString msgInvalidNumericParameter(const QString &a, const QString &numbe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
bool DebuggerPlugin::parseArgument(QStringList::const_iterator &it,
|
static bool parseArgument(QStringList::const_iterator &it,
|
||||||
const QStringList::const_iterator &cend,
|
const QStringList::const_iterator &cend,
|
||||||
QString *errorMessage)
|
DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters,
|
||||||
|
unsigned *enabledEngines, QString *errorMessage)
|
||||||
{
|
{
|
||||||
const QString &option = *it;
|
const QString &option = *it;
|
||||||
// '-debug <pid>'
|
// '-debug <pid>'
|
||||||
@@ -568,10 +574,10 @@ bool DebuggerPlugin::parseArgument(QStringList::const_iterator &it,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool ok;
|
bool ok;
|
||||||
m_cmdLineAttachPid = it->toULongLong(&ok);
|
attachRemoteParameters->attachPid = it->toULongLong(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
m_cmdLineAttachPid = 0;
|
attachRemoteParameters->attachPid = 0;
|
||||||
m_cmdLineAttachCore = *it;
|
attachRemoteParameters->attachCore = *it;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -584,7 +590,7 @@ bool DebuggerPlugin::parseArgument(QStringList::const_iterator &it,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool ok;
|
bool ok;
|
||||||
m_cmdLineWinCrashEvent = it->toULongLong(&ok);
|
attachRemoteParameters->winCrashEvent = it->toULongLong(&ok);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
*errorMessage = msgInvalidNumericParameter(option, *it);
|
*errorMessage = msgInvalidNumericParameter(option, *it);
|
||||||
return false;
|
return false;
|
||||||
@@ -593,40 +599,55 @@ bool DebuggerPlugin::parseArgument(QStringList::const_iterator &it,
|
|||||||
}
|
}
|
||||||
// engine disabling
|
// engine disabling
|
||||||
if (option == QLatin1String("-disable-cdb")) {
|
if (option == QLatin1String("-disable-cdb")) {
|
||||||
m_cmdLineEnabledEngines &= ~CdbEngineType;
|
*enabledEngines &= ~Debugger::CdbEngineType;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (option == QLatin1String("-disable-gdb")) {
|
if (option == QLatin1String("-disable-gdb")) {
|
||||||
m_cmdLineEnabledEngines &= ~GdbEngineType;
|
*enabledEngines &= ~Debugger::GdbEngineType;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (option == QLatin1String("-disable-sdb")) {
|
if (option == QLatin1String("-disable-sdb")) {
|
||||||
m_cmdLineEnabledEngines &= ~ScriptEngineType;
|
*enabledEngines &= ~Debugger::ScriptEngineType;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
*errorMessage = tr("Invalid debugger option: %1").arg(option);
|
*errorMessage = DebuggerPlugin::tr("Invalid debugger option: %1").arg(option);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DebuggerPlugin::parseArguments(const QStringList &args, QString *errorMessage)
|
static bool parseArguments(const QStringList &args,
|
||||||
|
DebuggerPlugin::AttachRemoteParameters *attachRemoteParameters,
|
||||||
|
unsigned *enabledEngines, QString *errorMessage)
|
||||||
{
|
{
|
||||||
const QStringList::const_iterator cend = args.constEnd();
|
const QStringList::const_iterator cend = args.constEnd();
|
||||||
for (QStringList::const_iterator it = args.constBegin(); it != cend; ++it)
|
for (QStringList::const_iterator it = args.constBegin(); it != cend; ++it)
|
||||||
if (!parseArgument(it, cend, errorMessage))
|
if (!parseArgument(it, cend, attachRemoteParameters, enabledEngines, errorMessage))
|
||||||
return false;
|
return false;
|
||||||
if (Debugger::Constants::Internal::debug)
|
if (Debugger::Constants::Internal::debug)
|
||||||
qDebug().nospace() << args << "engines=0x"
|
qDebug().nospace() << args << "engines=0x"
|
||||||
<< QString::number(m_cmdLineEnabledEngines, 16)
|
<< QString::number(*enabledEngines, 16)
|
||||||
<< " pid" << m_cmdLineAttachPid
|
<< " pid" << attachRemoteParameters->attachPid
|
||||||
<< " core" << m_cmdLineAttachCore << '\n';
|
<< " core" << attachRemoteParameters->attachCore << '\n';
|
||||||
return true;
|
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)
|
bool DebuggerPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
||||||
{
|
{
|
||||||
// Do not fail the whole plugin if something goes wrong here
|
// Do not fail the whole plugin if something goes wrong here
|
||||||
if (!parseArguments(arguments, errorMessage)) {
|
if (!parseArguments(arguments, &m_attachRemoteParameters, &m_cmdLineEnabledEngines, errorMessage)) {
|
||||||
*errorMessage = tr("Error evaluating command line arguments: %1")
|
*errorMessage = tr("Error evaluating command line arguments: %1")
|
||||||
.arg(*errorMessage);
|
.arg(*errorMessage);
|
||||||
qWarning("%s\n", qPrintable(*errorMessage));
|
qWarning("%s\n", qPrintable(*errorMessage));
|
||||||
@@ -1000,18 +1021,25 @@ void DebuggerPlugin::extensionsInitialized()
|
|||||||
//qDebug() << "EXTENSIONS INITIALIZED:" << env;
|
//qDebug() << "EXTENSIONS INITIALIZED:" << env;
|
||||||
if (!env.isEmpty())
|
if (!env.isEmpty())
|
||||||
m_manager->runTest(QString::fromLocal8Bit(env));
|
m_manager->runTest(QString::fromLocal8Bit(env));
|
||||||
if (m_cmdLineAttachPid)
|
if (m_attachRemoteParameters.attachPid || !m_attachRemoteParameters.attachCore.isEmpty())
|
||||||
QTimer::singleShot(0, this, SLOT(attachCmdLinePid()));
|
QTimer::singleShot(0, this, SLOT(attachCmdLine()));
|
||||||
if (!m_cmdLineAttachCore.isEmpty())
|
|
||||||
QTimer::singleShot(0, this, SLOT(attachCmdLineCore()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerPlugin::attachCmdLinePid()
|
void DebuggerPlugin::attachCmdLine()
|
||||||
{
|
{
|
||||||
m_manager->showStatusMessage(tr("Attaching to PID %1.").arg(m_cmdLineAttachPid));
|
if (m_manager->state() != DebuggerNotReady)
|
||||||
const QString crashParameter =
|
return;
|
||||||
m_cmdLineWinCrashEvent ? QString::number(m_cmdLineWinCrashEvent) : QString();
|
if (m_attachRemoteParameters.attachPid) {
|
||||||
attachExternalApplication(m_cmdLineAttachPid, crashParameter);
|
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. */
|
/*! Activates the previous mode when the current mode is the debug mode. */
|
||||||
@@ -1326,12 +1354,6 @@ void DebuggerPlugin::attachExternalApplication(qint64 pid, const QString &crashP
|
|||||||
ProjectExplorerPlugin::instance()->startRunControl(runControl, ProjectExplorer::Constants::DEBUGMODE);
|
ProjectExplorerPlugin::instance()->startRunControl(runControl, ProjectExplorer::Constants::DEBUGMODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DebuggerPlugin::attachCmdLineCore()
|
|
||||||
{
|
|
||||||
m_manager->showStatusMessage(tr("Attaching to core %1.").arg(m_cmdLineAttachCore));
|
|
||||||
attachCore(m_cmdLineAttachCore, QString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void DebuggerPlugin::attachCore()
|
void DebuggerPlugin::attachCore()
|
||||||
{
|
{
|
||||||
AttachCoreDialog dlg(m_manager->mainWindow());
|
AttachCoreDialog dlg(m_manager->mainWindow());
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QAbstractItemView;
|
|
||||||
class QAction;
|
class QAction;
|
||||||
class QCursor;
|
class QCursor;
|
||||||
class QMenu;
|
class QMenu;
|
||||||
@@ -68,6 +67,15 @@ class DebuggerPlugin : public ExtensionSystem::IPlugin
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
struct AttachRemoteParameters {
|
||||||
|
AttachRemoteParameters();
|
||||||
|
|
||||||
|
quint64 attachPid;
|
||||||
|
QString attachCore;
|
||||||
|
// Event handle for attaching to crashed Windows processes.
|
||||||
|
quint64 winCrashEvent;
|
||||||
|
};
|
||||||
|
|
||||||
DebuggerPlugin();
|
DebuggerPlugin();
|
||||||
~DebuggerPlugin();
|
~DebuggerPlugin();
|
||||||
|
|
||||||
@@ -75,6 +83,7 @@ private:
|
|||||||
virtual bool initialize(const QStringList &arguments, QString *error_message);
|
virtual bool initialize(const QStringList &arguments, QString *error_message);
|
||||||
virtual void shutdown();
|
virtual void shutdown();
|
||||||
virtual void extensionsInitialized();
|
virtual void extensionsInitialized();
|
||||||
|
virtual void remoteCommand(const QStringList &options, const QStringList &arguments);
|
||||||
|
|
||||||
QVariant configValue(const QString &name) const;
|
QVariant configValue(const QString &name) const;
|
||||||
|
|
||||||
@@ -106,16 +115,11 @@ private slots:
|
|||||||
void startRemoteApplication();
|
void startRemoteApplication();
|
||||||
void attachExternalApplication();
|
void attachExternalApplication();
|
||||||
void attachCore();
|
void attachCore();
|
||||||
void attachCmdLinePid();
|
void attachCmdLine();
|
||||||
void attachCmdLineCore();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readSettings();
|
void readSettings();
|
||||||
void writeSettings() const;
|
void writeSettings() const;
|
||||||
bool parseArguments(const QStringList &args, QString *errorMessage);
|
|
||||||
inline bool parseArgument(QStringList::const_iterator &it,
|
|
||||||
const QStringList::const_iterator& end,
|
|
||||||
QString *errorMessage);
|
|
||||||
void attachExternalApplication(qint64 pid, const QString &crashParameter = QString());
|
void attachExternalApplication(qint64 pid, const QString &crashParameter = QString());
|
||||||
void attachCore(const QString &core, const QString &exeFileName);
|
void attachCore(const QString &core, const QString &exeFileName);
|
||||||
|
|
||||||
@@ -131,11 +135,9 @@ private:
|
|||||||
QString m_previousMode;
|
QString m_previousMode;
|
||||||
TextEditor::BaseTextMark *m_locationMark;
|
TextEditor::BaseTextMark *m_locationMark;
|
||||||
int m_gdbRunningContext;
|
int m_gdbRunningContext;
|
||||||
|
AttachRemoteParameters m_attachRemoteParameters;
|
||||||
unsigned m_cmdLineEnabledEngines;
|
unsigned m_cmdLineEnabledEngines;
|
||||||
quint64 m_cmdLineAttachPid;
|
|
||||||
QString m_cmdLineAttachCore;
|
|
||||||
// Event handle for attaching to crashed Windows processes.
|
|
||||||
quint64 m_cmdLineWinCrashEvent;
|
|
||||||
QAction *m_toggleLockedAction;
|
QAction *m_toggleLockedAction;
|
||||||
|
|
||||||
QAction *m_startExternalAction;
|
QAction *m_startExternalAction;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
#include <QtCore/QByteArray>
|
#include <QtCore/QByteArray>
|
||||||
#include <QtCore/QString>
|
#include <QtCore/QString>
|
||||||
#include <QtCore/QDir>
|
#include <QtCore/QDir>
|
||||||
|
#include <QtCore/QTime>
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
#include <QtGui/QPushButton>
|
#include <QtGui/QPushButton>
|
||||||
|
|
||||||
@@ -61,6 +62,7 @@ static const WCHAR *debuggerRegistryValueNameC = L"Debugger";
|
|||||||
static const WCHAR *debuggerRegistryDefaultValueNameC = L"Debugger.Default";
|
static const WCHAR *debuggerRegistryDefaultValueNameC = L"Debugger.Default";
|
||||||
|
|
||||||
static const char *linkC = "http://msdn.microsoft.com/en-us/library/cc266343.aspx";
|
static const char *linkC = "http://msdn.microsoft.com/en-us/library/cc266343.aspx";
|
||||||
|
static const char *creatorBinaryC = "qtcreator.exe";
|
||||||
|
|
||||||
static inline QString wCharToQString(const WCHAR *w) { return QString::fromUtf16(reinterpret_cast<const ushort *>(w)); }
|
static inline QString wCharToQString(const WCHAR *w) { return QString::fromUtf16(reinterpret_cast<const ushort *>(w)); }
|
||||||
#ifdef __GNUC__
|
#ifdef __GNUC__
|
||||||
@@ -343,23 +345,49 @@ static QString getProcessBaseName(DWORD pid)
|
|||||||
|
|
||||||
// ------- main modes
|
// ------- main modes
|
||||||
|
|
||||||
bool startCreatorAsDebugger(QString *errorMessage)
|
static bool waitForProcess(DWORD pid)
|
||||||
|
{
|
||||||
|
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION|READ_CONTROL|SYNCHRONIZE, false, pid);
|
||||||
|
if (handle == NULL)
|
||||||
|
return false;
|
||||||
|
const DWORD waitResult = WaitForSingleObject(handle, INFINITE);
|
||||||
|
CloseHandle(handle);
|
||||||
|
return waitResult == WAIT_OBJECT_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool startCreatorAsDebugger(bool asClient, QString *errorMessage)
|
||||||
{
|
{
|
||||||
const QString dir = QApplication::applicationDirPath();
|
const QString dir = QApplication::applicationDirPath();
|
||||||
const QString binary = dir + QLatin1String("/qtcreator.exe");
|
const QString binary = dir + QLatin1Char('/') + QLatin1String(creatorBinaryC);
|
||||||
QStringList args;
|
QStringList args;
|
||||||
|
if (asClient)
|
||||||
|
args << QLatin1String("-client");
|
||||||
args << QLatin1String("-debug") << QString::number(argProcessId)
|
args << QLatin1String("-debug") << QString::number(argProcessId)
|
||||||
<< QLatin1String("-wincrashevent") << QString::number(argWinCrashEvent);
|
<< QLatin1String("-wincrashevent") << QString::number(argWinCrashEvent);
|
||||||
if (debug)
|
if (debug)
|
||||||
qDebug() << binary << args;
|
qDebug() << binary << args;
|
||||||
QProcess p;
|
QProcess p;
|
||||||
p.setWorkingDirectory(dir);
|
p.setWorkingDirectory(dir);
|
||||||
|
QTime executionTime;
|
||||||
|
executionTime.start();
|
||||||
p.start(binary, args, QIODevice::NotOpen);
|
p.start(binary, args, QIODevice::NotOpen);
|
||||||
if (!p.waitForStarted()) {
|
if (!p.waitForStarted()) {
|
||||||
*errorMessage = QString::fromLatin1("Unable to start %1!").arg(binary);
|
*errorMessage = QString::fromLatin1("Unable to start %1!").arg(binary);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
p.waitForFinished(-1);
|
// Short execution time: indicates that -client was passed on attach to
|
||||||
|
// another running instance of Qt Creator. Keep alive as long as user
|
||||||
|
// does not close the process. If that fails, try to launch 2nd instance.
|
||||||
|
const bool waitResult = p.waitForFinished(-1);
|
||||||
|
const bool ranAsClient = asClient && (executionTime.elapsed() < 10000);
|
||||||
|
if (waitResult && p.exitStatus() == QProcess::NormalExit && ranAsClient) {
|
||||||
|
if (p.exitCode() == 0) {
|
||||||
|
waitForProcess(argProcessId);
|
||||||
|
} else {
|
||||||
|
errorMessage->clear();
|
||||||
|
return startCreatorAsDebugger(false, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -408,7 +436,8 @@ bool startDefaultDebugger(QString *errorMessage)
|
|||||||
bool chooseDebugger(QString *errorMessage)
|
bool chooseDebugger(QString *errorMessage)
|
||||||
{
|
{
|
||||||
QString defaultDebugger;
|
QString defaultDebugger;
|
||||||
const QString msg = QString::fromLatin1("The application \"%1\" (process id %2) crashed. Would you like to debug it?").arg(getProcessBaseName(argProcessId)).arg(argProcessId);
|
const QString processName = getProcessBaseName(argProcessId);
|
||||||
|
const QString msg = QString::fromLatin1("The application \"%1\" (process id %2) crashed. Would you like to debug it?").arg(processName).arg(argProcessId);
|
||||||
QMessageBox msgBox(QMessageBox::Information, QLatin1String(titleC), msg, QMessageBox::Cancel);
|
QMessageBox msgBox(QMessageBox::Information, QLatin1String(titleC), msg, QMessageBox::Cancel);
|
||||||
QPushButton *creatorButton = msgBox.addButton(QLatin1String("Debug with Qt Creator"), QMessageBox::AcceptRole);
|
QPushButton *creatorButton = msgBox.addButton(QLatin1String("Debug with Qt Creator"), QMessageBox::AcceptRole);
|
||||||
QPushButton *defaultButton = msgBox.addButton(QLatin1String("Debug with default debugger"), QMessageBox::AcceptRole);
|
QPushButton *defaultButton = msgBox.addButton(QLatin1String("Debug with default debugger"), QMessageBox::AcceptRole);
|
||||||
@@ -416,8 +445,10 @@ bool chooseDebugger(QString *errorMessage)
|
|||||||
&& !defaultDebugger.isEmpty());
|
&& !defaultDebugger.isEmpty());
|
||||||
msgBox.exec();
|
msgBox.exec();
|
||||||
if (msgBox.clickedButton() == creatorButton) {
|
if (msgBox.clickedButton() == creatorButton) {
|
||||||
// Just in case, default to standard
|
// Just in case, default to standard. Do not run as client in the unlikely case
|
||||||
if (startCreatorAsDebugger(errorMessage))
|
// Creator crashed
|
||||||
|
const bool canRunAsClient = !processName.contains(QLatin1String(creatorBinaryC), Qt::CaseInsensitive);
|
||||||
|
if (startCreatorAsDebugger(canRunAsClient, errorMessage))
|
||||||
return true;
|
return true;
|
||||||
return startDefaultDebugger(errorMessage);
|
return startDefaultDebugger(errorMessage);
|
||||||
}
|
}
|
||||||
@@ -552,7 +583,7 @@ int main(int argc, char *argv[])
|
|||||||
usage(QCoreApplication::applicationFilePath(), errorMessage);
|
usage(QCoreApplication::applicationFilePath(), errorMessage);
|
||||||
break;
|
break;
|
||||||
case ForceCreatorMode:
|
case ForceCreatorMode:
|
||||||
ex = startCreatorAsDebugger(&errorMessage) ? 0 : -1;
|
ex = startCreatorAsDebugger(true, &errorMessage) ? 0 : -1;
|
||||||
break;
|
break;
|
||||||
case ForceDefaultMode:
|
case ForceDefaultMode:
|
||||||
ex = startDefaultDebugger(&errorMessage) ? 0 : -1;
|
ex = startDefaultDebugger(&errorMessage) ? 0 : -1;
|
||||||
|
|||||||
Reference in New Issue
Block a user