[Interest] Should we unload() Qt plugins?
Stephen Kelly
steveire at gmail.com
Sun Sep 21 15:24:12 CEST 2014
Hello,
Here is a simple plugin interface:
#ifndef PluginIFace_H
#define PluginIFace_H
#include <QObject>
class PluginIFace
{
public:
virtual ~PluginIFace() {}
// Caller takes ownership of the returned QObject*
virtual QObject* getObject() = 0;
};
Q_DECLARE_INTERFACE(PluginIFace, "com.example.PluginIFace/1.0" )
#endif
and a concrete plugin which returns a custom SomeObject:
#ifndef MyPlugin_H
#define MyPlugin_H
#include "pluginiface.h"
class SomeObject : public QObject
{
Q_OBJECT
public:
SomeObject(QObject *parent = 0);
};
class MyPlugin : public QObject, public PluginIFace
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "com.example.PluginIFace")
Q_INTERFACES(PluginIFace)
public:
MyPlugin(QObject *parent = 0);
QObject* getObject() Q_DECL_OVERRIDE;
};
#endif
And its implementation:
#include "myplugin.h"
SomeObject::SomeObject(QObject *parent)
: QObject(parent)
{
}
MyPlugin::MyPlugin(QObject *parent)
: QObject(parent)
{
}
QObject* MyPlugin::getObject()
{
return new SomeObject;
}
And a user of the plugin:
QCoreApplication app(argc, argv);
QPluginLoader pl(BUILD_DIR "/libmyplugin.so");
pl.load();
With the above code, valgrind reports:
HEAP SUMMARY:
in use at exit: 5,380 bytes in 37 blocks
total heap usage: 162 allocs, 125 frees, 22,715 bytes allocated
32 bytes in 1 blocks are still reachable in loss record 12 of 37
at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
by 0x691068F: _dlerror_run (dlerror.c:141)
by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)
by 0x51567F5: QLibraryPrivate::loadPlugin() (qlibrary.cpp:585)
by 0x514C058: QPluginLoader::load() (qpluginloader.cpp:240)
by 0x400E71: main (in
/home/stephen/safe/dev/playground/plugintest/build/main)
64 bytes in 1 blocks are still reachable in loss record 25 of 37
at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
by 0x4007E06: expand_dynamic_string_token (dl-load.c:162)
by 0x4008D71: _dl_map_object (dl-load.c:2538)
by 0x4014A53: dl_open_worker (dl-open.c:235)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x40143BA: _dl_open (dl-open.c:661)
by 0x691002A: dlopen_doit (dlopen.c:66)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x691062C: _dlerror_run (dlerror.c:163)
by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)
64 bytes in 1 blocks are still reachable in loss record 26 of 37
at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
by 0x400BDC3: _dl_new_object (dl-object.c:165)
by 0x4006783: _dl_map_object_from_fd (dl-load.c:1059)
by 0x4008DFF: _dl_map_object (dl-load.c:2605)
by 0x4014A53: dl_open_worker (dl-open.c:235)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x40143BA: _dl_open (dl-open.c:661)
by 0x691002A: dlopen_doit (dlopen.c:66)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x691062C: _dlerror_run (dlerror.c:163)
by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
144 bytes in 1 blocks are still reachable in loss record 32 of 37
at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
by 0x40118A2: _dl_check_map_versions (dl-version.c:293)
by 0x4014EEC: dl_open_worker (dl-open.c:278)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x40143BA: _dl_open (dl-open.c:661)
by 0x691002A: dlopen_doit (dlopen.c:66)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x691062C: _dlerror_run (dlerror.c:163)
by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)
by 0x51567F5: QLibraryPrivate::loadPlugin() (qlibrary.cpp:585)
1,232 bytes in 1 blocks are still reachable in loss record 36 of 37
at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
by 0x400BB23: _dl_new_object (dl-object.c:75)
by 0x4006783: _dl_map_object_from_fd (dl-load.c:1059)
by 0x4008DFF: _dl_map_object (dl-load.c:2605)
by 0x4014A53: dl_open_worker (dl-open.c:235)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x40143BA: _dl_open (dl-open.c:661)
by 0x691002A: dlopen_doit (dlopen.c:66)
by 0x400FFF3: _dl_catch_error (dl-error.c:187)
by 0x691062C: _dlerror_run (dlerror.c:163)
by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
LEAK SUMMARY:
definitely lost: 0 bytes in 0 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 1,536 bytes in 5 blocks
suppressed: 3,844 bytes in 32 blocks
If I add
pl.unload();
below the
pl.load();
in the sample code above, some of the 'still reachable' reports go away:
Memcheck, a memory error detector
Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info
Command: ./main
HEAP SUMMARY:
in use at exit: 3,876 bytes in 33 blocks
total heap usage: 162 allocs, 129 frees, 22,715 bytes allocated
32 bytes in 1 blocks are still reachable in loss record 12 of 33
at 0x4C2CC70: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-
linux.so)
by 0x691068F: _dlerror_run (dlerror.c:141)
by 0x69100C0: dlopen@@GLIBC_2.2.5 (dlopen.c:87)
by 0x515B22F: QLibraryPrivate::load_sys() (qlibrary_unix.cpp:231)
by 0x515651A: QLibraryPrivate::load() (qlibrary.cpp:537)
by 0x51567F5: QLibraryPrivate::loadPlugin() (qlibrary.cpp:585)
by 0x514C058: QPluginLoader::load() (qpluginloader.cpp:240)
by 0x400ED1: main (in
/home/stephen/safe/dev/playground/plugintest/build/main)
LEAK SUMMARY:
definitely lost: 0 bytes in 0 blocks
indirectly lost: 0 bytes in 0 blocks
possibly lost: 0 bytes in 0 blocks
still reachable: 32 bytes in 1 blocks
suppressed: 3,844 bytes in 32 blocks
So, maybe we should unload() Qt plugins?
However, the plugin interface contains
// Caller takes ownership of the returned QObject*
virtual QObject* getObject() = 0;
If we do something like
QCoreApplication app(argc, argv);
QPluginLoader pl(BUILD_DIR "/libmyplugin.so");
pl.load();
QObject *pluginObject = qobject_cast<PluginIFace*>(pl.instance())-
>getObject();
pl.unload();
delete pluginObject;
Then we get a segfault because the destructor of the object pointed to
by pluginObject was defined in the plugin, which was just unloaded, so the
destructor doesn't exist anymore.
In more complex programs, it is difficult to know whether it is safe to
call pl.unload() because we might not know whether all objects created
through the plugin interface have already been destroyed.
So, should the guideline be: "Never call QPluginLoader::unload()", and
ignore 'still reachable' reports from valgrind?
Thanks,
Steve.
More information about the Interest
mailing list