Debugger: Add dumper for Nim's string and sequence types

... and make the auto test infrastructure able to handle
a test. To pass the test, some Nim compiler must be
accessible.

Change-Id: I707aa72c0f3a2ea35c7131cba490cafb41617f6c
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2016-04-04 21:51:31 +02:00
committed by hjk
parent 21ba1f7b7a
commit e234bbd88b
3 changed files with 138 additions and 47 deletions

View File

@@ -1015,7 +1015,8 @@ class DumperBase:
if arrayByteSize == 0: if arrayByteSize == 0:
# This should not happen. But it does, see QTCREATORBUG-14755. # This should not happen. But it does, see QTCREATORBUG-14755.
# GDB/GCC produce sizeof == 0 for QProcess arr[3] # GDB/GCC produce sizeof == 0 for QProcess arr[3]
s = str(value.type) # And in the Nim string dumper.
s = value.type.name
itemCount = s[s.find('[')+1:s.find(']')] itemCount = s[s.find('[')+1:s.find(']')]
if not itemCount: if not itemCount:
itemCount = '100' itemCount = '100'

View File

@@ -161,6 +161,25 @@ def qdump__Eigen__Matrix(d, value):
s = s + 1 s = s + 1
#######################################################################
#
# Nim
#
#######################################################################
def qdump__NimStringDesc(d, value):
size, reserved = value.split('pp')
data = value.address() + 2 * d.ptrSize()
d.putCharArrayHelper(data, size, d.createType('char'), 'utf8')
def qdump__NimGenericSequence__(d, value, regex = "^TY[\d]+$"):
size, reserved = d.split('pp', value)
data = value.address() + 2 * d.ptrSize()
typeobj = value["data"].type.dereference()
d.putItemCount(size)
d.putArrayData(data, size, typeobj)
d.putBetterType("%s (%s[%s])" % (value.type.name, typeobj.name, size))
####################################################################### #######################################################################
# #
# D # D

View File

