comparison src/plugincandidates.cpp @ 55:81ce3ba92b16

Try to forward stderr from the checker to the log callback
author Chris Cannam
date Wed, 16 Jan 2019 14:25:28 +0000
parents 0d2d3c89fdf6
children 38ad8cba87a6
comparison
equal deleted inserted replaced
54:bb3d59462052 55:81ce3ba92b16
90 { 90 {
91 vector<string> candidates; 91 vector<string> candidates;
92 92
93 for (string dirname: path) { 93 for (string dirname: path) {
94 94
95 log("scanning directory " + dirname); 95 log("Scanning directory " + dirname);
96 96
97 QDir dir(dirname.c_str(), PLUGIN_GLOB, 97 QDir dir(dirname.c_str(), PLUGIN_GLOB,
98 QDir::Name | QDir::IgnoreCase, 98 QDir::Name | QDir::IgnoreCase,
99 QDir::Files | QDir::Readable); 99 QDir::Files | QDir::Readable);
100 100
114 vector<string> pluginPath, 114 vector<string> pluginPath,
115 string descriptorSymbolName) 115 string descriptorSymbolName)
116 { 116 {
117 string helperVersion = getHelperCompatibilityVersion(); 117 string helperVersion = getHelperCompatibilityVersion();
118 if (helperVersion != CHECKER_COMPATIBILITY_VERSION) { 118 if (helperVersion != CHECKER_COMPATIBILITY_VERSION) {
119 log("wrong plugin checker helper version found: expected v" + 119 log("Wrong plugin checker helper version found: expected v" +
120 string(CHECKER_COMPATIBILITY_VERSION) + ", found v" + 120 string(CHECKER_COMPATIBILITY_VERSION) + ", found v" +
121 helperVersion); 121 helperVersion);
122 throw runtime_error("wrong version of plugin load helper found"); 122 throw runtime_error("wrong version of plugin load helper found");
123 } 123 }
124 124
138 // Helper bailed out for some reason presumably associated 138 // Helper bailed out for some reason presumably associated
139 // with the plugin following the last one it reported 139 // with the plugin following the last one it reported
140 // on. Add a failure entry for that one and continue with 140 // on. Add a failure entry for that one and continue with
141 // the following ones. 141 // the following ones.
142 string failed = *(remaining.rbegin() + shortfall - 1); 142 string failed = *(remaining.rbegin() + shortfall - 1);
143 log("helper output ended before result for plugin " + failed); 143 log("Helper output ended before result for plugin " + failed);
144 result.push_back("FAILURE|" + failed + "|Plugin load check failed or timed out"); 144 result.push_back("FAILURE|" + failed + "|Plugin load check failed or timed out");
145 remaining = vector<string> 145 remaining = vector<string>
146 (remaining.rbegin(), remaining.rbegin() + shortfall - 1); 146 (remaining.rbegin(), remaining.rbegin() + shortfall - 1);
147 } 147 }
148 ++runcount; 148 ++runcount;
180 while (output.endsWith('\n') || output.endsWith('\r')) { 180 while (output.endsWith('\n') || output.endsWith('\r')) {
181 output.chop(1); 181 output.chop(1);
182 } 182 }
183 183
184 string versionString = QString(output).toStdString(); 184 string versionString = QString(output).toStdString();
185 log("read version string from helper: " + versionString); 185 log("Read version string from helper: " + versionString);
186 return versionString; 186 return versionString;
187 } 187 }
188 188
189 vector<string> 189 vector<string>
190 PluginCandidates::runHelper(vector<string> libraries, string descriptor) 190 PluginCandidates::runHelper(vector<string> libraries, string descriptor)
191 { 191 {
192 vector<string> output; 192 vector<string> output;
193 193
194 log("running helper " + m_helper + " with following library list:"); 194 log("Running helper " + m_helper + " with following library list:");
195 for (auto &lib: libraries) log(lib); 195 for (auto &lib: libraries) log(lib);
196 196
197 QProcess process; 197 QProcess process;
198 process.setReadChannel(QProcess::StandardOutput); 198 process.setReadChannel(QProcess::StandardOutput);
199 process.setProcessChannelMode(QProcess::ForwardedErrorChannel); 199
200 if (m_logCallback) {
201 log("Log callback is set: using separate-channels mode to gather stderr");
202 process.setProcessChannelMode(QProcess::SeparateChannels);
203 } else {
204 process.setProcessChannelMode(QProcess::ForwardedErrorChannel);
205 }
206
200 process.start(m_helper.c_str(), { descriptor.c_str() }); 207 process.start(m_helper.c_str(), { descriptor.c_str() });
201 208
202 if (!process.waitForStarted()) { 209 if (!process.waitForStarted()) {
203 QProcess::ProcessError err = process.error(); 210 QProcess::ProcessError err = process.error();
204 if (err == QProcess::FailedToStart) { 211 if (err == QProcess::FailedToStart) {
210 } else { 217 } else {
211 std::cerr << "Helper process " << m_helper 218 std::cerr << "Helper process " << m_helper
212 << " failed on startup with error code " 219 << " failed on startup with error code "
213 << err << std::endl; 220 << err << std::endl;
214 } 221 }
222 logErrors(&process);
215 throw runtime_error("plugin load helper failed to start"); 223 throw runtime_error("plugin load helper failed to start");
216 } 224 }
225
226 log("Helper " + m_helper + " started OK");
227 logErrors(&process);
217 228
218 for (auto &lib: libraries) { 229 for (auto &lib: libraries) {
219 process.write(lib.c_str(), lib.size()); 230 process.write(lib.c_str(), lib.size());
220 process.write("\n", 1); 231 process.write("\n", 1);
221 } 232 }
233 if (linelen > 0) { 244 if (linelen > 0) {
234 output.push_back(buf); 245 output.push_back(buf);
235 done = (output.size() == libraries.size()); 246 done = (output.size() == libraries.size());
236 } else if (linelen < 0) { 247 } else if (linelen < 0) {
237 // error case 248 // error case
238 log("received error code while reading from helper"); 249 log("Received error code while reading from helper");
239 done = true; 250 done = true;
240 } else { 251 } else {
241 // no error, but no line read (could just be between 252 // no error, but no line read (could just be between
242 // lines, or could be eof) 253 // lines, or could be eof)
243 done = (process.state() == QProcess::NotRunning); 254 done = (process.state() == QProcess::NotRunning);
244 if (!done) { 255 if (!done) {
245 if (t.elapsed() > timeout) { 256 if (t.elapsed() > timeout) {
246 // this is purely an emergency measure 257 // this is purely an emergency measure
247 log("timeout: helper took too long, killing it"); 258 log("Timeout: helper took too long, killing it");
248 process.kill(); 259 process.kill();
249 done = true; 260 done = true;
250 } else { 261 } else {
251 process.waitForReadyRead(200); 262 process.waitForReadyRead(200);
252 } 263 }
253 } 264 }
254 } 265 }
266 logErrors(&process);
255 } 267 }
256 268
257 if (process.state() != QProcess::NotRunning) { 269 if (process.state() != QProcess::NotRunning) {
258 process.close(); 270 process.close();
259 process.waitForFinished(); 271 process.waitForFinished();
260 } 272 logErrors(&process);
261 273 }
262 log("helper completed"); 274
275 log("Helper completed");
263 276
264 return output; 277 return output;
278 }
279
280 void
281 PluginCandidates::logErrors(QProcess *p)
282 {
283 p->setReadChannel(QProcess::StandardError);
284
285 qint64 byteCount = p->bytesAvailable();
286 if (byteCount == 0) {
287 log("Helper emitted no stderr output");
288 p->setReadChannel(QProcess::StandardOutput);
289 return;
290 }
291
292 QByteArray buffer = p->read(byteCount);
293 while (buffer.endsWith('\n') || buffer.endsWith('\r')) {
294 buffer.chop(1);
295 }
296 std::string str(buffer.constData(), buffer.size());
297 log("Helper stderr output follows:\n" + str);
298 log("Helper stderr output ends");
299
300 p->setReadChannel(QProcess::StandardOutput);
265 } 301 }
266 302
267 void 303 void
268 PluginCandidates::recordResult(string tag, vector<string> result) 304 PluginCandidates::recordResult(string tag, vector<string> result)
269 { 305 {
270 for (auto &r: result) { 306 for (auto &r: result) {
271 307
272 QString s(r.c_str()); 308 QString s(r.c_str());
273 QStringList bits = s.split("|"); 309 QStringList bits = s.split("|");
274 310
275 log(("read output line from helper: " + s.trimmed()).toStdString()); 311 log(("Read output line from helper: " + s.trimmed()).toStdString());
276 312
277 if (bits.size() < 2 || bits.size() > 3) { 313 if (bits.size() < 2 || bits.size() > 3) {
278 log("invalid output line (wrong number of |-separated fields)"); 314 log("Invalid output line (wrong number of |-separated fields)");
279 continue; 315 continue;
280 } 316 }
281 317
282 string status = bits[0].toStdString(); 318 string status = bits[0].toStdString();
283 319
303 if (codeRE.exactMatch(messageAndCode)) { 339 if (codeRE.exactMatch(messageAndCode)) {
304 QStringList caps(codeRE.capturedTexts()); 340 QStringList caps(codeRE.capturedTexts());
305 if (caps.length() == 3) { 341 if (caps.length() == 3) {
306 message = caps[1].toStdString(); 342 message = caps[1].toStdString();
307 code = PluginCheckCode(caps[2].toInt()); 343 code = PluginCheckCode(caps[2].toInt());
308 log("split failure report into message and failure code " 344 log("Split failure report into message and failure code "
309 + caps[2].toStdString()); 345 + caps[2].toStdString());
310 } else { 346 } else {
311 log("unable to split out failure code from report"); 347 log("Unable to split out failure code from report");
312 } 348 }
313 } else { 349 } else {
314 log("failure message does not give a failure code"); 350 log("Failure message does not give a failure code");
315 } 351 }
316 352
317 if (message == "") { 353 if (message == "") {
318 message = messageAndCode.toStdString(); 354 message = messageAndCode.toStdString();
319 } 355 }
320 356
321 m_failures[tag].push_back({ library, code, message }); 357 m_failures[tag].push_back({ library, code, message });
322 358
323 } else { 359 } else {
324 log("unexpected status \"" + status + "\" in output line"); 360 log("Unexpected status \"" + status + "\" in output line");
325 } 361 }
326 } 362 }
327 } 363 }
328 364