summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@nokia.com>2009-01-15 14:47:14 +0100
committerOswald Buddenhagen <oswald.buddenhagen@nokia.com>2009-01-15 14:58:54 +0100
commit20eb6a1b82f2a5b88d15cf438420789d81d1704b (patch)
tree3b6813938cecb49e2ca48d9da3cec7075ff011a4
parent72b6a105b7b01f29bdce06709e5029ed6748364a (diff)
downloadqt-creator-20eb6a1b82f2a5b88d15cf438420789d81d1704b.tar.gz
user gdb's --tty option for collecting app output
-rw-r--r--src/plugins/debugger/debugger.pro2
-rw-r--r--src/plugins/debugger/gdbengine.cpp39
-rw-r--r--src/plugins/debugger/gdbengine.h7
-rw-r--r--src/plugins/debugger/outputcollector.cpp179
-rw-r--r--src/plugins/debugger/outputcollector.h91
5 files changed, 300 insertions, 18 deletions
diff --git a/src/plugins/debugger/debugger.pro b/src/plugins/debugger/debugger.pro
index 80521765fe..469ea34601 100644
--- a/src/plugins/debugger/debugger.pro
+++ b/src/plugins/debugger/debugger.pro
@@ -30,6 +30,7 @@ HEADERS += attachexternaldialog.h \
imports.h \
moduleshandler.h \
moduleswindow.h \
+ outputcollector.h \
procinterrupt.h \
registerhandler.h \
registerwindow.h \
@@ -56,6 +57,7 @@ SOURCES += attachexternaldialog.cpp \
gdbmi.cpp \
moduleshandler.cpp \
moduleswindow.cpp \
+ outputcollector.cpp \
procinterrupt.cpp \
registerhandler.cpp \
registerwindow.cpp \
diff --git a/src/plugins/debugger/gdbengine.cpp b/src/plugins/debugger/gdbengine.cpp
index b979271734..78d2522639 100644
--- a/src/plugins/debugger/gdbengine.cpp
+++ b/src/plugins/debugger/gdbengine.cpp
@@ -252,6 +252,7 @@ void GdbEngine::init()
m_pendingRequests = 0;
m_gdbVersion = 100;
m_shared = 0;
+ m_outputCodec = QTextCodec::codecForLocale();
m_oldestAcceptableToken = -1;
@@ -266,6 +267,8 @@ void GdbEngine::init()
SLOT(exitDebugger()));
// Output
+ connect(&m_outputCollector, SIGNAL(byteDelivery(QByteArray)),
+ SLOT(readDebugeeOutput(QByteArray)));
connect(this, SIGNAL(gdbResponseAvailable()),
this, SLOT(handleResponse()), Qt::QueuedConnection);
@@ -355,6 +358,12 @@ static void skipTerminator(const char *&from, const char *to)
skipSpaces(from, to);
}
+void GdbEngine::readDebugeeOutput(const QByteArray &data)
+{
+ emit applicationOutputAvailable(m_outputCodec->toUnicode(
+ data.constData(), data.length(), &m_outputCodecState));
+}
+
// called asyncronously as response to Gdb stdout output in
// gdbResponseAvailable()
void GdbEngine::handleResponse()
@@ -407,22 +416,6 @@ void GdbEngine::handleResponse()
break;
}
- if (token == -1 && *from != '&' && *from != '~' && *from != '*') {
- // FIXME: On Linux the application's std::out is merged in here.
- // High risk of falsely interpreting this as MI output.
- // We assume that we _always_ use tokens, so not finding a token
- // is a positive indication for the presence of application output.
- QString s;
- while (from != to && *from != '\n')
- s += *from++;
- //qDebug() << "UNREQUESTED DATA " << s << " TAKEN AS APPLICATION OUTPUT";
- //s += '\n';
-
- m_inbuffer = QByteArray(from, to - from);
- emit applicationOutputAvailable(s);
- continue;
- }
-
// next char decides kind of record
const char c = *from++;
//qDebug() << "CODE:" << c;
@@ -590,8 +583,7 @@ static void fixMac(QByteArray &out)
void GdbEngine::readGdbStandardError()
{
- QByteArray err = m_gdbProc.readAllStandardError();
- emit applicationOutputAvailable(err);
+ qWarning() << "Unexpected gdb stderr:" << m_gdbProc.readAllStandardError();
}
void GdbEngine::readGdbStandardOutput()
@@ -1484,6 +1476,7 @@ void GdbEngine::exitDebugger()
m_varToType.clear();
m_dataDumperState = DataDumperUninitialized;
m_shared = 0;
+ m_outputCollector.shutdown();
//q->settings()->m_debugDumpers = false;
}
@@ -1506,6 +1499,15 @@ bool GdbEngine::startDebugger()
return false;
}
+ if (!m_outputCollector.listen()) {
+ QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
+ tr("Cannot set up communication with child process: %1")
+ .arg(m_outputCollector.errorString()));
+ return false;
+ }
+
+ gdbArgs.prepend(QLatin1String("--tty=") + m_outputCollector.serverName());
+
//gdbArgs.prepend(QLatin1String("--quiet"));
gdbArgs.prepend(QLatin1String("mi"));
gdbArgs.prepend(QLatin1String("-i"));
@@ -1533,6 +1535,7 @@ bool GdbEngine::startDebugger()
if (m_gdbProc.state() != QProcess::Running) {
QMessageBox::critical(q->mainWindow(), tr("Debugger Startup Failure"),
tr("Cannot start debugger: %1").arg(m_gdbProc.errorString()));
+ m_outputCollector.shutdown();
return false;
}
diff --git a/src/plugins/debugger/gdbengine.h b/src/plugins/debugger/gdbengine.h
index abf920c91e..60493a53e5 100644
--- a/src/plugins/debugger/gdbengine.h
+++ b/src/plugins/debugger/gdbengine.h
@@ -36,6 +36,7 @@
#include "idebuggerengine.h"
#include "gdbmi.h"
+#include "outputcollector.h"
#include <QtCore/QByteArray>
#include <QtCore/QHash>
@@ -43,6 +44,7 @@
#include <QtCore/QObject>
#include <QtCore/QProcess>
#include <QtCore/QPoint>
+#include <QtCore/QTextCodec>
#include <QtCore/QVariant>
QT_BEGIN_NAMESPACE
@@ -173,6 +175,7 @@ private slots:
void gdbProcError(QProcess::ProcessError error);
void readGdbStandardOutput();
void readGdbStandardError();
+ void readDebugeeOutput(const QByteArray &data);
private:
int terminationIndex(const QByteArray &buffer, int &length);
@@ -190,6 +193,10 @@ private:
void handleQueryPwd(const GdbResultRecord &response);
void handleQuerySources(const GdbResultRecord &response);
+ OutputCollector m_outputCollector;
+ QTextCodec *m_outputCodec;
+ QTextCodec::ConverterState m_outputCodecState;
+
QByteArray m_inbuffer;
QProcess m_gdbProc;
diff --git a/src/plugins/debugger/outputcollector.cpp b/src/plugins/debugger/outputcollector.cpp
new file mode 100644
index 0000000000..9efb18c95a
--- /dev/null
+++ b/src/plugins/debugger/outputcollector.cpp
@@ -0,0 +1,179 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception
+** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+
+#include "outputcollector.h"
+
+#ifdef Q_OS_WIN
+
+#include <QtNetwork/QLocalServer>
+#include <QtNetwork/QLocalSocket>
+
+#else
+
+#include <QtCore/QFile>
+#include <QtCore/QSocketNotifier>
+#include <QtCore/QTemporaryFile>
+#include <QtCore/QVarLengthArray>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#endif
+
+namespace Debugger {
+namespace Internal {
+
+OutputCollector::OutputCollector(QObject *parent)
+ : QObject(parent)
+{
+#ifdef Q_OS_WIN
+ m_server = 0;
+ m_socket = 0;
+#endif
+}
+
+OutputCollector::~OutputCollector()
+{
+ shutdown();
+}
+
+bool OutputCollector::listen()
+{
+#ifdef Q_OS_WIN
+ if (m_server)
+ return m_server->isListening();
+ m_server = new QLocalServer(this);
+ connect(m_server, SIGNAL(newConnection()), SLOT(newConnectionAvailable()));
+ return m_server->listen(QLatin1String("creator-") + QCoreApplication::applicationPid()); // XXX how to make that secure?
+#else
+ if (!m_serverPath.isEmpty())
+ return true;
+ QByteArray codedServerPath;
+ forever {
+ {
+ QTemporaryFile tf;
+ if (!tf.open()) {
+ m_errorString = tr("Cannot create temporary file: %2").arg(tf.errorString());
+ m_serverPath.clear();
+ return false;
+ }
+ m_serverPath = tf.fileName();
+ }
+ // By now the temp file was deleted again
+ codedServerPath = QFile::encodeName(m_serverPath);
+ if (!::mkfifo(codedServerPath.constData(), 0600))
+ break;
+ if (errno != EEXIST) {
+ m_errorString = tr("Cannot create FiFo %1: %2").arg(m_serverPath, strerror(errno));
+ m_serverPath.clear();
+ return false;
+ }
+ }
+ if ((m_serverFd = ::open(codedServerPath.constData(), O_RDONLY|O_NONBLOCK)) < 0) {
+ m_errorString = tr("Cannot open FiFo %1: %2").arg(m_serverPath, strerror(errno));
+ m_serverPath.clear();
+ return false;
+ }
+ m_serverNotifier = new QSocketNotifier(m_serverFd, QSocketNotifier::Read, this);
+ connect(m_serverNotifier, SIGNAL(activated(int)), SLOT(bytesAvailable()));
+ return true;
+#endif
+}
+
+void OutputCollector::shutdown()
+{
+#ifdef Q_OS_WIN
+ delete m_server; // Deletes socket as well (QObject parent)
+ m_server = 0;
+ m_socket = 0;
+#else
+ if (!m_serverPath.isEmpty()) {
+ ::close(m_serverFd);
+ ::unlink(QFile::encodeName(m_serverPath).constData());
+ delete m_serverNotifier;
+ m_serverPath.clear();
+ }
+#endif
+}
+
+QString OutputCollector::errorString() const
+{
+#ifdef Q_OS_WIN
+ return m_socket ? m_socket->errorString() : m_server->errorString();
+#else
+ return m_errorString;
+#endif
+}
+
+QString OutputCollector::serverName() const
+{
+#ifdef Q_OS_WIN
+ return m_server->fullServerPath();
+#else
+ return m_serverPath;
+#endif
+}
+
+#ifdef Q_OS_WIN
+void OutputCollector::newConnectionAvailable()
+{
+ if (m_socket)
+ return;
+ m_socket = m_server->nextPendingConnection();
+ connect(m_socket, SIGNAL(bytesAvailable()), SLOT(bytesAvailable()));
+}
+#endif
+
+void OutputCollector::bytesAvailable()
+{
+#ifdef Q_OS_WIN
+ emit byteDelivery(m_socket->readAll());
+#else
+ size_t nbytes = 0;
+ if (::ioctl(m_serverFd, FIONREAD, (char *) &nbytes) < 0)
+ return;
+ QVarLengthArray<char, 8192> buff(nbytes);
+ if (::read(m_serverFd, buff.data(), nbytes) != (int)nbytes)
+ return;
+ if (nbytes) // Skip EOF notifications
+ emit byteDelivery(QByteArray::fromRawData(buff.data(), nbytes));
+#endif
+}
+
+} // namespace Internal
+} // namespace Debugger
diff --git a/src/plugins/debugger/outputcollector.h b/src/plugins/debugger/outputcollector.h
new file mode 100644
index 0000000000..b84a1b3610
--- /dev/null
+++ b/src/plugins/debugger/outputcollector.h
@@ -0,0 +1,91 @@
+/***************************************************************************
+**
+** This file is part of Qt Creator
+**
+** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+**
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+**
+** Non-Open Source Usage
+**
+** Licensees may use this file in accordance with the Qt Beta Version
+** License Agreement, Agreement version 2.2 provided with the Software or,
+** alternatively, in accordance with the terms contained in a written
+** agreement between you and Nokia.
+**
+** GNU General Public License Usage
+**
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the packaging
+** of this file. Please review the following information to ensure GNU
+** General Public Licensing requirements will be met:
+**
+** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt GPL Exception
+** version 1.3, included in the file GPL_EXCEPTION.txt in this package.
+**
+***************************************************************************/
+
+#ifndef OUTPUT_COLLECTOR_H
+#define OUTPUT_COLLECTOR_H
+
+#include <QtCore/QByteArray>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+class QLocalServer;
+class QLocalSocket;
+class QSocketNotifier;
+QT_END_NAMESPACE
+
+namespace Debugger {
+namespace Internal {
+
+///////////////////////////////////////////////////////////////////////
+//
+// OutputCollector
+//
+///////////////////////////////////////////////////////////////////////
+
+class OutputCollector : public QObject
+{
+ Q_OBJECT
+
+public:
+ OutputCollector(QObject *parent = 0);
+ ~OutputCollector();
+ bool listen();
+ void shutdown();
+ QString serverName() const;
+ QString errorString() const;
+
+signals:
+ void byteDelivery(const QByteArray &data);
+
+private slots:
+ void bytesAvailable();
+#ifdef Q_OS_WIN
+ void newConnectionAvailable();
+#endif
+
+private:
+#ifdef Q_OS_WIN
+ QLocalServer *m_server;
+ QLocalSocket *m_socket;
+#else
+ QString m_serverPath;
+ int m_serverFd;
+ QSocketNotifier *m_serverNotifier;
+ QString m_errorString;
+#endif
+};
+
+} // namespace Internal
+} // namespace Debugger
+
+#endif // OUTPUT_COLLECTOR_H