@@ -646,33 +646,11 @@ struct NetworkProfile {};
struct QmlProfile {}; struct QmlProfile {};
struct QmlPrivateProfile {}; struct QmlPrivateProfile {};
struct NimProfile {};
struct BigArrayProfile {}; struct BigArrayProfile {};
struct DataBase class Data
{
DataBase()
: useQt(false), useQHash(false),
forceC(false), engines(AllEngines),
glibcxxDebug(false), useDebugImage(false),
bigArray(false)
{}
mutable bool useQt;
mutable bool useQHash;
mutable bool forceC;
mutable int engines;
mutable bool glibcxxDebug;
mutable bool useDebugImage;
mutable bool bigArray;
mutable GdbVersion neededGdbVersion; // DEC. 70600
mutable LldbVersion neededLldbVersion;
mutable QtVersion neededQtVersion; // HEX! 0x50300
mutable GccVersion neededGccVersion; // DEC. 40702 for 4.7.2
mutable ClangVersion neededClangVersion; // DEC.
mutable BoostVersion neededBoostVersion; // DEC. 105400 for 1.54.0
};
class Data : public DataBase
{ {
public: public:
Data() {} Data() {}
@@ -854,14 +832,37 @@ public:
const Data &operator+(const ForceC &) const const Data &operator+(const ForceC &) const
{ {
forceC = true; mainFile = "main.c";
return *this; return *this;
} }
public: public:
mutable bool useQt = false;
mutable bool useQHash = false;
mutable int engines = AllEngines;
mutable int skipLevels = 0; // Levels to go 'up' before dumping variables.
mutable bool glibcxxDebug = false;
mutable bool useDebugImage = false;
mutable bool bigArray = false;
mutable GdbVersion neededGdbVersion; // DEC. 70600
mutable LldbVersion neededLldbVersion;
mutable QtVersion neededQtVersion; // HEX! 0x50300
mutable GccVersion neededGccVersion; // DEC. 40702 for 4.7.2
mutable ClangVersion neededClangVersion; // DEC.
mutable BoostVersion neededBoostVersion; // DEC. 105400 for 1.54.0
mutable QString configTest;
mutable QString allProfile; // Overrides anything below if not empty.
mutable QString allCode; // Overrides anything below if not empty.
mutable QString mainFile = "main.cpp";
mutable QString projectFile = "doit.pro";
mutable QString profileExtra; mutable QString profileExtra;
mutable QString includes; mutable QString includes;
mutable QString code; mutable QString code;
mutable QList<Check> checks; mutable QList<Check> checks;
}; };
@@ -1150,28 +1151,42 @@ void tst_Dumpers::dumper()
+ QByteArray::number(data.neededGccVersion.max)); + QByteArray::number(data.neededGccVersion.max));
} }
const char *mainFile = data.forceC ? "main.c" : "main.cpp"; if (!data.configTest.isEmpty()) {
QProcess configTest;
configTest.start(data.configTest);
QVERIFY(configTest.waitForFinished());
output = configTest.readAllStandardOutput();
error = configTest.readAllStandardError();
if (configTest.exitCode()) {
MSKIP_SINGLE("Configure test failed: '"
+ data.configTest.toUtf8() + "' " + output + ' ' + error);
}
}
QFile proFile(t->buildPath + QLatin1String("/doit.pro")); QFile proFile(t->buildPath + '/' + data.projectFile);
QVERIFY(proFile.open(QIODevice::ReadWrite)); QVERIFY(proFile.open(QIODevice::ReadWrite));
proFile.write("SOURCES = "); if (data.allProfile.isEmpty()) {
proFile.write(mainFile); proFile.write("SOURCES = ");
proFile.write("\nTARGET = doit\n"); proFile.write(data.mainFile.toUtf8());
proFile.write("\nCONFIG -= app_bundle\n"); proFile.write("\nTARGET = doit\n");
proFile.write("\nCONFIG -= release\n"); proFile.write("\nCONFIG -= app_bundle\n");
proFile.write("\nCONFIG += debug\n"); proFile.write("\nCONFIG -= release\n");
if (data.useQt) proFile.write("\nCONFIG += debug\n");
proFile.write("QT -= widgets gui\n"); if (data.useQt)
else proFile.write("QT -= widgets gui\n");
proFile.write("CONFIG -= QT\n"); else
if (m_useGLibCxxDebug) proFile.write("CONFIG -= QT\n");
proFile.write("DEFINES += _GLIBCXX_DEBUG\n"); if (m_useGLibCxxDebug)
if (m_debuggerEngine == GdbEngine && m_debuggerVersion < 70500) proFile.write("DEFINES += _GLIBCXX_DEBUG\n");
proFile.write("QMAKE_CXXFLAGS += -gdwarf-3\n"); if (m_debuggerEngine == GdbEngine && m_debuggerVersion < 70500)
proFile.write(data.profileExtra.toUtf8()); proFile.write("QMAKE_CXXFLAGS += -gdwarf-3\n");
proFile.write(data.profileExtra.toUtf8());
} else {
proFile.write(data.allProfile.toUtf8());
}
proFile.close(); proFile.close();
QFile source(t->buildPath + QLatin1Char('/') + QLatin1String(mainFile)); QFile source(t->buildPath + QLatin1Char('/') + data.mainFile);
QVERIFY(source.open(QIODevice::ReadWrite)); QVERIFY(source.open(QIODevice::ReadWrite));
QString fullCode = QString() + QString fullCode = QString() +
"\n\n#if defined(_MSC_VER)" + (data.useQt ? "\n\n#if defined(_MSC_VER)" + (data.useQt ?
@@ -1234,6 +1249,8 @@ void tst_Dumpers::dumper()
"\n BREAK;" "\n BREAK;"
"\n return 0;" "\n return 0;"
"\n}\n"; "\n}\n";
if (!data.allCode.isEmpty())
fullCode = data.allCode;
source.write(fullCode.toUtf8()); source.write(fullCode.toUtf8());
source.close(); source.close();
@@ -1251,7 +1268,12 @@ void tst_Dumpers::dumper()
output = qmake.readAllStandardOutput(); output = qmake.readAllStandardOutput();
error = qmake.readAllStandardError(); error = qmake.readAllStandardError();
//qDebug() << "stdout: " << output; //qDebug() << "stdout: " << output;
if (!error.isEmpty()) { qDebug() << error; QVERIFY(false); }
if (data.allProfile.isEmpty()) { // Nim...
if (!error.isEmpty()) {
qDebug() << error; QVERIFY(false);
}
}
QProcess make; QProcess make;
make.setWorkingDirectory(t->buildPath); make.setWorkingDirectory(t->buildPath);
@@ -1267,7 +1289,7 @@ void tst_Dumpers::dumper()
qDebug() << "\n------------------ CODE --------------------"; qDebug() << "\n------------------ CODE --------------------";
qDebug() << fullCode; qDebug() << fullCode;
qDebug() << "\n------------------ CODE --------------------"; qDebug() << "\n------------------ CODE --------------------";
qDebug() << ".pro: " << qPrintable(proFile.fileName()); qDebug() << "Project file: " << qPrintable(proFile.fileName());
} }
QByteArray dumperDir = DUMPERDIR; QByteArray dumperDir = DUMPERDIR;
@@ -1321,6 +1343,7 @@ void tst_Dumpers::dumper()
"python from gdbbridge import *\n" "python from gdbbridge import *\n"
"python theDumper.setupDumpers()\n" "python theDumper.setupDumpers()\n"
"run " + nograb + "\n" "run " + nograb + "\n"
"up " + QString::number(data.skipLevels) + "\n"
"python theDumper.fetchVariables({" "python theDumper.fetchVariables({"
"'token':2,'fancy':1,'forcens':1," "'token':2,'fancy':1,'forcens':1,"
"'autoderef':1,'dyntype':1,'passexceptions':1," "'autoderef':1,'dyntype':1,'passexceptions':1,"
@@ -6207,6 +6230,54 @@ void tst_Dumpers::dumper_data()
+ Check("v14.2", "[2]", "116", "@QChar") + Check("v14.2", "[2]", "116", "@QChar")
+ Check("v15", "\"utf16\"", "@QJSValue (QString)") + Check("v15", "\"utf16\"", "@QJSValue (QString)")
+ Check("v15.1", "[1]", "116", "@QChar"); + Check("v15.1", "[1]", "116", "@QChar");
#ifdef Q_OS_LINUX
// Hint: To open a failing test in Creator, do:
// touch qt_tst_dumpers_Nim_.../dummy.nimproject
// qtcreator qt_tst_dumpers_Nim_*/dummy.nimproject
Data nimData;
nimData.configTest = "which nim";
nimData.allProfile =
"CONFIG -= qt\n"
"# Prevents linking\n"
"TARGET=\n"
"# Overwrites qmake-generated 'all' target.\n"
"all.commands = nim c --debugInfo --lineDir:on --out:doit main.nim\n"
"all.depends = main.nim\n"
"all.CONFIG = phony\n\n"
"QMAKE_EXTRA_TARGETS += all\n";
nimData.allCode =
"type Mirror = ref object\n"
" tag:int\n"
" other:array[0..1, Mirror]\n\n"
"proc mainProc =\n"
" var name: string = \"Hello World\"\n"
" var i: int = 43\n"
" var x: seq[int]\n"
" x = @[1, 2, 3, 4, 5, 6]\n\n"
" # Crash it.\n"
" var m1 = Mirror(tag:1)\n"
" var m2 = Mirror(tag:2)\n"
" var m3 = Mirror(tag:3)\n\n"
" m1.other[0] = m2; m1.other[1] = m3\n"
" m2.other[0] = m1; m2.other[1] = m3\n"
" m3.other[0] = m1; m3.other[1] = m2\n\n"
" for i in 1..30000:\n"
//" echo i\n"
" var mx : Mirror; mx.deepCopy(m1)\n"
" m1 = mx\n\n"
"if isMainModule:\n"
" mainProc()\n";
nimData.mainFile = "main.nim";
nimData.skipLevels = 15;
QTest::newRow("Nim")
<< nimData
+ GdbEngine
+ Check("name", "\"Hello World\"", "NimStringDesc")
+ Check("x", "<6 items>", Pattern("TY.*NI.6..")) // Something like "TY95019 (NI[6])"
+ Check("x.2", "[2]", "3", "NI");
#endif
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])