blob: 7c66f3e09a9a975dd3304242a595a5f53770192b [file] [log] [blame]
Zack Rusin3acde362011-04-06 01:11:55 -04001#include "retracer.h"
2
Zack Rusinf389ae82011-04-10 19:27:28 -04003#include "apitracecall.h"
José Fonseca3f456402012-03-25 20:59:24 +01004#include "thumbnail.h"
Zack Rusinf389ae82011-04-10 19:27:28 -04005
Dan McCabe66dfdda2012-03-05 17:20:39 -08006#include "image.hpp"
7
Zack Rusin3acde362011-04-06 01:11:55 -04008#include <QDebug>
Zack Rusinf389ae82011-04-10 19:27:28 -04009#include <QVariant>
Dan McCabe66dfdda2012-03-05 17:20:39 -080010#include <QList>
11#include <QImage>
Zack Rusinf389ae82011-04-10 19:27:28 -040012
13#include <qjson/parser.h>
Zack Rusin3acde362011-04-06 01:11:55 -040014
15Retracer::Retracer(QObject *parent)
Zack Rusinf389ae82011-04-10 19:27:28 -040016 : QThread(parent),
Zack Rusin404a1ef2011-04-19 23:49:56 -040017 m_benchmarking(false),
Zack Rusin3acde362011-04-06 01:11:55 -040018 m_doubleBuffered(true),
19 m_captureState(false),
Zack Rusinf389ae82011-04-10 19:27:28 -040020 m_captureCall(0)
Zack Rusin3acde362011-04-06 01:11:55 -040021{
Zack Rusinf389ae82011-04-10 19:27:28 -040022#ifdef Q_OS_WIN
23 QString format = QLatin1String("%1;");
24#else
25 QString format = QLatin1String("%1:");
26#endif
José Fonseca27440922011-11-01 08:27:12 +000027 QString buildPath = format.arg(APITRACE_BINARY_DIR);
Zack Rusinf389ae82011-04-10 19:27:28 -040028 m_processEnvironment = QProcessEnvironment::systemEnvironment();
29 m_processEnvironment.insert("PATH", buildPath +
30 m_processEnvironment.value("PATH"));
31
32 qputenv("PATH",
33 m_processEnvironment.value("PATH").toLatin1());
Zack Rusin3acde362011-04-06 01:11:55 -040034}
35
36QString Retracer::fileName() const
37{
38 return m_fileName;
39}
40
41void Retracer::setFileName(const QString &name)
42{
43 m_fileName = name;
44}
45
José Fonseca62997b42011-11-27 15:16:34 +000046void Retracer::setAPI(trace::API api)
47{
48 m_api = api;
49}
50
Zack Rusin3acde362011-04-06 01:11:55 -040051bool Retracer::isBenchmarking() const
52{
53 return m_benchmarking;
54}
55
56void Retracer::setBenchmarking(bool bench)
57{
58 m_benchmarking = bench;
59}
60
61bool Retracer::isDoubleBuffered() const
62{
63 return m_doubleBuffered;
64}
65
66void Retracer::setDoubleBuffered(bool db)
67{
68 m_doubleBuffered = db;
69}
70
Zack Rusin3acde362011-04-06 01:11:55 -040071void Retracer::setCaptureAtCallNumber(qlonglong num)
72{
73 m_captureCall = num;
74}
75
76qlonglong Retracer::captureAtCallNumber() const
77{
78 return m_captureCall;
79}
80
81bool Retracer::captureState() const
82{
83 return m_captureState;
84}
85
86void Retracer::setCaptureState(bool enable)
87{
88 m_captureState = enable;
89}
90
Dan McCabe66dfdda2012-03-05 17:20:39 -080091bool Retracer::captureThumbnails() const
92{
93 return m_captureThumbnails;
94}
95
96void Retracer::setCaptureThumbnails(bool enable)
97{
98 m_captureThumbnails = enable;
99}
100
Zack Rusinf389ae82011-04-10 19:27:28 -0400101
102void Retracer::run()
103{
104 RetraceProcess *retrace = new RetraceProcess();
105 retrace->process()->setProcessEnvironment(m_processEnvironment);
106
107 retrace->setFileName(m_fileName);
José Fonseca62997b42011-11-27 15:16:34 +0000108 retrace->setAPI(m_api);
Zack Rusinf389ae82011-04-10 19:27:28 -0400109 retrace->setBenchmarking(m_benchmarking);
110 retrace->setDoubleBuffered(m_doubleBuffered);
111 retrace->setCaptureState(m_captureState);
Dan McCabe66dfdda2012-03-05 17:20:39 -0800112 retrace->setCaptureThumbnails(m_captureThumbnails);
Zack Rusinf389ae82011-04-10 19:27:28 -0400113 retrace->setCaptureAtCallNumber(m_captureCall);
114
115 connect(retrace, SIGNAL(finished(const QString&)),
116 this, SLOT(cleanup()));
117 connect(retrace, SIGNAL(error(const QString&)),
118 this, SLOT(cleanup()));
119 connect(retrace, SIGNAL(finished(const QString&)),
120 this, SIGNAL(finished(const QString&)));
121 connect(retrace, SIGNAL(error(const QString&)),
122 this, SIGNAL(error(const QString&)));
Zack Rusined40bc62011-08-28 17:11:02 -0400123 connect(retrace, SIGNAL(foundState(ApiTraceState*)),
124 this, SIGNAL(foundState(ApiTraceState*)));
Dan McCabe66dfdda2012-03-05 17:20:39 -0800125 connect(retrace, SIGNAL(foundThumbnails(const QList<QImage>&)),
126 this, SIGNAL(foundThumbnails(const QList<QImage>&)));
Zack Rusin10fd4772011-09-14 01:45:12 -0400127 connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
128 this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
Zack Rusinf389ae82011-04-10 19:27:28 -0400129
130 retrace->start();
131
132 exec();
133
134 /* means we need to kill the process */
135 if (retrace->process()->state() != QProcess::NotRunning) {
136 retrace->terminate();
137 }
138
139 delete retrace;
140}
141
142
143void RetraceProcess::start()
144{
José Fonseca62997b42011-11-27 15:16:34 +0000145 QString prog;
Zack Rusinf389ae82011-04-10 19:27:28 -0400146 QStringList arguments;
Zack Rusin16ae0362011-04-11 21:30:04 -0400147
José Fonseca62997b42011-11-27 15:16:34 +0000148 if (m_api == trace::API_GL) {
149 prog = QLatin1String("glretrace");
150 } else if (m_api == trace::API_EGL) {
151 prog = QLatin1String("eglretrace");
152 } else {
153 assert(0);
154 return;
155 }
156
Zack Rusin16ae0362011-04-11 21:30:04 -0400157 if (m_doubleBuffered) {
158 arguments << QLatin1String("-db");
José Fonseca1872d962011-06-01 19:49:13 +0100159 } else {
160 arguments << QLatin1String("-sb");
Zack Rusin16ae0362011-04-11 21:30:04 -0400161 }
162
Dan McCabe66dfdda2012-03-05 17:20:39 -0800163 if (m_captureState || m_captureThumbnails) {
164 if (m_captureState) {
165 arguments << QLatin1String("-D");
166 arguments << QString::number(m_captureCall);
167 }
168 if (m_captureThumbnails) {
169 arguments << QLatin1String("-s"); // emit snapshots
170 arguments << QLatin1String("-"); // emit to stdout
171 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400172 } else {
173 if (m_benchmarking) {
174 arguments << QLatin1String("-b");
175 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400176 }
177
178 arguments << m_fileName;
179
José Fonseca62997b42011-11-27 15:16:34 +0000180 m_process->start(prog, arguments);
Zack Rusinf389ae82011-04-10 19:27:28 -0400181}
182
183
José Fonseca56cd8ac2011-11-24 16:30:49 +0000184void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
Zack Rusin3acde362011-04-06 01:11:55 -0400185{
Zack Rusinf389ae82011-04-10 19:27:28 -0400186 QString msg;
José Fonseca56cd8ac2011-11-24 16:30:49 +0000187
188 if (exitStatus != QProcess::NormalExit) {
189 msg = QLatin1String("Process crashed");
190 } else if (exitCode != 0) {
191 msg = QLatin1String("Process exited with non zero exit code");
Zack Rusinf389ae82011-04-10 19:27:28 -0400192 } else {
Dan McCabe66dfdda2012-03-05 17:20:39 -0800193 if (m_captureState || m_captureThumbnails) {
194 if (m_captureState) {
195 bool ok = false;
José Fonseca676cd172012-03-24 09:46:24 +0000196 m_process->setReadChannel(QProcess::StandardOutput);
197 QVariantMap parsedJson = m_jsonParser->parse(m_process, &ok).toMap();
Dan McCabe66dfdda2012-03-05 17:20:39 -0800198 ApiTraceState *state = new ApiTraceState(parsedJson);
199 emit foundState(state);
200 msg = tr("State fetched.");
201 }
202 if (m_captureThumbnails) {
203 m_process->setReadChannel(QProcess::StandardOutput);
204
205 QList<QImage> thumbnails;
206
207 while (!m_process->atEnd()) {
208 unsigned channels = 0;
209 unsigned width = 0;
210 unsigned height = 0;
211
212 char header[512];
213 qint64 headerSize = 0;
214 int headerLines = 3; // assume no optional comment line
215
216 for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
217 qint64 headerRead = m_process->readLine(&header[headerSize], sizeof(header) - headerSize);
218
219 // if header actually contains optional comment line, ...
220 if (headerLine == 1 && header[headerSize] == '#') {
221 ++headerLines;
222 }
223
224 headerSize += headerRead;
225 }
226
227 const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
228
229 // if invalid PNM header was encountered, ...
230 if (header == headerEnd) {
231 qDebug() << "error: invalid snapshot stream encountered\n";
232 break;
233 }
234
235 //qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height << "\n";
236
237 QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
238
239 int rowBytes = channels * width;
240 for (int y = 0; y < height; ++y) {
241 unsigned char *scanLine = snapshot.scanLine(y);
242 m_process->read((char *) scanLine, rowBytes);
243 }
244
José Fonseca3f456402012-03-25 20:59:24 +0100245 QImage thumb = thumbnail(snapshot);
246 thumbnails.append(thumb);
Dan McCabe66dfdda2012-03-05 17:20:39 -0800247 }
248
249 emit foundThumbnails(thumbnails);
250 msg = tr("Thumbnails fetched.");
251 }
José Fonseca56cd8ac2011-11-24 16:30:49 +0000252 } else {
José Fonseca676cd172012-03-24 09:46:24 +0000253 QByteArray output;
Dan McCabe66dfdda2012-03-05 17:20:39 -0800254 output = m_process->readAllStandardOutput();
José Fonseca56cd8ac2011-11-24 16:30:49 +0000255 msg = QString::fromUtf8(output);
256 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400257 }
258
José Fonseca8fdf56c2012-03-24 10:06:56 +0000259 m_process->setReadChannel(QProcess::StandardError);
Zack Rusin10fd4772011-09-14 01:45:12 -0400260 QList<ApiTraceError> errors;
José Fonseca8fdf56c2012-03-24 10:06:56 +0000261 QRegExp regexp("(^\\d+): +(\\b\\w+\\b): ([^\\r\\n]+)[\\r\\n]*$");
262 while (!m_process->atEnd()) {
263 QString line = m_process->readLine();
Zack Rusinb39e1c62011-04-19 23:09:26 -0400264 if (regexp.indexIn(line) != -1) {
Zack Rusin10fd4772011-09-14 01:45:12 -0400265 ApiTraceError error;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400266 error.callIndex = regexp.cap(1).toInt();
267 error.type = regexp.cap(2);
268 error.message = regexp.cap(3);
269 errors.append(error);
270 }
271 }
272 if (!errors.isEmpty()) {
273 emit retraceErrors(errors);
274 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400275 emit finished(msg);
Zack Rusin3acde362011-04-06 01:11:55 -0400276}
277
Zack Rusinf389ae82011-04-10 19:27:28 -0400278void RetraceProcess::replayError(QProcess::ProcessError err)
Zack Rusin3acde362011-04-06 01:11:55 -0400279{
José Fonseca56cd8ac2011-11-24 16:30:49 +0000280 /*
281 * XXX: this function is likely unnecessary and should be eliminated given
282 * that replayFinished is always called, even on errors.
283 */
284
285#if 0
Zack Rusin3acde362011-04-06 01:11:55 -0400286 qDebug()<<"Process error = "<<err;
287 qDebug()<<"\terr = "<<m_process->readAllStandardError();
288 qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
José Fonseca56cd8ac2011-11-24 16:30:49 +0000289#endif
Zack Rusin3acde362011-04-06 01:11:55 -0400290
291 emit error(
292 tr("Couldn't execute the replay file '%1'").arg(m_fileName));
293}
294
Zack Rusin10fd4772011-09-14 01:45:12 -0400295Q_DECLARE_METATYPE(QList<ApiTraceError>);
Zack Rusinf389ae82011-04-10 19:27:28 -0400296RetraceProcess::RetraceProcess(QObject *parent)
297 : QObject(parent)
298{
299 m_process = new QProcess(this);
300 m_jsonParser = new QJson::Parser();
301
Zack Rusin10fd4772011-09-14 01:45:12 -0400302 qRegisterMetaType<QList<ApiTraceError> >();
Zack Rusinb39e1c62011-04-19 23:09:26 -0400303
Zack Rusinf389ae82011-04-10 19:27:28 -0400304 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
José Fonseca56cd8ac2011-11-24 16:30:49 +0000305 this, SLOT(replayFinished(int, QProcess::ExitStatus)));
Zack Rusinf389ae82011-04-10 19:27:28 -0400306 connect(m_process, SIGNAL(error(QProcess::ProcessError)),
307 this, SLOT(replayError(QProcess::ProcessError)));
308}
309
310QProcess * RetraceProcess::process() const
311{
312 return m_process;
313}
314
315QString RetraceProcess::fileName() const
316{
317 return m_fileName;
318}
319
320void RetraceProcess::setFileName(const QString &name)
321{
322 m_fileName = name;
323}
324
José Fonseca62997b42011-11-27 15:16:34 +0000325void RetraceProcess::setAPI(trace::API api)
326{
327 m_api = api;
328}
329
Zack Rusinf389ae82011-04-10 19:27:28 -0400330bool RetraceProcess::isBenchmarking() const
331{
332 return m_benchmarking;
333}
334
335void RetraceProcess::setBenchmarking(bool bench)
336{
337 m_benchmarking = bench;
338}
339
340bool RetraceProcess::isDoubleBuffered() const
341{
342 return m_doubleBuffered;
343}
344
345void RetraceProcess::setDoubleBuffered(bool db)
346{
347 m_doubleBuffered = db;
348}
349
350void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
351{
352 m_captureCall = num;
353}
354
355qlonglong RetraceProcess::captureAtCallNumber() const
356{
357 return m_captureCall;
358}
359
360bool RetraceProcess::captureState() const
361{
362 return m_captureState;
363}
364
365void RetraceProcess::setCaptureState(bool enable)
366{
367 m_captureState = enable;
368}
369
Dan McCabe66dfdda2012-03-05 17:20:39 -0800370bool RetraceProcess::captureThumbnails() const
371{
372 return m_captureThumbnails;
373}
374
375void RetraceProcess::setCaptureThumbnails(bool enable)
376{
377 m_captureThumbnails = enable;
378}
379
Zack Rusinf389ae82011-04-10 19:27:28 -0400380void RetraceProcess::terminate()
381{
382 if (m_process) {
383 m_process->terminate();
384 emit finished(tr("Process terminated."));
385 }
386}
387
388void Retracer::cleanup()
389{
390 quit();
391}
392
393RetraceProcess::~RetraceProcess()
394{
395 delete m_jsonParser;
396}
397
Zack Rusin3acde362011-04-06 01:11:55 -0400398#include "retracer.moc"