Is there a way to properly run qmltestrunner
against a test, that uses a custom qml module, that is compiled with address sanitizer flags (e.g. --fsanitize=address
), and to be able to detect all leaks in this qmlmodule?
The problem is that qmltestrunner
is not linked against the address sanitizer, thus it complains regarding the linkage order when importing custom qmlmodule.
> qmltestrunner -import ../../../../build/qmlmodules/ -platform offscreen
==393140==ASan runtime does not come first in the initial library list; you should either link runtime to your application or manually preload it with LD_PRELOAD.
When I am trying to run a qmltestrunner
explicitly preloading sanitizer it detects qt internal leaks, but doesn't detect leaks in my qmlmodule:
> LD_PRELOAD=/lib/x86_64-linux-gnu/libasan.so.6 qmltestrunner -import ../../../../build/MyLib/qmlmodules/ -platform offscreen
is 0x12345********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.2.1 20210110), debian 11
PASS : qmltestrunner::TestIoauExample::initTestCase()
PASS : qmltestrunner::TestIoauExample::test_ioau_example()
PASS : qmltestrunner::TestIoauExample::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 2ms
********* Finished testing of qmltestrunner *********
=================================================================
==447337==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 500 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f3417a7 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
#1 0x7f3f06f6950a in ioau::IoauTester::HeightmapGenerator::HeightmapGenerator(QObject*) (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/libioautester-qmlplugin.so+0x1550a)
#2 0x7f3f06f62223 in void QQmlPrivate::createInto<ioau::IoauTester::HeightmapGenerator>(void*) (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/libioautester-qmlplugin.so+0xe223)
#3 0x7f3f0d4e2c89 in QQmlType::create(QObject**, void**, unsigned long) const (/lib/x86_64-linux-gnu/libQt5Qml.so.5+0x291c89)
#4 0x7f3f0d4c127c in QQmlComponent::QQmlComponent(QQmlEngine*, QObject*) (/lib/x86_64-linux-gnu/libQt5Qml.so.5+0x27027c)
Indirect leak of 168 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0dd51b53 in QScreen::QScreen(QPlatformScreen*) (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x165b53)
Indirect leak of 88 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0efb8d04 in QObject::QObject(QObject*) (/lib/x86_64-linux-gnu/libQt5Core.so.5+0x2e2d04)
Indirect leak of 40 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f076610a8 (/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/libqoffscreen.so+0x120a8)
Indirect leak of 24 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f07662167 (/usr/lib/x86_64-linux-gnu/qt5/plugins/platforms/libqoffscreen.so+0x13167)
Indirect leak of 16 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0ede4bb0 in QtSharedPointer::ExternalRefCountData::getAndRef(QObject const*) (/lib/x86_64-linux-gnu/libQt5Core.so.5+0x10ebb0)
Indirect leak of 16 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0dd01422 in QWindowSystemInterface::handleScreenAdded(QPlatformScreen*, bool) (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x115422)
#2 0x7773c9c2beee67ff (<unknown module>)
Indirect leak of 16 byte(s) in 1 object(s) allocated from:
#0 0x7f3f0f341647 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
#1 0x7f3f0dd0cadb in QPlatformScreen::QPlatformScreen() (/lib/x86_64-linux-gnu/libQt5Gui.so.5+0x120adb)
SUMMARY: AddressSanitizer: 868 byte(s) leaked in 8 allocation(s).
When I disable Asan
linkage order verification test qmltestrunner
passes tests, but my leak is not detected:
> ASAN_OPTIONS=verify_asan_link_order=false qmltestrunner -import ../../../../build/MyLib/qmlmodules/ -platform offscreen
is 0x12345********* Start testing of qmltestrunner *********
Config: Using QtTest library 5.15.2, Qt 5.15.2 (x86_64-little_endian-lp64 shared (dynamic) release build; by GCC 10.2.1 20210110), debian 11
PASS : qmltestrunner::TestIoauExample::initTestCase()
PASS : qmltestrunner::TestIoauExample::test_ioau_example()
PASS : qmltestrunner::TestIoauExample::cleanupTestCase()
Totals: 3 passed, 0 failed, 0 skipped, 0 blacklisted, 2ms
********* Finished testing of qmltestrunner *********
qml source file with TestCase
i am trying to run:
import QtQuick 2.0
import QtTest 1.2
import MyLibTester 0.1
Item {
TestCase {
name: "TestMyLibExample"
when: heightmapGenerator.ready
function test_mylib_example() {
verify(heightmapGenerator.ready)
}
}
resources: HeightmapGenerator {
id: heightmapGenerator
property bool loaded: false
property bool ready: false
Component.onCompleted: {
ready = true
loaded = true
}
}
}
a leak that I am trying to introduce and which is not detected when running qmltestrunner
--- a/mylib/qmlmodules/MyLibTester/src/HeightmapGenerator.cpp
+++ b/mylib/qmlmodules/MyLibTester/src/HeightmapGenerator.cpp
@@ -9,6 +9,8 @@
+#include "../tests/leak_tst.h"
namespace mylib
{
namespace MyLibTester
@@ -35,10 +37,13 @@ static float cubicSpline(float x)
}
-
HeightmapGenerator::HeightmapGenerator(QObject* parent)
: QObject(parent)
{
+ auto test_leak = leak_test(500);
+ std::cout << "is "
+ << (test_leak == reinterpret_cast<bool*>(0x12345) ? "NOT" : "")
+ << "0x12345";
}
However, when I compile a simple app with the same leak introduced sanitizer is able to detect the leak:
//leak_tst.h
#ifndef leak_tst
#define leak_tst
bool* leak_test(int n)
{
return new bool[n];
}
#endif
///leak_tst.cpp
#include "leak_tst.h"
#include <iostream>
int main()
{
auto test_leak = leak_test(500);
std::cout << "is "
<< (test_leak == reinterpret_cast<bool*>(0x12345) ? "NOT" : "")
<< "0x12345";
return 0;
}
> leak_tst
is 0x12345
=================================================================
==419360==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 500 byte(s) in 1 object(s) allocated from:
#0 0x7fb68db4b7a7 in operator new[](unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:102
#1 0x55b8687e3132 in main (/home/ifolbort/localstorage/workspace/ioau-testbed2/ioau/build/monolith/build/ioau/qmlmodules/IoauTester/tests/test-leak+0x1132)
#2 0x7fb68d734d09 in __libc_start_main ../csu/libc-start.c:308
SUMMARY: AddressSanitizer: 500 byte(s) leaked in 1 allocation(s).