changeset 1148:9cdb4206aceb 3.0-integration

Check for plugin loadability before trying to load in the main process (POSIX only so far)
author Chris Cannam
date Mon, 11 Jan 2016 14:18:56 +0000
parents bff23ef9407e
children afed8be79032
files plugin/FeatureExtractionPluginFactory.cpp plugin/FeatureExtractionPluginFactory.h system/System.cpp system/System.h
diffstat 4 files changed, 126 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- 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<QString>
+FeatureExtractionPluginFactory::winnowPluginCandidates(vector<QString> candidates)
+{
+    vector<QString> good, bad;
+    vector<PluginLoadStatus> 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 = "<b>Failed to load plugins</b></p>Failed to load one or more plugin libraries:</p><ul>\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("<li>%1 (%2)</li>\n")
+                .arg(bad[i])
+                .arg(m);
+        }
+        warningMessage += "</ul>";
+        cerr << warningMessage; //!!! for now!
+    }
+    return good;
+}
+
+vector<QString>
 FeatureExtractionPluginFactory::getPluginIdentifiers()
 {
     Profiler profiler("FeatureExtractionPluginFactory::getPluginIdentifiers");
 
     vector<QString> rv;
-    vector<QString> candidates = getPluginCandidateFiles();
-
+    vector<QString> candidates = winnowPluginCandidates(getPluginCandidateFiles());
+    
     for (QString soname : candidates) {
 
 #ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE
--- 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<Vamp::Plugin *, void *> m_handleMap;
-
+    
     std::vector<QString> getPluginCandidateFiles();
+    std::vector<QString> winnowPluginCandidates(std::vector<QString> candidates);
     
     void generateTaxonomy();
 };
--- 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 <unistd.h>
+#include <sys/wait.h>
+
+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
--- 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 <cmath>
 
 #ifndef M_PI