From 20eb6a1b82f2a5b88d15cf438420789d81d1704b Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Thu, 15 Jan 2009 14:47:14 +0100 Subject: user gdb's --tty option for collecting app output --- src/plugins/debugger/debugger.pro | 2 + src/plugins/debugger/gdbengine.cpp | 39 +++---- src/plugins/debugger/gdbengine.h | 7 ++ src/plugins/debugger/outputcollector.cpp | 179 +++++++++++++++++++++++++++++++ src/plugins/debugger/outputcollector.h | 91 ++++++++++++++++ 5 files changed, 300 insertions(+), 18 deletions(-) create mode 100644 src/plugins/debugger/outputcollector.cpp create mode 100644 src/plugins/debugger/outputcollector.h 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 #include @@ -43,6 +44,7 @@ #include #include #include +#include #include 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 +#include + +#else + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#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 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 +#include + +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 -- cgit v1.2.1