Mercurial > hg > vamp-plugin-load-checker
changeset 2:2288c1d05c28
Simple Qt-based class to invoke the helper
author | Chris Cannam |
---|---|
date | Tue, 12 Apr 2016 17:38:57 +0100 |
parents | fbffc249990c |
children | 3bae396cf8e0 |
files | Makefile helper.cpp plugincandidates.cpp plugincandidates.h |
diffstat | 4 files changed, 240 insertions(+), 6 deletions(-) [+] |
line wrap: on
line diff
--- a/Makefile Tue Apr 12 13:33:00 2016 +0100 +++ b/Makefile Tue Apr 12 17:38:57 2016 +0100 @@ -1,9 +1,17 @@ + +all: helper plugincandidates CXXFLAGS := -Wall -Werror helper: helper.o $(CXX) -o $@ $< -ldl +# todo: qmake .pro file +CXXFLAGS += -I/usr/include/qt -I/usr/include/qt/QtCore -fPIC -std=c++11 + +plugincandidates: plugincandidates.o + $(CXX) -o $@ $< -lQt5Core -ldl + clean: - rm helper.o + rm helper.o plugincandidates.o
--- a/helper.cpp Tue Apr 12 13:33:00 2016 +0100 +++ b/helper.cpp Tue Apr 12 17:38:57 2016 +0100 @@ -1,3 +1,4 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ /** * Plugin Load Checker Helper @@ -20,11 +21,11 @@ * Output line for failed load of library libname.so: * FAILURE|/path/to/libname.so|Reason for failure if available * - * Note that sometimes plugins will crash completely on load, bringing - * down this program with them. If the program exits before all listed - * plugins have been checked, this means that the plugin following the - * last reported one has crashed. Typically the caller may want to run - * it again, omitting that plugin. + * Sometimes plugins will crash completely on load, bringing down this + * program with them. If the program exits before all listed plugins + * have been checked, this means that the plugin following the last + * reported one has crashed. Typically the caller may want to run it + * again, omitting that plugin. */ #ifdef _WIN32
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugincandidates.cpp Tue Apr 12 17:38:57 2016 +0100 @@ -0,0 +1,158 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#include "plugincandidates.h" + +#include <set> +#include <stdexcept> +#include <iostream> + +#include <QProcess> +#include <QDir> + +#ifdef _WIN32 +#define PLUGIN_GLOB "*.dll" +#define PATH_SEPARATOR ';' +#else +#define PATH_SEPARATOR ':' +#ifdef __APPLE__ +#define PLUGIN_GLOB "*.dylib *.so" +#else +#define PLUGIN_GLOB "*.so" +#endif +#endif + +using namespace std; + +PluginCandidates::PluginCandidates(string helperExecutableName) : + m_helper(helperExecutableName) +{ +} + +vector<string> +PluginCandidates::getCandidateLibrariesFor(string tag) +{ + return m_candidates[tag]; +} + +vector<PluginCandidates::FailureRec> +PluginCandidates::getFailedLibrariesFor(string tag) +{ + return m_failures[tag]; +} + +vector<string> +PluginCandidates::getLibrariesInPath(vector<string> path) +{ + vector<string> candidates; + + for (string dirname: path) { + +//#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE + cerr << "getLibrariesInPath: scanning directory " << dirname << endl; +//#endif + + QDir dir(dirname.c_str(), PLUGIN_GLOB, + QDir::Name | QDir::IgnoreCase, + QDir::Files | QDir::Readable); + + for (unsigned int i = 0; i < dir.count(); ++i) { + QString soname = dir.filePath(dir[i]); + candidates.push_back(soname.toStdString()); + } + } + + return candidates; +} + +void +PluginCandidates::scan(string tag, + vector<string> pluginPath, + string descriptorFunctionName) +{ + vector<string> libraries = getLibrariesInPath(pluginPath); + vector<string> remaining = libraries; + + int runlimit = 20; + int runcount = 0; + + vector<string> result; + + while (result.size() < libraries.size() && runcount < runlimit) { + vector<string> output = runHelper(remaining, descriptorFunctionName); + result.insert(result.end(), output.begin(), output.end()); + int shortfall = int(remaining.size()) - int(output.size()); + if (shortfall > 0) { + // Helper bailed out for some reason presumably associated + // with the plugin following the last one it reported + // on. Add a null entry for that one and continue with the + // following ones. + result.push_back(""); + if (shortfall == 1) { + remaining = vector<string>(); + } else { + remaining = vector<string> + (remaining.rbegin(), remaining.rbegin() + shortfall - 1); + } + } + ++runcount; + } + + recordResult(tag, result); +} + +vector<string> +PluginCandidates::runHelper(vector<string> libraries, string descriptor) +{ + vector<string> output; + cerr << "running helper with following library list:" << endl; + for (auto &lib: libraries) cerr << lib << endl; + + QProcess process; + process.setReadChannel(QProcess::StandardOutput); + process.start(m_helper.c_str(), { descriptor.c_str() }); + if (!process.waitForStarted()) { + cerr << "helper failed to start" << endl; + throw runtime_error("plugin load helper failed to start"); + } + for (auto &lib: libraries) { + process.write(lib.c_str(), lib.size()); + process.write("\n", 1); + } + + int buflen = 4096; + while (process.waitForReadyRead()) { + char buf[buflen]; + qint64 linelen = process.readLine(buf, buflen); +// cerr << "read line: " << buf; + if (linelen < 0) { + cerr << "read failed from plugin load helper" << endl; + return output; + } + output.push_back(buf); + if (output.size() == libraries.size()) { + process.close(); + process.waitForFinished(); + break; + } + } + + return output; +} + +void +PluginCandidates::recordResult(string tag, vector<string> result) +{ + cerr << "recordResult: not yet implemented, but result was:" << endl; + for (auto &r: result) cerr << r; + cerr << "(ends)" << endl; +} + +int main(int argc, char **argv) +{ + //!!! just a test + PluginCandidates candidates("./helper"); + candidates.scan("vamp", + { "/usr/lib/vamp", "/usr/local/lib/vamp" }, + "vampGetPluginDescriptor"); +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugincandidates.h Tue Apr 12 17:38:57 2016 +0100 @@ -0,0 +1,67 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +#ifndef PLUGIN_CANDIDATES_H +#define PLUGIN_CANDIDATES_H + +#include <string> +#include <vector> +#include <map> + +/** + * Class to identify and list candidate shared-library files possibly + * containing plugins. Uses a separate process (the "helper", whose + * executable name must be provided at construction) to test-load each + * library in order to winnow out any that fail to load or crash on + * load. + * + * Requires C++11 and the Qt5 QtCore library. + */ +class PluginCandidates +{ + typedef std::vector<std::string> stringlist; + +public: + /** Construct a PluginCandidates scanner that uses the given + * executable as its load check helper. + */ + PluginCandidates(std::string helperExecutableName); + + /** Scan the libraries found in the given plugin path (i.e. list + * of plugin directories), checking that the given descriptor + * function can be looked up in each. Store the results + * internally, associated with the given (arbitrary) tag, for + * later querying using getCandidateLibrariesFor() and + * getFailedLibrariesFor(). + * + * Not thread-safe. + */ + void scan(std::string tag, + stringlist pluginPath, + std::string descriptorFunctionName); + + /** Return list of plugin library paths that were checked + * successfully during the scan for the given tag. + */ + stringlist getCandidateLibrariesFor(std::string tag); + + struct FailureRec { + std::string library; + std::string message; + }; + + /** Return list of failure reports arising from the prior scan for + * the given tag. + */ + std::vector<FailureRec> getFailedLibrariesFor(std::string tag); + +private: + std::string m_helper; + std::map<std::string, stringlist> m_candidates; + std::map<std::string, std::vector<FailureRec> > m_failures; + + stringlist getLibrariesInPath(stringlist path); + stringlist runHelper(stringlist libraries, std::string descriptor); + void recordResult(std::string tag, stringlist results); +}; + +#endif