debugger: make autotest compilable and non-locking again

Change-Id: Ia7cb9e3f4d8aabc1bcd2580dc9d03d06989df1dd
Reviewed-on: http://codereview.qt.nokia.com/265
Reviewed-by: hjk <qthjk@ovi.com>
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
This commit is contained in:
hjk
2011-05-31 12:31:00 +02:00
committed by hjk
parent f3a529fe31
commit e08b797ea5

View File

@@ -36,16 +36,11 @@
//bool checkUninitialized = true; //bool checkUninitialized = true;
bool checkUninitialized = false; bool checkUninitialized = false;
//#define DO_DEBUG 1 #define DO_DEBUG 1
//TESTED_COMPONENT=src/plugins/debugger/gdb //TESTED_COMPONENT=src/plugins/debugger/gdb
#include "gdb/gdbmi.h" #include "gdb/gdbmi.h"
#include <QtCore/QThread>
#include <QtCore/QMutex>
#include <QtCore/QSignalMapper>
#include <QtCore/QWaitCondition>
#ifdef QT_GUI_LIB #ifdef QT_GUI_LIB
#include <QtGui/QBitmap> #include <QtGui/QBitmap>
#include <QtGui/QBrush> #include <QtGui/QBrush>
@@ -97,7 +92,7 @@ bool checkUninitialized = false;
#endif #endif
#define DEBUGX(s) qDebug() << s #define DEBUGX(s) qDebug() << s
#define gettid() QString("0x%1").arg((qulonglong)(void *)currentThread(), 0, 16) #define gettid() QString("0x%1").arg((qulonglong)(void *)currentGdbWrapper(), 0, 16)
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
QString gdbBinary = "c:\\MinGw\\bin\\gdb.exe"; QString gdbBinary = "c:\\MinGw\\bin\\gdb.exe";
@@ -186,12 +181,14 @@ struct QString3
class tst_Gdb; class tst_Gdb;
class Thread : public QThread class GdbWrapper : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Thread(tst_Gdb *test); GdbWrapper(tst_Gdb *test);
~GdbWrapper();
void startup(QProcess *proc); void startup(QProcess *proc);
void run(); void run();
@@ -199,25 +196,34 @@ public:
public slots: public slots:
void readStandardOutput(); void readStandardOutput();
bool parseOutput(const QByteArray &ba);
void readStandardError(); void readStandardError();
void handleGdbStarted(); void handleGdbStarted();
void handleGdbError(QProcess::ProcessError); void handleGdbError(QProcess::ProcessError);
void handleGdbFinished(int, QProcess::ExitStatus); void handleGdbFinished(int, QProcess::ExitStatus);
void writeToGdbRequested(const QByteArray &ba)
QByteArray writeToGdbRequested(const QByteArray &ba)
{ {
DEBUG("THREAD GDB IN: " << ba); DEBUG("GDB IN: " << ba);
m_proc->write(ba); m_proc.write(ba);
m_proc->write("\n"); m_proc.write("\n");
while (true) {
m_proc.waitForReadyRead();
QByteArray output = m_proc.readAllStandardOutput();
if (parseOutput(output))
break;
}
return m_buffer;
} }
public: public:
QByteArray m_output;
QByteArray m_lastStopped; // last seen "*stopped" message QByteArray m_lastStopped; // last seen "*stopped" message
int m_line; // line extracted from last "*stopped" message int m_line; // line extracted from last "*stopped" message
QProcess *m_proc; // owned QProcess m_proc;
tst_Gdb *m_test; // not owned tst_Gdb *m_test; // not owned
QString m_errorString; QString m_errorString;
QByteArray m_buffer;
}; };
class tst_Gdb : public QObject class tst_Gdb : public QObject
@@ -232,8 +238,10 @@ public:
const QByteArray &expanded = QByteArray(), bool fancy = true); const QByteArray &expanded = QByteArray(), bool fancy = true);
void next(int n = 1); void next(int n = 1);
signals: QByteArray writeToGdb(const QByteArray &ba)
void writeToGdb(const QByteArray &ba); {
return m_gdb->writeToGdbRequested(ba);
}
private slots: private slots:
void initTestCase(); void initTestCase();
@@ -307,12 +315,9 @@ private:
QHash<QByteArray, int> m_lineForLabel; QHash<QByteArray, int> m_lineForLabel;
QByteArray m_function; QByteArray m_function;
Thread m_thread; GdbWrapper *m_gdb;
}; };
QByteArray buffer;
QSemaphore freeBytes(1);
QSemaphore usedBytes(0);
// //
// Dumpers // Dumpers
@@ -358,66 +363,82 @@ static const QByteArray specQChar(QChar ch)
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
// //
// Gdb Thread // GdbWrapper
// //
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
Thread::Thread(tst_Gdb *test) : m_proc(0), m_test(test) GdbWrapper::GdbWrapper(tst_Gdb *test) : m_test(test)
{ {
//#ifdef Q_OS_WIN qWarning() << "SETUP START\n\n";
// qDebug() << "\nTHREAD CREATED" << GetCurrentProcessId() << GetCurrentThreadId(); #ifndef Q_CC_GNU
//#else QSKIP("gdb test not applicable for compiler", SkipAll);
// qDebug() << "\nTHREAD CREATED" << getpid() << gettid(); #endif
//#endif //qDebug() << "\nRUN" << getpid() << gettid();
moveToThread(this); QStringList args;
connect(m_test, SIGNAL(writeToGdb(QByteArray)), args << QLatin1String("-i")
this, SLOT(writeToGdbRequested(QByteArray)), Qt::QueuedConnection); << QLatin1String("mi") << QLatin1String("--args")
<< qApp->applicationFilePath();
qWarning() << "Starting" << gdbBinary << args;
m_proc.start(gdbBinary, args);
if (!m_proc.waitForStarted()) {
const QString msg = QString::fromLatin1("Unable to run %1: %2")
.arg(gdbBinary, m_proc.errorString());
QSKIP(msg.toLatin1().constData(), SkipAll);
} }
void Thread::startup(QProcess *proc) connect(&m_proc, SIGNAL(error(QProcess::ProcessError)),
{
m_proc = proc;
m_proc->moveToThread(this);
connect(m_proc, SIGNAL(error(QProcess::ProcessError)),
this, SLOT(handleGdbError(QProcess::ProcessError))); this, SLOT(handleGdbError(QProcess::ProcessError)));
connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), connect(&m_proc, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(handleGdbFinished(int, QProcess::ExitStatus))); this, SLOT(handleGdbFinished(int, QProcess::ExitStatus)));
connect(m_proc, SIGNAL(started()), connect(&m_proc, SIGNAL(started()),
this, SLOT(handleGdbStarted())); this, SLOT(handleGdbStarted()));
connect(m_proc, SIGNAL(readyReadStandardOutput()), connect(&m_proc, SIGNAL(readyReadStandardOutput()),
this, SLOT(readStandardOutput())); this, SLOT(readStandardOutput()));
connect(m_proc, SIGNAL(readyReadStandardError()), connect(&m_proc, SIGNAL(readyReadStandardError()),
this, SLOT(readStandardError())); this, SLOT(readStandardError()));
start();
m_proc.write("python execfile('../../../share/qtcreator/gdbmacros/dumper.py')\n");
m_proc.write("python execfile('../../../share/qtcreator/gdbmacros/gdbmacros.py')\n");
m_proc.write("bbsetup\n");
m_proc.write("break breaker\n");
m_proc.write("handle SIGSTOP stop pass\n");
m_proc.write("run\n");
} }
void Thread::handleGdbError(QProcess::ProcessError error) GdbWrapper::~GdbWrapper()
{
}
void GdbWrapper::handleGdbError(QProcess::ProcessError error)
{ {
qDebug() << "GDB ERROR: " << error; qDebug() << "GDB ERROR: " << error;
//this->exit();
} }
void Thread::handleGdbFinished(int code, QProcess::ExitStatus st) void GdbWrapper::handleGdbFinished(int code, QProcess::ExitStatus st)
{ {
qDebug() << "GDB FINISHED: " << code << st; qDebug() << "GDB FINISHED: " << code << st;
//m_waitCondition.wakeAll();
//this->exit();
//throw 42;
} }
void Thread::readStandardOutput() void GdbWrapper::readStandardOutput()
{ {
QByteArray ba = m_proc->readAllStandardOutput(); //parseOutput(m_proc.readAllStandardOutput());
DEBUG("THREAD GDB OUT: " << ba); }
bool GdbWrapper::parseOutput(const QByteArray &ba0)
{
if (ba0.isEmpty())
return false;
QByteArray ba = ba0;
DEBUG("GDB OUT: " << ba);
// =library-loaded... // =library-loaded...
if (ba.startsWith("=")) { if (ba.startsWith("=")) {
//DEBUG("LIBRARY LOADED"); DEBUG("LIBRARY LOADED");
return; return false;
} }
if (ba.startsWith("*stopped")) { if (ba.startsWith("*stopped")) {
m_lastStopped = ba; m_lastStopped = ba;
DEBUG("THREAD GDB OUT 2: " << ba); DEBUG("GDB OUT 2: " << ba);
if (!ba.contains("func=\"breaker\"")) { if (!ba.contains("func=\"breaker\"")) {
int pos1 = ba.indexOf(",line=\"") + 7; int pos1 = ba.indexOf(",line=\"") + 7;
int pos2 = ba.indexOf("\"", pos1); int pos2 = ba.indexOf("\"", pos1);
@@ -467,61 +488,41 @@ void Thread::readStandardOutput()
// No interesting output before 'locals=...' // No interesting output before 'locals=...'
int pos = ba.indexOf("locals={iname="); int pos = ba.indexOf("locals={iname=");
if (pos == -1 && m_output.isEmpty()) if (pos == -1 && ba.isEmpty())
return; return true;
if (m_output.isEmpty()) QByteArray output;
m_output = ba.mid(pos); if (output.isEmpty())
output = ba.mid(pos);
else else
m_output += ba; output += ba;
// Up to ^done\n(gdb) // Up to ^done\n(gdb)
pos = m_output.indexOf("(gdb)"); pos = output.indexOf("(gdb)");
if (pos == -1) if (pos == -1)
return; return true;
m_output = m_output.left(pos); output = output.left(pos);
pos = m_output.indexOf("^done"); pos = output.indexOf("^done");
if (pos >= 4) if (pos >= 4)
m_output = m_output.left(pos - 4); output = output.left(pos - 4);
if (m_output.isEmpty()) if (output.isEmpty())
return; return true;
//qWarning() << "WAKE UP: " << m_output; m_buffer += output;
//qDebug() << "\n2 ABOUT TO ACQUIRE FREE "; return true;
freeBytes.acquire();
//qDebug() << "\n2 ACQUIRED FREE ";
buffer = m_output;
m_output.clear();
//m_waitCondition.wakeAll();
//qDebug() << "\n2 ABOUT TO RELEASE USED";
usedBytes.release();
//qDebug() << "\n2 RELEASED USED";
} }
void Thread::readStandardError() void GdbWrapper::readStandardError()
{ {
QByteArray ba = m_proc->readAllStandardError(); QByteArray ba = m_proc.readAllStandardError();
qDebug() << "THREAD GDB ERR: " << ba; qDebug() << "GDB ERR: " << ba;
} }
void Thread::handleGdbStarted() void GdbWrapper::handleGdbStarted()
{ {
//qDebug() << "\n\nGDB STARTED" << getpid() << gettid() << "\n\n"; //qDebug() << "\n\nGDB STARTED" << getpid() << gettid() << "\n\n";
} }
void Thread::run()
{
m_proc->write("python execfile('../../../share/qtcreator/gdbmacros/dumper.py')\n");
m_proc->write("python execfile('../../../share/qtcreator/gdbmacros/gdbmacros.py')\n");
m_proc->write("bbsetup\n");
m_proc->write("break breaker\n");
m_proc->write("handle SIGSTOP stop pass\n");
m_proc->write("run\n");
qDebug() << "\n2 THREAD RUNNING, RELEASE FREE";
freeBytes.release();
exec();
}
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
// //
@@ -530,32 +531,12 @@ void Thread::run()
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
tst_Gdb::tst_Gdb() tst_Gdb::tst_Gdb()
: m_thread(this) : m_gdb(0)
{ {
} }
void tst_Gdb::initTestCase() void tst_Gdb::initTestCase()
{ {
qWarning() << "SETUP START\n\n";
#ifndef Q_CC_GNU
QSKIP("gdb test not applicable for compiler", SkipAll);
#endif
//qDebug() << "\nTHREAD RUN" << getpid() << gettid();
QProcess *gdbProc = new QProcess;
QStringList args;
args << QLatin1String("-i")
<< QLatin1String("mi") << QLatin1String("--args")
<< qApp->applicationFilePath();
qWarning() << "Starting" << gdbBinary << args;
gdbProc->start(gdbBinary, args);
if (!gdbProc->waitForStarted()) {
const QString msg = QString::fromLatin1("Unable to run %1: %2")
.arg(gdbBinary, gdbProc->errorString());
delete gdbProc;
QSKIP(msg.toLatin1().constData(), SkipAll);
}
const QString fileName = "tst_gdb.cpp"; const QString fileName = "tst_gdb.cpp";
QFile file(fileName); QFile file(fileName);
if (!file.open(QIODevice::ReadOnly)) { if (!file.open(QIODevice::ReadOnly)) {
@@ -577,13 +558,14 @@ void tst_Gdb::initTestCase()
m_lineForLabel[QByteArray(funcName + ba.mid(7, pos - 8)).trimmed()] = i + 1; m_lineForLabel[QByteArray(funcName + ba.mid(7, pos - 8)).trimmed()] = i + 1;
} }
} }
//freeBytes.acquire();
m_thread.startup(gdbProc); Q_ASSERT(!m_gdb);
//usedBytes.release(); m_gdb = new GdbWrapper(this);
} }
void tst_Gdb::prepare(const QByteArray &function) void tst_Gdb::prepare(const QByteArray &function)
{ {
Q_ASSERT(m_gdb);
m_function = function; m_function = function;
writeToGdb("b " + function); writeToGdb("b " + function);
writeToGdb("call " + function + "()"); writeToGdb("call " + function + "()");
@@ -603,26 +585,15 @@ void tst_Gdb::check(const QByteArray &label, const QByteArray &expected0,
if (fancy) if (fancy)
options += ",fancy"; options += ",fancy";
options += ",autoderef"; options += ",autoderef";
writeToGdb("bb options:" + options + " vars: expanded:" + expanded QByteArray ba = writeToGdb("bb options:" + options
+ " vars: expanded:" + expanded
+ " typeformats: formats: watchers:\n"); + " typeformats: formats: watchers:\n");
//bb options:fancy,autoderef vars: expanded: typeformats:63686172202a=1 //bb options:fancy,autoderef vars: expanded: typeformats:63686172202a=1
//formats: watchers: //formats: watchers:
//qDebug() << "\n1 ABOUT TO ACQUIRE USED ";
usedBytes.acquire();
//qDebug() << "\n1 ACQUIRED USED ";
QByteArray ba = buffer;
buffer.clear();
//qDebug() << "\n1 ABOUT TO RELEASE FREE ";
freeBytes.release();
//qDebug() << "\n1 RELEASED FREE ";
//locals.fromString("{" + ba + "}"); //locals.fromString("{" + ba + "}");
QByteArray received = ba.replace("\"", "'"); QByteArray received = ba.replace("\"", "'");
//qDebug() << "OUTPUT: " << ba << "\n\n";
//qDebug() << "OUTPUT: " << locals.toString() << "\n\n";
QByteArray actual = received.trimmed(); QByteArray actual = received.trimmed();
int pos = actual.indexOf("^done"); int pos = actual.indexOf("^done");
if (pos != -1) if (pos != -1)
@@ -633,7 +604,7 @@ void tst_Gdb::check(const QByteArray &label, const QByteArray &expected0,
actual.chop(2); actual.chop(2);
QByteArray expected = "locals={iname='local',name='Locals',value=' ',type=' '," QByteArray expected = "locals={iname='local',name='Locals',value=' ',type=' ',"
"children=[" + expected0 + "]}"; "children=[" + expected0 + "]}";
int line = m_thread.m_line; int line = m_gdb->m_line;
QByteArrayList l1_0 = actual.split(','); QByteArrayList l1_0 = actual.split(',');
QByteArrayList l1; QByteArrayList l1;
@@ -688,7 +659,7 @@ void tst_Gdb::check(const QByteArray &label, const QByteArray &expected0,
int expline = m_lineForLabel.value(m_function + '@' + label); int expline = m_lineForLabel.value(m_function + '@' + label);
int actline = line; int actline = line;
if (actline != expline) { if (actline != expline) {
qWarning() << "LAST STOPPED: " << m_thread.m_lastStopped; qWarning() << "LAST STOPPED: " << m_gdb->m_lastStopped;
} }
QCOMPARE(actline, expline); QCOMPARE(actline, expline);
} }
@@ -701,10 +672,13 @@ void tst_Gdb::next(int n)
void tst_Gdb::cleanupTestCase() void tst_Gdb::cleanupTestCase()
{ {
Q_ASSERT(m_gdb);
writeToGdb("kill"); writeToGdb("kill");
writeToGdb("quit"); writeToGdb("quit");
//m_thread.m_proc->waitForFinished(); //m_gdb.m_proc.waitForFinished();
//m_thread.wait(); //m_gdb.wait();
delete m_gdb;
m_gdb = 0;
} }