# HG changeset patch # User Chris Cannam # Date 1452521936 0 # Node ID d57f9344db19b1508397ac4563415f74178fa457 # Parent 0f90a357cb2ac9fe749700ddd25db248f35c781b Check for plugin loadability before trying to load in the main process (POSIX only so far) diff -r 0f90a357cb2a -r d57f9344db19 plugin/FeatureExtractionPluginFactory.cpp --- a/plugin/FeatureExtractionPluginFactory.cpp Fri Jan 08 15:39:12 2016 +0000 +++ b/plugin/FeatureExtractionPluginFactory.cpp Mon Jan 11 14:18:56 2016 +0000 @@ -136,13 +136,59 @@ } vector +FeatureExtractionPluginFactory::winnowPluginCandidates(vector candidates) +{ + vector good, bad; + vector badStatuses; + + for (QString c: candidates) { + + PluginLoadStatus status = + TestPluginLoadability(c, "vampGetPluginDescriptor"); + + if (status == PluginLoadOK) { + good.push_back(c); + } else if (status == UnknownPluginLoadStatus) { + cerr << "WARNING: Unknown load status for plugin candidate \"" + << c << "\", continuing" << endl; + good.push_back(c); + } else { + bad.push_back(c); + badStatuses.push_back(status); + } + } + + if (!bad.empty()) { + QString warningMessage = "Failed to load plugins

Failed to load one or more plugin libraries:

    \n"; + for (int i = 0; i < bad.size(); ++i) { + QString m; + if (badStatuses[i] == PluginLoadFailedToLoadLibrary) { + m = "Failed to load library"; + } else if (badStatuses[i] == PluginLoadFailedToFindDescriptor) { + m = "Failed to query plugins from library after loading"; + } else if (badStatuses[i] == PluginLoadFailedElsewhere) { + m = "Unknown failure"; + } else { + m = "Success: internal error?"; + } + warningMessage += QString("
  • %1 (%2)
  • \n") + .arg(bad[i]) + .arg(m); + } + warningMessage += "
"; + cerr << warningMessage; //!!! for now! + } + return good; +} + +vector FeatureExtractionPluginFactory::getPluginIdentifiers() { Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers"); vector rv; - vector candidates = getPluginCandidateFiles(); - + vector candidates = winnowPluginCandidates(getPluginCandidateFiles()); + for (QString soname : candidates) { #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE diff -r 0f90a357cb2a -r d57f9344db19 plugin/FeatureExtractionPluginFactory.h --- a/plugin/FeatureExtractionPluginFactory.h Fri Jan 08 15:39:12 2016 +0000 +++ b/plugin/FeatureExtractionPluginFactory.h Mon Jan 11 14:18:56 2016 +0000 @@ -57,8 +57,9 @@ friend class PluginDeletionNotifyAdapter; void pluginDeleted(Vamp::Plugin *); std::map m_handleMap; - + std::vector getPluginCandidateFiles(); + std::vector winnowPluginCandidates(std::vector candidates); void generateTaxonomy(); }; diff -r 0f90a357cb2a -r d57f9344db19 system/System.cpp --- a/system/System.cpp Fri Jan 08 15:39:12 2016 +0000 +++ b/system/System.cpp Mon Jan 11 14:18:56 2016 +0000 @@ -325,3 +325,64 @@ double princarg(double a) { return mod(a + M_PI, -2 * M_PI) + M_PI; } float princargf(float a) { return float(princarg(a)); } +#ifndef _WIN32 + +#include +#include + +PluginLoadStatus +TestPluginLoadability(QString soname, QString descriptorFn) +{ + //!!! This is POSIX only, no equivalent on Windows, where we'll + //!!! have to do something completely different + + pid_t pid = fork(); + + if (pid < 0) { + return UnknownPluginLoadStatus; // fork failed + } + + if (pid == 0) { // the child process + + void *handle = DLOPEN(soname, RTLD_NOW | RTLD_LOCAL); + if (!handle) { + cerr << "isPluginLibraryLoadable: Failed to open plugin library \"" + << soname << "\": " << dlerror() << "\n"; + cerr << "exiting with status 1" << endl; + exit(1); + } + + void *fn = DLSYM(handle, descriptorFn.toLocal8Bit().data()); + if (!fn) { + cerr << "isPluginLibraryLoadable: Failed to find plugin descriptor function \"" << descriptorFn << "\" in library \"" << soname << "\": " << dlerror() << "\n"; + exit(2); + } + + exit(0); + + } else { // the parent process + + int status = 0; + + do { + waitpid(pid, &status, 0); + } while (WIFSTOPPED(status)); + + if (WIFEXITED(status)) { + switch (WEXITSTATUS(status)) { + case 0: return PluginLoadOK; // success + case 1: return PluginLoadFailedToLoadLibrary; + case 2: return PluginLoadFailedToFindDescriptor; + default: return PluginLoadFailedElsewhere; + } + } + + if (WIFSIGNALED(status)) { + return PluginLoadFailedElsewhere; + } + + return UnknownPluginLoadStatus; + } +} + +#endif diff -r 0f90a357cb2a -r d57f9344db19 system/System.h --- a/system/System.h Fri Jan 08 15:39:12 2016 +0000 +++ b/system/System.h Mon Jan 11 14:18:56 2016 +0000 @@ -154,6 +154,21 @@ extern void StoreStartupLocale(); extern void RestoreStartupLocale(); +enum PluginLoadStatus { + UnknownPluginLoadStatus, + PluginLoadOK, + PluginLoadFailedToLoadLibrary, + PluginLoadFailedToFindDescriptor, + PluginLoadFailedElsewhere +}; + +// Check whether a plugin library is loadable without crashing (may +// need to spawn an external process to do it). Descriptor fn is the +// name of a LADSPA/DSSI/Vamp-style descriptor function to try +// calling; may be an empty string if the plugin doesn't follow that +// convention. +PluginLoadStatus TestPluginLoadability(QString soname, QString descriptorFn); + #include #ifndef M_PI