comparison plugincandidates.cpp @ 6:61dbb18f2369

Logging, timeouts
author Chris Cannam
date Wed, 13 Apr 2016 18:41:49 +0100
parents 74064d6f5e07
children
comparison
equal deleted inserted replaced
5:74064d6f5e07 6:61dbb18f2369
33 #include <stdexcept> 33 #include <stdexcept>
34 #include <iostream> 34 #include <iostream>
35 35
36 #include <QProcess> 36 #include <QProcess>
37 #include <QDir> 37 #include <QDir>
38 #include <QTime>
38 39
39 #if defined(_WIN32) 40 #if defined(_WIN32)
40 #define PLUGIN_GLOB "*.dll" 41 #define PLUGIN_GLOB "*.dll"
41 #elif defined(__APPLE__) 42 #elif defined(__APPLE__)
42 #define PLUGIN_GLOB "*.dylib *.so" 43 #define PLUGIN_GLOB "*.dylib *.so"
45 #endif 46 #endif
46 47
47 using namespace std; 48 using namespace std;
48 49
49 PluginCandidates::PluginCandidates(string helperExecutableName) : 50 PluginCandidates::PluginCandidates(string helperExecutableName) :
50 m_helper(helperExecutableName) 51 m_helper(helperExecutableName),
51 { 52 m_logCallback(0)
53 {
54 }
55
56 void
57 PluginCandidates::setLogCallback(LogCallback *cb)
58 {
59 m_logCallback = cb;
52 } 60 }
53 61
54 vector<string> 62 vector<string>
55 PluginCandidates::getCandidateLibrariesFor(string tag) const 63 PluginCandidates::getCandidateLibrariesFor(string tag) const
56 { 64 {
63 { 71 {
64 if (m_failures.find(tag) == m_failures.end()) return {}; 72 if (m_failures.find(tag) == m_failures.end()) return {};
65 else return m_failures.at(tag); 73 else return m_failures.at(tag);
66 } 74 }
67 75
76 void
77 PluginCandidates::log(string message)
78 {
79 if (m_logCallback) m_logCallback->log("PluginCandidates: " + message);
80 }
81
68 vector<string> 82 vector<string>
69 PluginCandidates::getLibrariesInPath(vector<string> path) 83 PluginCandidates::getLibrariesInPath(vector<string> path)
70 { 84 {
71 vector<string> candidates; 85 vector<string> candidates;
72 86
73 for (string dirname: path) { 87 for (string dirname: path) {
74 88
75 //#ifdef DEBUG_PLUGIN_SCAN_AND_INSTANTIATE 89 log("scanning directory " + dirname + "\n");
76 cerr << "getLibrariesInPath: scanning directory " << dirname << endl;
77 //#endif
78 90
79 QDir dir(dirname.c_str(), PLUGIN_GLOB, 91 QDir dir(dirname.c_str(), PLUGIN_GLOB,
80 QDir::Name | QDir::IgnoreCase, 92 QDir::Name | QDir::IgnoreCase,
81 QDir::Files | QDir::Readable); 93 QDir::Files | QDir::Readable);
82 94
109 if (shortfall > 0) { 121 if (shortfall > 0) {
110 // Helper bailed out for some reason presumably associated 122 // Helper bailed out for some reason presumably associated
111 // with the plugin following the last one it reported 123 // with the plugin following the last one it reported
112 // on. Add a failure entry for that one and continue with 124 // on. Add a failure entry for that one and continue with
113 // the following ones. 125 // the following ones.
114 cerr << "shortfall = " << shortfall << " (of " << remaining.size() << ")" << endl; 126 string failed = *(remaining.rbegin() + shortfall - 1);
115 result.push_back("FAILURE|" + 127 log("helper output ended before result for plugin " + failed + "\n");
116 *(remaining.rbegin() + shortfall - 1) + 128 result.push_back("FAILURE|" + failed + "|Plugin load check failed or timed out");
117 "|Plugin load check failed");
118 remaining = vector<string> 129 remaining = vector<string>
119 (remaining.rbegin(), remaining.rbegin() + shortfall - 1); 130 (remaining.rbegin(), remaining.rbegin() + shortfall - 1);
120 } 131 }
121 ++runcount; 132 ++runcount;
122 } 133 }
126 137
127 vector<string> 138 vector<string>
128 PluginCandidates::runHelper(vector<string> libraries, string descriptor) 139 PluginCandidates::runHelper(vector<string> libraries, string descriptor)
129 { 140 {
130 vector<string> output; 141 vector<string> output;
131 // cerr << "running helper with following library list:" << endl; 142
132 // for (auto &lib: libraries) cerr << lib << endl; 143 log("running helper with following library list:\n");
144 for (auto &lib: libraries) log(lib + "\n");
133 145
134 QProcess process; 146 QProcess process;
135 process.setReadChannel(QProcess::StandardOutput); 147 process.setReadChannel(QProcess::StandardOutput);
136 process.setProcessChannelMode(QProcess::ForwardedErrorChannel); 148 process.setProcessChannelMode(QProcess::ForwardedErrorChannel);
137 process.start(m_helper.c_str(), { descriptor.c_str() }); 149 process.start(m_helper.c_str(), { descriptor.c_str() });
141 } 153 }
142 for (auto &lib: libraries) { 154 for (auto &lib: libraries) {
143 process.write(lib.c_str(), lib.size()); 155 process.write(lib.c_str(), lib.size());
144 process.write("\n", 1); 156 process.write("\n", 1);
145 } 157 }
158
159 QTime t;
160 t.start();
161 int timeout = 3000; // ms
146 162
147 int buflen = 4096; 163 int buflen = 4096;
148 bool done = false; 164 bool done = false;
149 165
150 while (!done) { 166 while (!done) {
153 if (linelen > 0) { 169 if (linelen > 0) {
154 output.push_back(buf); 170 output.push_back(buf);
155 done = (output.size() == libraries.size()); 171 done = (output.size() == libraries.size());
156 } else if (linelen < 0) { 172 } else if (linelen < 0) {
157 // error case 173 // error case
174 log("received error code while reading from helper\n");
158 done = true; 175 done = true;
159 } else { 176 } else {
160 // no error, but no line read (could just be between 177 // no error, but no line read (could just be between
161 // lines, or could be eof) 178 // lines, or could be eof)
162 done = (process.state() == QProcess::NotRunning); 179 done = (process.state() == QProcess::NotRunning);
163 if (!done) process.waitForReadyRead(100); 180 if (!done) {
181 if (t.elapsed() > timeout) {
182 // this is purely an emergency measure
183 log("timeout: helper took too long, killing it\n");
184 process.kill();
185 done = true;
186 } else {
187 process.waitForReadyRead(200);
188 }
189 }
164 } 190 }
165 } 191 }
166 192
167 if (process.state() != QProcess::NotRunning) { 193 if (process.state() != QProcess::NotRunning) {
168 process.close(); 194 process.close();
177 { 203 {
178 for (auto &r: result) { 204 for (auto &r: result) {
179 205
180 QString s(r.c_str()); 206 QString s(r.c_str());
181 QStringList bits = s.split("|"); 207 QStringList bits = s.split("|");
208
209 log("read output line from helper: " + r);
210
182 if (bits.size() < 2 || bits.size() > 3) { 211 if (bits.size() < 2 || bits.size() > 3) {
183 cerr << "Invalid helper output line: \"" << r << "\"" << endl; 212 log("invalid output line (wrong number of |-separated fields)\n");
184 continue; 213 continue;
185 } 214 }
186 215
187 string status = bits[0].toStdString(); 216 string status = bits[0].toStdString();
188 217
197 226
198 } else if (status == "FAILURE") { 227 } else if (status == "FAILURE") {
199 m_failures[tag].push_back({ library, message }); 228 m_failures[tag].push_back({ library, message });
200 229
201 } else { 230 } else {
202 cerr << "Unexpected status " << status 231 log("unexpected status \"" + status + "\" in output line\n");
203 << " in helper output line: \"" << r << "\"" << endl; 232 }
204 } 233 }
205 } 234 }
206 } 235
207