diff --git a/tests/auto/debugger/tst_gdb.cpp b/tests/auto/debugger/tst_gdb.cpp index bff9d166017004e70751b1bf7b51177f01235ab7..f40b0756c7d5a2b5e7d722e6972516029e7f56fd 100644 --- a/tests/auto/debugger/tst_gdb.cpp +++ b/tests/auto/debugger/tst_gdb.cpp @@ -47,6 +47,46 @@ using namespace Debugger; using namespace Debugger::Internal; +class Foo +{ +public: + Foo(int i = 0) + : a(i), b(2) + {} + + ~Foo() + { + } + void doit() + { + static QObject ob; + m["1"] = "2"; + h[&ob] = m.begin(); + + a += 1; + --b; + //s += 'x'; + } + + + struct Bar { + Bar() : ob(0) {} + QObject *ob; + }; + +public: + int a, b; + char x[6]; + +private: + //QString s; + typedef QMap<QString, QString> Map; + Map m; + QHash<QObject *, Map::iterator> h; +}; + + + ///////////////////////////////////////////////////////////////////////// // // Helper stuff @@ -137,7 +177,9 @@ signals: void writeToGdb(const QByteArray &ba); private slots: + void dumpArray(); void dumpMisc(); + void dumpFoo(); void dumpQByteArray(); void dumpQChar(); void dumpQList_char_star(); @@ -146,8 +188,11 @@ private slots: void dumpQList_QString(); void dumpQList_QString3(); void dumpQList_Int3(); + void dumpQStack(); void dumpQString(); void dumpQStringList(); + void dumpQWeakPointer(); + void dumpQVector(); public slots: void dumperCompatibility(); @@ -181,7 +226,6 @@ public slots: void dumpQVariant_QString(); void dumpQVariant_QStringList(); void dumpStdVector(); - void dumpQWeakPointer(); #endif private: @@ -401,136 +445,487 @@ void getMapNodeParams(size_t &nodeSize, size_t &valOffset) } #endif -#if 0 -void tst_Gdb::dumpQAbstractItemHelper(QModelIndex &index) + + +///////////////////////////////////////////////////////////////////////// +// +// Gdb Thread +// +///////////////////////////////////////////////////////////////////////// + +Thread::Thread(tst_Gdb *test) { - const QAbstractItemModel *model = index.model(); - const QString &rowStr = N(index.row()); - const QString &colStr = N(index.column()); - const QByteArray &internalPtrStrSymbolic = ptrToBa(index.internalPointer()); - const QByteArray &internalPtrStrValue = ptrToBa(index.internalPointer(), false); - const QByteArray &modelPtrStr = ptrToBa(model); - QByteArray indexSpecSymbolic = QByteArray().append(rowStr + "," + colStr + ","). - append(internalPtrStrSymbolic + "," + modelPtrStr); - QByteArray indexSpecValue = QByteArray().append(rowStr + "," + colStr + ","). - append(internalPtrStrValue + "," + modelPtrStr); - QByteArray expected = QByteArray("tiname='iname',addr='").append(ptrToBa(&index)). - append("',type='"NS"QAbstractItem',addr='$").append(indexSpecSymbolic). - append("',value='").append(valToString(model->data(index).toString())). - append("',numchild='1',children=["); - int rowCount = model->rowCount(index); - int columnCount = model->columnCount(index); - for (int row = 0; row < rowCount; ++row) { - for (int col = 0; col < columnCount; ++col) { - const QModelIndex &childIndex = model->index(row, col, index); - expected.append("{name='[").append(valToString(row)).append(","). - append(N(col)).append("]',numchild='1',addr='$"). - append(N(childIndex.row())).append(","). - append(N(childIndex.column())).append(","). - append(ptrToBa(childIndex.internalPointer())).append(","). - append(modelPtrStr).append("',type='"NS"QAbstractItem',value='"). - append(valToString(model->data(childIndex).toString())).append("'}"); - if (col < columnCount - 1 || row < rowCount - 1) - expected.append(","); - } - } - expected.append("]"); - testDumper(expected, &index, NS"QAbstractItem", true, indexSpecValue); + moveToThread(this); + m_test = test; + m_proc = 0; + m_proc = new QProcess; + m_proc->moveToThread(this); + qDebug() << "\nTHREAD CREATED" << getpid() << gettid(); + connect(m_test, SIGNAL(writeToGdb(QByteArray)), + this, SLOT(writeToGdbRequested(QByteArray))); + connect(m_proc, SIGNAL(error(QProcess::ProcessError)), + this, SLOT(handleGdbError(QProcess::ProcessError))); + connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), + this, SLOT(handleGdbFinished(int, QProcess::ExitStatus))); + connect(m_proc, SIGNAL(started()), + this, SLOT(handleGdbStarted())); + connect(m_proc, SIGNAL(readyReadStandardOutput()), + this, SLOT(readStandardOutput())); + connect(m_proc, SIGNAL(readyReadStandardError()), + this, SLOT(readStandardError())); + start(); } -void tst_Gdb::dumpQAbstractItemAndModelIndex() +void Thread::handleGdbError(QProcess::ProcessError error) { - class PseudoTreeItemModel : public QAbstractItemModel - { - public: - PseudoTreeItemModel() : QAbstractItemModel(), parent1(0), - parent1Child(1), parent2(10), parent2Child1(11), parent2Child2(12) - {} + qDebug() << "GDB ERROR: " << error; + //this->exit(); +} - int columnCount(const QModelIndex &parent = QModelIndex()) const - { - Q_UNUSED(parent); - return 1; - } +void Thread::handleGdbFinished(int code, QProcess::ExitStatus st) +{ + qDebug() << "GDB FINISHED: " << code << st; + //m_waitCondition.wakeAll(); + //this->exit(); + throw 42; +} - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const - { - return !index.isValid() || role != Qt::DisplayRole ? - QVariant() : *static_cast<int *>(index.internalPointer()); +void Thread::readStandardOutput() +{ + QByteArray ba = m_proc->readAllStandardOutput(); + DEBUG("THREAD GDB OUT: " << ba); + // =library-loaded... + if (ba.startsWith("=")) + return; + if (ba.startsWith("*stopped")) { + m_lastStopped = ba; + //qDebug() << "THREAD GDB OUT: " << ba; + if (!ba.contains("func=\"main\"")) { + int pos1 = ba.indexOf(",line=\"") + 7; + int pos2 = ba.indexOf("\"", pos1); + m_line = ba.mid(pos1, pos2 - pos1).toInt(); + DEBUG(" LINE 1: " << m_line); } + } - QModelIndex index(int row, int column, - const QModelIndex & parent = QModelIndex()) const - { - QModelIndex index; - if (column == 0) { - if (!parent.isValid()) { - if (row == 0) - index = createIndex(row, column, &parent1); - else if (row == 1) - index = createIndex(row, column, &parent2); - } else if (parent.internalPointer() == &parent1 && row == 0) { - index = createIndex(row, column, &parent1Child); - } else if (parent.internalPointer() == &parent2) { - index = createIndex(row, column, - row == 0 ? &parent2Child1 : &parent2Child2); - } - } - return index; + // The "call" is always aborted with a message like: + // "~"2321\t /* A */ QString s;\n" " + // "&"The program being debugged stopped while in a function called ..." + // "^error,msg="The program being debugged stopped ..." + // Extract the "2321" from this + static QByteArray lastText; + if (ba.startsWith("~")) { + lastText = ba; + if (ba.size() > 8 + && (ba.at(2) < 'a' || ba.at(2) > 'z') + && (ba.at(2) < '0' || ba.at(2) > '9') + && !ba.startsWith("~\"Breakpoint ") + && !ba.startsWith("~\" at ") + && !ba.startsWith("~\" locals=") + && !ba.startsWith("~\"XXX:")) { + QByteArray ba1 = ba.mid(2, ba.size() - 6); + if (ba1.startsWith(" File ")) + ba1 = ba1.replace(2, ba1.indexOf(','), ""); + qWarning() << "OUT: " << ba1; } + } + if (ba.startsWith("&\"The program being debugged")) { + int pos1 = 2; + int pos2 = lastText.indexOf("\\", pos1); + m_line = lastText.mid(pos1, pos2 - pos1).toInt(); + DEBUG(" LINE 2: " << m_line); + } + if (ba.startsWith("^error,msg=")) { + if (!ba.startsWith("^error,msg=\"The program being debugged stopped")) + qWarning() << "ERROR: " << ba.mid(1, ba.size() - 3); + } - QModelIndex parent(const QModelIndex & index) const - { - QModelIndex parent; - if (index.isValid()) { - if (index.internalPointer() == &parent1Child) - parent = createIndex(0, 0, &parent1); - else if (index.internalPointer() == &parent2Child1 || - index.internalPointer() == &parent2Child2) - parent = createIndex(1, 0, &parent2); - } - return parent; - } + if (ba.startsWith("~\"XXX: ")) { + QByteArray ba1 = ba.mid(7, ba.size() - 11); + qWarning() << "MESSAGE: " << ba.mid(7, ba.size() - 12); + } + if (!ba.contains("locals={iname=")) + return; + //m_output += ba; + ba = ba.mid(2, ba.size() - 4); + ba = ba.replace("\\\"", "\""); + m_output = ba; + m_waitCondition.wakeAll(); +} - int rowCount(const QModelIndex &parent = QModelIndex()) const - { - int rowCount; - if (!parent.isValid() || parent.internalPointer() == &parent2) - rowCount = 2; - else if (parent.internalPointer() == &parent1) - rowCount = 1; - else - rowCount = 0; - return rowCount; - } +void Thread::readStandardError() +{ + QByteArray ba = m_proc->readAllStandardOutput(); + qDebug() << "THREAD GDB ERR: " << ba; +} - private: - mutable int parent1; - mutable int parent1Child; - mutable int parent2; - mutable int parent2Child1; - mutable int parent2Child2; - }; +void Thread::handleGdbStarted() +{ + //qDebug() << "\n\nGDB STARTED" << getpid() << gettid() << "\n\n"; +} - PseudoTreeItemModel m2; +void Thread::run() +{ + //qDebug() << "\nTHREAD RUN" << getpid() << gettid(); + m_proc->start("./gdb -i mi --args ./tst_gdb run"); + m_proc->waitForStarted(); + m_proc->write("break main\n"); + m_proc->write("run\n"); + m_proc->write("handle SIGSTOP stop pass\n"); + //qDebug() << "\nTHREAD RUNNING"; + exec(); +} - // Case 1: ModelIndex with no children. - QStringListModel m(QStringList() << "item1" << "item2" << "item3"); - QModelIndex index = m.index(2, 0); - testDumper(QByteArray("type='$T',value='(2, 0)',numchild='5',children=[" - "{name='row',value='2',type='int',numchild='0'}," - "{name='column',value='0',type='int',numchild='0'}," - "{name='parent',value='<invalid>',exp='(('$T'*)$A)->parent()'," - "type='$T',numchild='1'}," - "{name='internalId',%}," - "{name='model',value='%',type='"NS"QAbstractItemModel*'," - "numchild='1'}]") - << generateQStringSpec(N(index.internalId())) - << ptrToBa(&m), - &index, NS"QModelIndex", true); +///////////////////////////////////////////////////////////////////////// +// +// Test Class Framework Implementation +// +///////////////////////////////////////////////////////////////////////// - // Case 2: ModelIndex with one child. +tst_Gdb::tst_Gdb() + : m_thread(this) +{ + // FIXME: Wait until gdb proc is running. + QTest::qWait(600); + + QFile file("tst_gdb.cpp"); + Q_ASSERT(file.open(QIODevice::ReadOnly)); + QByteArray funcName; + const QByteArrayList bal = file.readAll().split('\n'); + Q_ASSERT(bal.size() > 100); + for (int i = 0; i != bal.size(); ++i) { + const QByteArray &ba = bal.at(i); + if (ba.startsWith("void dump")) { + int pos = ba.indexOf('('); + funcName = ba.mid(5, pos - 5) + '@'; + } else if (ba.startsWith(" /*")) { + int pos = ba.indexOf('*', 7); + m_lineForLabel[funcName + ba.mid(7, pos - 8)] = i + 1; + } + } +} + +void tst_Gdb::prepare(const QByteArray &function) +{ + m_function = function; + writeToGdb("b " + function); + writeToGdb("call " + function + "()"); +} + +static bool isJoker(const QByteArray &ba) +{ + return ba.endsWith("'-'") || ba.contains("'-'}"); +} + +void tst_Gdb::run(const QByteArray &label, const QByteArray &expected0, + const QByteArray &expanded, bool fancy) +{ + //qDebug() << "\nABOUT TO RUN TEST: " << expanded; + qWarning() << label << "..."; + writeToGdb("bb " + QByteArray::number(int(fancy)) + " " + expanded); + m_mutex.lock(); + m_waitCondition.wait(&m_mutex); + QByteArray ba = m_thread.m_output; + m_mutex.unlock(); + //GdbMi locals; + //locals.fromString("{" + ba + "}"); + QByteArray received = ba.replace("\"", "'"); + //qDebug() << "OUTPUT: " << ba << "\n\n"; + //qDebug() << "OUTPUT: " << locals.toString() << "\n\n"; + + QByteArray actual = received.trimmed(); + if (actual.endsWith("\\n")) + actual.chop(2); + QByteArray expected = "locals={iname='local',name='Locals',value=' ',type=' '," + "children=[" + expected0 + "]}"; + int line = m_thread.m_line; + + QByteArrayList l1 = actual.split(','); + QByteArrayList l2 = expected.split(','); + + bool ok = l1.size() == l2.size(); + if (ok) { + for (int i = 0 ; i < l1.size(); ++i) { + // Use "-" as joker. + if (l1.at(i) != l2.at(i) && !isJoker(l2.at(i))) + ok = false; + } + } else { + qWarning() << "!= size: " << l1.size() << l2.size(); + } + + if (!ok) { + int i = 0; + for ( ; i < l1.size() && i < l2.size(); ++i) { + if (l1.at(i) == l2.at(i) || isJoker(l2.at(i))) { + qWarning() << "== " << l1.at(i); + } else { + //qWarning() << "!= " << l1.at(i).right(30) << l2.at(i).right(30); + qWarning() << "!= " << l1.at(i) << l2.at(i); + ok = false; + } + } + for ( ; i < l2.size(); ++i) + qWarning() << "!= " << "-----" << l2.at(i); + for ( ; i < l1.size(); ++i) + qWarning() << "!= " << l1.at(i) << "-----"; + if (l1.size() != l2.size()) { + ok = false; + qWarning() << "!= size: " << l1.size() << l2.size(); + } + qWarning() << "RECEIVED: " << received; + } + QCOMPARE(ok, true); + //qWarning() << "LINE: " << line << "ACT/EXP" << m_function + '@' + label; + //QCOMPARE(actual, expected); + + + int expline = m_lineForLabel.value(m_function + '@' + label); + int actline = line; + if (actline != expline) { + qWarning() << "LAST STOPPED: " << m_thread.m_lastStopped; + } + QCOMPARE(actline, expline); +} + +void tst_Gdb::next(int n) +{ + for (int i = 0; i != n; ++i) + writeToGdb("next"); +} + +void tst_Gdb::cleanupTestCase() +{ + writeToGdb("kill"); + writeToGdb("quit"); + //m_thread.m_proc->waitForFinished(); +} + + +///////////////////////////////////////////////////////////////////////// +// +// Dumper Tests +// +///////////////////////////////////////////////////////////////////////// + +///////////////////////////// Foo structure ///////////////////////////////// + +void dumpFoo() +{ + /* A */ Foo f; + /* B */ f.doit(); + /* D */ (void) 0; +} + +void tst_Gdb::dumpFoo() +{ + prepare("dumpFoo"); + next(); + run("B","{iname='local.f',addr='-',name='f',type='Foo'," + "value='-',numchild='5'}", "", 0); + run("B","{iname='local.f',addr='-',name='f',type='Foo'," + "value='-',numchild='5',children=[" + "{iname='local.f.a',name='a',type='int',value='0',numchild='0'}," + "{iname='local.f.b',name='b',type='int',value='2',numchild='0'}," + "{iname='local.f.x',name='x',type='char [6]'," + "value='{...}',numchild='1'}," + "{iname='local.f.m',name='m',type='"NS"QMap<"NS"QString, "NS"QString>'," + "value='{...}',numchild='1'}," + "{iname='local.f.h',name='h',type='"NS"QHash<"NS"QObject*, " + ""NS"QMap<"NS"QString, "NS"QString>::iterator>'," + "value='{...}',numchild='1'}]}", + "local.f", 0); +} + + +///////////////////////////// Array /////////////////////////////////////// + +void dumpArray_char() +{ + /* A */ const char s[] = "XYZ"; + /* B */ (void) &s; } + +void dumpArray_int() +{ + /* A */ int s[] = {1, 2, 3}; + /* B */ (void) s; } + +void tst_Gdb::dumpArray() +{ + prepare("dumpArray_char"); + next(); + // FIXME: numchild should be '4', not '1' + run("B","{iname='local.s',addr='-',name='s',type='char [4]'," + "value='-',numchild='1'}", ""); + run("B","{iname='local.s',addr='-',name='s',type='char [4]'," + "value='-',numchild='1',childtype='char',childnumchild='0'," + "children=[{value='88 'X''},{value='89 'Y''},{value='90 'Z''}," + "{value='0 '\\\\000''}]}", + "local.s"); + + prepare("dumpArray_int"); + next(); + // FIXME: numchild should be '3', not '1' + run("B","{iname='local.s',addr='-',name='s',type='int [3]'," + "value='-',numchild='1'}", ""); + run("B","{iname='local.s',addr='-',name='s',type='int [3]'," + "value='-',numchild='1',childtype='int',childnumchild='0'," + "children=[{value='1'},{value='2'},{value='3'}]}", + "local.s"); +} + + +///////////////////////////// Misc stuff ///////////////////////////////// + +void dumpMisc() +{ + /* A */ int *s = new int(1); + /* B */ *s += 1; + /* D */ (void) 0; +} + +void tst_Gdb::dumpMisc() +{ + prepare("dumpMisc"); + next(); + run("B","{iname='local.s',addr='-',name='s',type='int *'," + "value='-',numchild='1'}", "", 0); + run("B","{iname='local.s',addr='-',name='s',type='int *'," + "value='-',numchild='1',children=[{iname='local.s.*'," + "name='*s',type='int',value='1',numchild='0'}]}", "local.s", 0); +} + +#if 0 +void tst_Gdb::dumpQAbstractItemHelper(QModelIndex &index) +{ + const QAbstractItemModel *model = index.model(); + const QString &rowStr = N(index.row()); + const QString &colStr = N(index.column()); + const QByteArray &internalPtrStrSymbolic = ptrToBa(index.internalPointer()); + const QByteArray &internalPtrStrValue = ptrToBa(index.internalPointer(), false); + const QByteArray &modelPtrStr = ptrToBa(model); + QByteArray indexSpecSymbolic = QByteArray().append(rowStr + "," + colStr + ","). + append(internalPtrStrSymbolic + "," + modelPtrStr); + QByteArray indexSpecValue = QByteArray().append(rowStr + "," + colStr + ","). + append(internalPtrStrValue + "," + modelPtrStr); + QByteArray expected = QByteArray("tiname='iname',addr='").append(ptrToBa(&index)). + append("',type='"NS"QAbstractItem',addr='$").append(indexSpecSymbolic). + append("',value='").append(valToString(model->data(index).toString())). + append("',numchild='1',children=["); + int rowCount = model->rowCount(index); + int columnCount = model->columnCount(index); + for (int row = 0; row < rowCount; ++row) { + for (int col = 0; col < columnCount; ++col) { + const QModelIndex &childIndex = model->index(row, col, index); + expected.append("{name='[").append(valToString(row)).append(","). + append(N(col)).append("]',numchild='1',addr='$"). + append(N(childIndex.row())).append(","). + append(N(childIndex.column())).append(","). + append(ptrToBa(childIndex.internalPointer())).append(","). + append(modelPtrStr).append("',type='"NS"QAbstractItem',value='"). + append(valToString(model->data(childIndex).toString())).append("'}"); + if (col < columnCount - 1 || row < rowCount - 1) + expected.append(","); + } + } + expected.append("]"); + testDumper(expected, &index, NS"QAbstractItem", true, indexSpecValue); +} + +void tst_Gdb::dumpQAbstractItemAndModelIndex() +{ + class PseudoTreeItemModel : public QAbstractItemModel + { + public: + PseudoTreeItemModel() : QAbstractItemModel(), parent1(0), + parent1Child(1), parent2(10), parent2Child1(11), parent2Child2(12) + {} + + int columnCount(const QModelIndex &parent = QModelIndex()) const + { + Q_UNUSED(parent); + return 1; + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + return !index.isValid() || role != Qt::DisplayRole ? + QVariant() : *static_cast<int *>(index.internalPointer()); + } + + QModelIndex index(int row, int column, + const QModelIndex & parent = QModelIndex()) const + { + QModelIndex index; + if (column == 0) { + if (!parent.isValid()) { + if (row == 0) + index = createIndex(row, column, &parent1); + else if (row == 1) + index = createIndex(row, column, &parent2); + } else if (parent.internalPointer() == &parent1 && row == 0) { + index = createIndex(row, column, &parent1Child); + } else if (parent.internalPointer() == &parent2) { + index = createIndex(row, column, + row == 0 ? &parent2Child1 : &parent2Child2); + } + } + return index; + } + + QModelIndex parent(const QModelIndex & index) const + { + QModelIndex parent; + if (index.isValid()) { + if (index.internalPointer() == &parent1Child) + parent = createIndex(0, 0, &parent1); + else if (index.internalPointer() == &parent2Child1 || + index.internalPointer() == &parent2Child2) + parent = createIndex(1, 0, &parent2); + } + return parent; + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const + { + int rowCount; + if (!parent.isValid() || parent.internalPointer() == &parent2) + rowCount = 2; + else if (parent.internalPointer() == &parent1) + rowCount = 1; + else + rowCount = 0; + return rowCount; + } + + private: + mutable int parent1; + mutable int parent1Child; + mutable int parent2; + mutable int parent2Child1; + mutable int parent2Child2; + }; + + PseudoTreeItemModel m2; + + // Case 1: ModelIndex with no children. + QStringListModel m(QStringList() << "item1" << "item2" << "item3"); + QModelIndex index = m.index(2, 0); + + testDumper(QByteArray("type='$T',value='(2, 0)',numchild='5',children=[" + "{name='row',value='2',type='int',numchild='0'}," + "{name='column',value='0',type='int',numchild='0'}," + "{name='parent',value='<invalid>',exp='(('$T'*)$A)->parent()'," + "type='$T',numchild='1'}," + "{name='internalId',%}," + "{name='model',value='%',type='"NS"QAbstractItemModel*'," + "numchild='1'}]") + << generateQStringSpec(N(index.internalId())) + << ptrToBa(&m), + &index, NS"QModelIndex", true); + + // Case 2: ModelIndex with one child. QModelIndex index2 = m2.index(0, 0); dumpQAbstractItemHelper(index2); @@ -667,7 +1062,7 @@ void dumpQByteArrayTest() void tst_Gdb::dumpQByteArray() { prepare("dumpQByteArrayTest"); - if (checkUninitialized) + if (1 || checkUninitialized) run("A","{iname='local.ba',addr='-',name='ba',type='"NS"QByteArray'," "value='<not in scope>',numchild='0'}"); next(); @@ -2019,326 +2414,150 @@ template <typename T1, typename T2> size_t offsetOf(const T1 *klass, const T2 *member) { return static_cast<size_t>(reinterpret_cast<const char *>(member) - - reinterpret_cast<const char *>(klass)); -} - -template <typename T> -void tst_Gdb::dumpQWeakPointerHelper(QWeakPointer<T> &ptr) -{ - typedef QtSharedPointer::ExternalRefCountData Data; - const size_t dataOffset = 0; - const Data *d = *reinterpret_cast<const Data **>( - reinterpret_cast<const char **>(&ptr) + dataOffset); - const int *weakRefPtr = reinterpret_cast<const int *>(&d->weakref); - const int *strongRefPtr = reinterpret_cast<const int *>(&d->strongref); - T *data = ptr.toStrongRef().data(); - const QString dataStr = valToString(*data); - QByteArray expected("value='"); - if (isSimpleType<T>()) - expected.append(dataStr); - expected.append("',valuedisabled='true',numchild='1',children=[{name='data',addr='"). - append(ptrToBa(data)).append("',type='").append(typeToString<T>()). - append("',value='").append(dataStr).append("'},{name='weakref',value='"). - append(valToString(*weakRefPtr)).append("',type='int',addr='"). - append(ptrToBa(weakRefPtr)).append("',numchild='0'},{name='strongref',value='"). - append(valToString(*strongRefPtr)).append("',type='int',addr='"). - append(ptrToBa(strongRefPtr)).append("',numchild='0'}]"); - testDumper(expected, &ptr, NS"QWeakPointer", true, typeToString<T>()); -} -#endif - -void tst_Gdb::dumpQWeakPointer() -{ -#if QT_VERSION >= 0x040500 - // Case 1: Simple type. - - // Case 1.1: Null pointer. - QSharedPointer<int> spNull; - QWeakPointer<int> wp = spNull.toWeakRef(); - testDumper("value='<null>',valuedisabled='true',numchild='0'", - &wp, NS"QWeakPointer", true, "int"); - - // Case 1.2: Weak pointer is unique. - QSharedPointer<int> sp(new int(99)); - wp = sp.toWeakRef(); - dumpQWeakPointerHelper(wp); - - // Case 1.3: There are other weak pointers. - QWeakPointer<int> wp2 = sp.toWeakRef(); - dumpQWeakPointerHelper(wp); - - // Case 1.4: There are other strong shared pointers as well. - QSharedPointer<int> sp2(sp); - dumpQWeakPointerHelper(wp); - - // Case 2: Composite type. - QSharedPointer<QString> spS(new QString("Test")); - QWeakPointer<QString> wpS = spS.toWeakRef(); - dumpQWeakPointerHelper(wpS); -#endif -} -#endif // #if 0 - - -///////////////////////////////////////////////////////////////////////// -// -// Gdb Thread -// -///////////////////////////////////////////////////////////////////////// - -Thread::Thread(tst_Gdb *test) -{ - moveToThread(this); - m_test = test; - m_proc = 0; - m_proc = new QProcess; - m_proc->moveToThread(this); - qDebug() << "\nTHREAD CREATED" << getpid() << gettid(); - connect(m_test, SIGNAL(writeToGdb(QByteArray)), - this, SLOT(writeToGdbRequested(QByteArray))); - connect(m_proc, SIGNAL(error(QProcess::ProcessError)), - this, SLOT(handleGdbError(QProcess::ProcessError))); - connect(m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), - this, SLOT(handleGdbFinished(int, QProcess::ExitStatus))); - connect(m_proc, SIGNAL(started()), - this, SLOT(handleGdbStarted())); - connect(m_proc, SIGNAL(readyReadStandardOutput()), - this, SLOT(readStandardOutput())); - connect(m_proc, SIGNAL(readyReadStandardError()), - this, SLOT(readStandardError())); - start(); -} - -void Thread::handleGdbError(QProcess::ProcessError error) -{ - qDebug() << "GDB ERROR: " << error; - //this->exit(); -} - -void Thread::handleGdbFinished(int code, QProcess::ExitStatus st) -{ - qDebug() << "GDB FINISHED: " << code << st; - //m_waitCondition.wakeAll(); - //this->exit(); - throw 42; -} - -void Thread::readStandardOutput() -{ - QByteArray ba = m_proc->readAllStandardOutput(); - DEBUG("THREAD GDB OUT: " << ba); - // =library-loaded... - if (ba.startsWith("=")) - return; - if (ba.startsWith("*stopped")) { - m_lastStopped = ba; - //qDebug() << "THREAD GDB OUT: " << ba; - if (!ba.contains("func=\"main\"")) { - int pos1 = ba.indexOf(",line=\"") + 7; - int pos2 = ba.indexOf("\"", pos1); - m_line = ba.mid(pos1, pos2 - pos1).toInt(); - DEBUG(" LINE 1: " << m_line); - } - } - - // The "call" is always aborted with a message like: - // "~"2321\t /* A */ QString s;\n" " - // "&"The program being debugged stopped while in a function called ..." - // "^error,msg="The program being debugged stopped ..." - // Extract the "2321" from this - static QByteArray lastText; - if (ba.startsWith("~")) { - lastText = ba; - if (ba.size() > 8 - && (ba.at(2) < 'a' || ba.at(2) > 'z') - && (ba.at(2) < '0' || ba.at(2) > '9') - && !ba.startsWith("~\"Breakpoint ") - && !ba.startsWith("~\" at ") - && !ba.startsWith("~\" locals=") - && !ba.startsWith("~\"XXX:")) { - QByteArray ba1 = ba.mid(2, ba.size() - 6); - if (ba1.startsWith(" File ")) - ba1 = ba1.replace(2, ba1.indexOf(','), ""); - qWarning() << "OUT: " << ba1; - } - } - if (ba.startsWith("&\"The program being debugged")) { - int pos1 = 2; - int pos2 = lastText.indexOf("\\", pos1); - m_line = lastText.mid(pos1, pos2 - pos1).toInt(); - DEBUG(" LINE 2: " << m_line); - } - if (ba.startsWith("^error,msg=")) { - if (!ba.startsWith("^error,msg=\"The program being debugged stopped")) - qWarning() << "ERROR: " << ba.mid(1, ba.size() - 3); - } - - if (ba.startsWith("~\"XXX: ")) { - QByteArray ba1 = ba.mid(7, ba.size() - 11); - qWarning() << "MESSAGE: " << ba.mid(7, ba.size() - 12); - } - if (!ba.contains("locals={iname=")) - return; - //m_output += ba; - ba = ba.mid(2, ba.size() - 4); - ba = ba.replace("\\\"", "\""); - m_output = ba; - m_waitCondition.wakeAll(); + reinterpret_cast<const char *>(klass)); } -void Thread::readStandardError() +template <typename T> +void tst_Gdb::dumpQWeakPointerHelper(QWeakPointer<T> &ptr) { - QByteArray ba = m_proc->readAllStandardOutput(); - qDebug() << "THREAD GDB ERR: " << ba; + typedef QtSharedPointer::ExternalRefCountData Data; + const size_t dataOffset = 0; + const Data *d = *reinterpret_cast<const Data **>( + reinterpret_cast<const char **>(&ptr) + dataOffset); + const int *weakRefPtr = reinterpret_cast<const int *>(&d->weakref); + const int *strongRefPtr = reinterpret_cast<const int *>(&d->strongref); + T *data = ptr.toStrongRef().data(); + const QString dataStr = valToString(*data); + QByteArray expected("value='"); + if (isSimpleType<T>()) + expected.append(dataStr); + expected.append("',valuedisabled='true',numchild='1',children=[{name='data',addr='"). + append(ptrToBa(data)).append("',type='").append(typeToString<T>()). + append("',value='").append(dataStr).append("'},{name='weakref',value='"). + append(valToString(*weakRefPtr)).append("',type='int',addr='"). + append(ptrToBa(weakRefPtr)).append("',numchild='0'},{name='strongref',value='"). + append(valToString(*strongRefPtr)).append("',type='int',addr='"). + append(ptrToBa(strongRefPtr)).append("',numchild='0'}]"); + testDumper(expected, &ptr, NS"QWeakPointer", true, typeToString<T>()); } +#endif +#endif // #if 0 -void Thread::handleGdbStarted() +///////////////////////////// QWeakPointer ///////////////////////////////// + +#if QT_VERSION >= 0x040500 +void dumpQWeakPointer_11() { - //qDebug() << "\n\nGDB STARTED" << getpid() << gettid() << "\n\n"; + // Case 1: Simple type. + // Case 1.1: Null pointer. + /* A */ QSharedPointer<int> sp; + /* */ QWeakPointer<int> wp = sp.toWeakRef(); + /* B */ (void) 0; } -void Thread::run() +void dumpQWeakPointer_12() { - //qDebug() << "\nTHREAD RUN" << getpid() << gettid(); - m_proc->start("./gdb -i mi --args ./tst_gdb run"); - m_proc->waitForStarted(); - m_proc->write("break main\n"); - m_proc->write("run\n"); - m_proc->write("handle SIGSTOP stop pass\n"); - //qDebug() << "\nTHREAD RUNNING"; - exec(); + // Case 1.2: Weak pointer is unique. + /* A */ QSharedPointer<int> sp(new int(99)); + /* */ QWeakPointer<int> wp = sp.toWeakRef(); + /* B */ (void) 0; } - -///////////////////////////////////////////////////////////////////////// -// -// Test Class Framework Implementation -// -///////////////////////////////////////////////////////////////////////// - -tst_Gdb::tst_Gdb() - : m_thread(this) +void dumpQWeakPointer_13() { - // FIXME: Wait until gdb proc is running. - QTest::qWait(600); - - QFile file("tst_gdb.cpp"); - Q_ASSERT(file.open(QIODevice::ReadOnly)); - QByteArray funcName; - const QByteArrayList bal = file.readAll().split('\n'); - Q_ASSERT(bal.size() > 100); - for (int i = 0; i != bal.size(); ++i) { - const QByteArray &ba = bal.at(i); - if (ba.startsWith("void dump")) { - int pos = ba.indexOf('('); - funcName = ba.mid(5, pos - 5) + '@'; - } else if (ba.startsWith(" /*")) { - int pos = ba.indexOf('*', 7); - m_lineForLabel[funcName + ba.mid(7, pos - 8)] = i + 1; - } - } + // Case 1.3: There are other weak pointers. + /* A */ QSharedPointer<int> sp(new int(99)); + /* */ QWeakPointer<int> wp = sp.toWeakRef(); + /* B */ (void) 0; } -void tst_Gdb::prepare(const QByteArray &function) +void dumpQWeakPointer_14() { - m_function = function; - writeToGdb("b " + function); - writeToGdb("call " + function + "()"); + // Case 1.4: There are other strong shared pointers as well. + /* A */ QSharedPointer<int> sp(new int(99)); + /* */ QSharedPointer<int> sp2(sp); + /* B */ (void) 0; } -static bool isJoker(const QByteArray &ba) +void dumpQWeakPointer_2() { - return ba.endsWith("'-'") || ba.contains("'-'}"); + // Case 2: Composite type. + /* A */ QSharedPointer<QString> sp(new QString("Test")); + /* */ QWeakPointer<QString> wp = sp.toWeakRef(); + /* B */ (void) 0; } +#endif -void tst_Gdb::run(const QByteArray &label, const QByteArray &expected0, - const QByteArray &expanded, bool fancy) +void tst_Gdb::dumpQWeakPointer() { - //qDebug() << "\nABOUT TO RUN TEST: " << expanded; - qWarning() << label << "..."; - writeToGdb("bb " + QByteArray::number(int(fancy)) + " " + expanded); - m_mutex.lock(); - m_waitCondition.wait(&m_mutex); - QByteArray ba = m_thread.m_output; - m_mutex.unlock(); - //GdbMi locals; - //locals.fromString("{" + ba + "}"); - QByteArray received = ba.replace("\"", "'"); - //qDebug() << "OUTPUT: " << ba << "\n\n"; - //qDebug() << "OUTPUT: " << locals.toString() << "\n\n"; - - QByteArray actual____ = received.trimmed(); - QByteArray expected = "locals={iname='local',name='Locals',value=' ',type=' '," - "children=[" + expected0 + "]}"; - int line = m_thread.m_line; - - QByteArrayList l1 = actual____.split(','); - QByteArrayList l2 = expected.split(','); +#if QT_VERSION >= 0x040500 - bool ok = l1.size() == l2.size(); - if (ok) { - for (int i = 0 ; i < l1.size(); ++i) { - // Use "-" as joker. - if (l1.at(i) != l2.at(i) && !isJoker(l2.at(i))) - ok = false; - } - } else { - qWarning() << "!= size: " << l1.size() << l2.size(); - } - - if (!ok) { - int i = 0; - for ( ; i < l1.size() && i < l2.size(); ++i) { - if (l1.at(i) == l2.at(i) || isJoker(l2.at(i))) { - qWarning() << "== " << l1.at(i); - } else { - //qWarning() << "!= " << l1.at(i).right(30) << l2.at(i).right(30); - qWarning() << "!= " << l1.at(i) << l2.at(i); - ok = false; - } - } - for ( ; i < l2.size(); ++i) - qWarning() << "!= " << "-----" << l2.at(i); - for ( ; i < l1.size(); ++i) - qWarning() << "!= " << l1.at(i) << "-----"; - if (l1.size() != l2.size()) { - ok = false; - qWarning() << "!= size: " << l1.size() << l2.size(); - } - qWarning() << "RECEIVED: " << received; - } - QCOMPARE(ok, true); - //qWarning() << "LINE: " << line << "ACT/EXP" << m_function + '@' + label; - //QCOMPARE(actual____, expected); +return; + // Case 1.1: Null pointer. + prepare("dumpQWeakPointer_11"); + if (checkUninitialized) + run("A","{iname='local.sp',addr='-',name='sp'," + "type='"NS"QSharedPointer<int>',value='<not in scope>',numchild='0'}"); + next(); + run("B","{iname='local.sp',addr='-',name='sp'," + "type='"NS"QSharedPointer<int>',value='<0 items>',numchild='1'}"); - int expline = m_lineForLabel.value(m_function + '@' + label); - int actline = line; - if (actline != expline) { - qWarning() << "LAST STOPPED: " << m_thread.m_lastStopped; - } - QCOMPARE(actline, expline); -} +/* + //,numchild='1',children=[{name='data',addr='"). + append(ptrToBa(data)).append("',type='").append(typeToString<T>()). + append("',value='").append(dataStr).append("'},{name='weakref',value='"). + append(valToString(*weakRefPtr)).append("',type='int',addr='"). + append(ptrToBa(weakRefPtr)).append("',numchild='0'},{name='strongref',value='"). + append(valToString(*strongRefPtr)).append("',type='int',addr='"). + append(ptrToBa(strongRefPtr)).append("',numchild='0'}]"); -void tst_Gdb::next(int n) + // Case 1.2: Weak pointer is unique. +void tst_Gdb::dumpQWeakPointerHelper(QWeakPointer<T> &ptr) { - for (int i = 0; i != n; ++i) - writeToGdb("next"); + typedef QtSharedPointer::ExternalRefCountData Data; + const size_t dataOffset = 0; + const Data *d = *reinterpret_cast<const Data **>( + reinterpret_cast<const char **>(&ptr) + dataOffset); + const int *weakRefPtr = reinterpret_cast<const int *>(&d->weakref); + const int *strongRefPtr = reinterpret_cast<const int *>(&d->strongref); + T *data = ptr.toStrongRef().data(); + const QString dataStr = valToString(*data); + QByteArray expected("value='"); + if (isSimpleType<T>()) + expected.append(dataStr); + expected.append("',valuedisabled='true',numchild='1',children=[{name='data',addr='"). + append(ptrToBa(data)).append("',type='").append(typeToString<T>()). + append("',value='").append(dataStr).append("'},{name='weakref',value='"). + append(valToString(*weakRefPtr)).append("',type='int',addr='"). + append(ptrToBa(weakRefPtr)).append("',numchild='0'},{name='strongref',value='"). + append(valToString(*strongRefPtr)).append("',type='int',addr='"). + append(ptrToBa(strongRefPtr)).append("',numchild='0'}]"); + testDumper(expected, &ptr, NS"QWeakPointer", true, typeToString<T>()); } -void tst_Gdb::cleanupTestCase() -{ - writeToGdb("kill"); - writeToGdb("quit"); - //m_thread.m_proc->waitForFinished(); -} + QSharedPointer<int> sp(new int(99)); + wp = sp.toWeakRef(); + dumpQWeakPointerHelper(wp); -///////////////////////////////////////////////////////////////////////// -// -// Dumper Tests -// -///////////////////////////////////////////////////////////////////////// + // Case 1.3: There are other weak pointers. + QWeakPointer<int> wp2 = sp.toWeakRef(); + dumpQWeakPointerHelper(wp); + + // Case 1.4: There are other strong shared pointers as well. + QSharedPointer<int> sp2(sp); + dumpQWeakPointerHelper(wp); + + // Case 2: Composite type. + QSharedPointer<QString> spS(new QString("Test")); + QWeakPointer<QString> wpS = spS.toWeakRef(); + dumpQWeakPointerHelper(wpS); +*/ +#endif +} + +///////////////////////////// QList<int> ///////////////////////////////// void dumpQList_int() { @@ -2373,6 +2592,9 @@ void tst_Gdb::dumpQList_int() "{value='1'},{value='2'}]}", "local.list"); } + +///////////////////////////// QList<char> ///////////////////////////////// + void dumpQList_char() { /* A */ QList<char> list; @@ -2398,6 +2620,9 @@ void tst_Gdb::dumpQList_char() "{value='97 'a''}]}", "local.list"); } + +///////////////////////////// QList<const char *> ///////////////////////////////// + void dumpQList_char_star() { /* A */ QList<const char *> list; @@ -2433,6 +2658,9 @@ void tst_Gdb::dumpQList_char_star() "{valueencoded='6',value='6263',numchild='0'}]}", "local.list"); } + +///////////////////////////// QList<QString> ///////////////////////////////////// + void dumpQList_QString() { /* A */ QList<QString> list; @@ -2458,6 +2686,9 @@ void tst_Gdb::dumpQList_QString() "{valueencoded='7',value='480061006c006c006f00'}]}", "local.list"); } + +///////////////////////////// QList<QString3> /////////////////////////////////// + void dumpQList_QString3() { /* A */ QList<QString3> list; @@ -2493,6 +2724,9 @@ void tst_Gdb::dumpQList_QString3() "local.list,local.list.0"); } + +///////////////////////////// QList<Int3> ///////////////////////////////////// + void dumpQList_Int3() { /* A */ QList<Int3> list; @@ -2525,25 +2759,47 @@ void tst_Gdb::dumpQList_Int3() "local.list,local.list.0"); } -void dumpMisc() +///////////////////////////// QVector ///////////////////////////////// + +void dumpQStack() { - /* A */ int *s = new int(1); - /* B */ *s += 1; - /* D */ (void) s; + /* A */ QStack<int> v; + /* B */ v.append(3); + /* C */ v.append(2); + /* D */ (void) 0; } -void tst_Gdb::dumpMisc() +void tst_Gdb::dumpQStack() { - prepare("dumpMisc"); + prepare("dumpQStack"); + if (checkUninitialized) + run("A","{iname='local.v',addr='-',name='v',type='"NS"QStack<int>'," + "value='<not in scope>',numchild='0'}"); next(); - run("B","{iname='local.s',addr='-',name='s',type='int *'," - "value='-',numchild='1'}", "", 0); - run("B","{iname='local.s',addr='-',name='s',type='int *'," - "value='-',numchild='1',children=[{iname='local.s.*'," - "name='*s',type='int',value='1',numchild='0'}]}", "local.s", 0); + run("B","{iname='local.v',addr='-',name='v',type='"NS"QStack<int>'," + "value='<0 items>',numchild='0'}"); + run("B","{iname='local.v',addr='-',name='v',type='"NS"QStack<int>'," + "value='<0 items>',numchild='0',children=[]}", "local.v"); + next(); + run("C","{iname='local.v',addr='-',name='v',type='"NS"QStack<int>'," + "value='<1 items>',numchild='1'}"); + run("C","{iname='local.v',addr='-',name='v',type='"NS"QStack<int>'," + "value='<1 items>',numchild='1',childtype='int'," + "childnumchild='0',children=[{value='3'}]}", // rounding... + "local.v"); + next(); + run("D","{iname='local.v',addr='-',name='v',type='"NS"QStack<int>'," + "value='<2 items>',numchild='2'}"); + run("D","{iname='local.v',addr='-',name='v',type='"NS"QStack<int>'," + "value='<2 items>',numchild='2',childtype='int'," + "childnumchild='0',children=[{value='3'},{value='2'}]}", + "local.v"); } -void dumpQStringTest() + +///////////////////////////// QString ///////////////////////////////////// + +void dumpQString() { /* A */ QString s; /* B */ s = "hallo"; @@ -2553,7 +2809,7 @@ void dumpQStringTest() void tst_Gdb::dumpQString() { - prepare("dumpQStringTest"); + prepare("dumpQString"); if (checkUninitialized) run("A","{iname='local.s',addr='-',name='s',type='"NS"QString'," "value='<not in scope>',numchild='0'}"); @@ -2582,7 +2838,10 @@ void tst_Gdb::dumpQString() "valueencoded='7',value='680061006c006c006f007800',numchild='0'}"); } -void dumpQStringListTest() + +///////////////////////////// QStringList ///////////////////////////////// + +void dumpQStringList() { /* A */ QStringList s; /* B */ s.append("hello"); @@ -2592,7 +2851,7 @@ void dumpQStringListTest() void tst_Gdb::dumpQStringList() { - prepare("dumpQStringListTest"); + prepare("dumpQStringList"); if (checkUninitialized) run("A","{iname='local.s',addr='-',name='s',type='"NS"QStringList'," "value='<not in scope>',numchild='0'}"); @@ -2621,6 +2880,43 @@ void tst_Gdb::dumpQStringList() } +///////////////////////////// QVector ///////////////////////////////// + +void dumpQVector() +{ + /* A */ QVector<double> v; + /* B */ v.append(3.14); + /* C */ v.append(2.81); + /* D */ (void) 0; +} + +void tst_Gdb::dumpQVector() +{ + prepare("dumpQVector"); + if (checkUninitialized) + run("A","{iname='local.v',addr='-',name='v',type='"NS"QVector<double>'," + "value='<not in scope>',numchild='0'}"); + next(); + run("B","{iname='local.v',addr='-',name='v',type='"NS"QVector<double>'," + "value='<0 items>',numchild='0'}"); + run("B","{iname='local.v',addr='-',name='v',type='"NS"QVector<double>'," + "value='<0 items>',numchild='0',children=[]}", "local.v"); + next(); + run("C","{iname='local.v',addr='-',name='v',type='"NS"QVector<double>'," + "value='<1 items>',numchild='1'}"); + run("C","{iname='local.v',addr='-',name='v',type='"NS"QVector<double>'," + "value='<1 items>',numchild='1',childtype='double'," + "childnumchild='0',children=[{value='-'}]}", // rounding... + "local.v"); + next(); + run("D","{iname='local.v',addr='-',name='v',type='"NS"QVector<double>'," + "value='<2 items>',numchild='2'}"); + run("D","{iname='local.v',addr='-',name='v',type='"NS"QVector<double>'," + "value='<2 items>',numchild='2',childtype='double'," + "childnumchild='0',children=[{value='-'},{value='-'}]}", + "local.v"); +} + ///////////////////////////////////////////////////////////////////////// // // Main