blob: 59e4093c0e09dd3af34183d891ce33b20ce8430d [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"
4
Dan McCabe66dfdda2012-03-05 17:20:39 -08005#include "image.hpp"
6
Zack Rusin3acde362011-04-06 01:11:55 -04007#include <QDebug>
Zack Rusinf389ae82011-04-10 19:27:28 -04008#include <QVariant>
Dan McCabe66dfdda2012-03-05 17:20:39 -08009#include <QList>
10#include <QImage>
Zack Rusinf389ae82011-04-10 19:27:28 -040011
12#include <qjson/parser.h>
Zack Rusin3acde362011-04-06 01:11:55 -040013
14Retracer::Retracer(QObject *parent)
Zack Rusinf389ae82011-04-10 19:27:28 -040015 : QThread(parent),
Zack Rusin404a1ef2011-04-19 23:49:56 -040016 m_benchmarking(false),
Zack Rusin3acde362011-04-06 01:11:55 -040017 m_doubleBuffered(true),
18 m_captureState(false),
Zack Rusinf389ae82011-04-10 19:27:28 -040019 m_captureCall(0)
Zack Rusin3acde362011-04-06 01:11:55 -040020{
Zack Rusinf389ae82011-04-10 19:27:28 -040021#ifdef Q_OS_WIN
22 QString format = QLatin1String("%1;");
23#else
24 QString format = QLatin1String("%1:");
25#endif
José Fonseca27440922011-11-01 08:27:12 +000026 QString buildPath = format.arg(APITRACE_BINARY_DIR);
Zack Rusinf389ae82011-04-10 19:27:28 -040027 m_processEnvironment = QProcessEnvironment::systemEnvironment();
28 m_processEnvironment.insert("PATH", buildPath +
29 m_processEnvironment.value("PATH"));
30
31 qputenv("PATH",
32 m_processEnvironment.value("PATH").toLatin1());
Zack Rusin3acde362011-04-06 01:11:55 -040033}
34
35QString Retracer::fileName() const
36{
37 return m_fileName;
38}
39
40void Retracer::setFileName(const QString &name)
41{
42 m_fileName = name;
43}
44
José Fonseca62997b42011-11-27 15:16:34 +000045void Retracer::setAPI(trace::API api)
46{
47 m_api = api;
48}
49
Zack Rusin3acde362011-04-06 01:11:55 -040050bool Retracer::isBenchmarking() const
51{
52 return m_benchmarking;
53}
54
55void Retracer::setBenchmarking(bool bench)
56{
57 m_benchmarking = bench;
58}
59
60bool Retracer::isDoubleBuffered() const
61{
62 return m_doubleBuffered;
63}
64
65void Retracer::setDoubleBuffered(bool db)
66{
67 m_doubleBuffered = db;
68}
69
Zack Rusin3acde362011-04-06 01:11:55 -040070void Retracer::setCaptureAtCallNumber(qlonglong num)
71{
72 m_captureCall = num;
73}
74
75qlonglong Retracer::captureAtCallNumber() const
76{
77 return m_captureCall;
78}
79
80bool Retracer::captureState() const
81{
82 return m_captureState;
83}
84
85void Retracer::setCaptureState(bool enable)
86{
87 m_captureState = enable;
88}
89
Dan McCabe66dfdda2012-03-05 17:20:39 -080090bool Retracer::captureThumbnails() const
91{
92 return m_captureThumbnails;
93}
94
95void Retracer::setCaptureThumbnails(bool enable)
96{
97 m_captureThumbnails = enable;
98}
99
Zack Rusinf389ae82011-04-10 19:27:28 -0400100
101void Retracer::run()
102{
103 RetraceProcess *retrace = new RetraceProcess();
104 retrace->process()->setProcessEnvironment(m_processEnvironment);
105
106 retrace->setFileName(m_fileName);
José Fonseca62997b42011-11-27 15:16:34 +0000107 retrace->setAPI(m_api);
Zack Rusinf389ae82011-04-10 19:27:28 -0400108 retrace->setBenchmarking(m_benchmarking);
109 retrace->setDoubleBuffered(m_doubleBuffered);
110 retrace->setCaptureState(m_captureState);
Dan McCabe66dfdda2012-03-05 17:20:39 -0800111 retrace->setCaptureThumbnails(m_captureThumbnails);
Zack Rusinf389ae82011-04-10 19:27:28 -0400112 retrace->setCaptureAtCallNumber(m_captureCall);
113
114 connect(retrace, SIGNAL(finished(const QString&)),
115 this, SLOT(cleanup()));
116 connect(retrace, SIGNAL(error(const QString&)),
117 this, SLOT(cleanup()));
118 connect(retrace, SIGNAL(finished(const QString&)),
119 this, SIGNAL(finished(const QString&)));
120 connect(retrace, SIGNAL(error(const QString&)),
121 this, SIGNAL(error(const QString&)));
Zack Rusined40bc62011-08-28 17:11:02 -0400122 connect(retrace, SIGNAL(foundState(ApiTraceState*)),
123 this, SIGNAL(foundState(ApiTraceState*)));
Dan McCabe66dfdda2012-03-05 17:20:39 -0800124 connect(retrace, SIGNAL(foundThumbnails(const QList<QImage>&)),
125 this, SIGNAL(foundThumbnails(const QList<QImage>&)));
Zack Rusin10fd4772011-09-14 01:45:12 -0400126 connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
127 this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
Zack Rusinf389ae82011-04-10 19:27:28 -0400128
129 retrace->start();
130
131 exec();
132
133 /* means we need to kill the process */
134 if (retrace->process()->state() != QProcess::NotRunning) {
135 retrace->terminate();
136 }
137
138 delete retrace;
139}
140
141
142void RetraceProcess::start()
143{
José Fonseca62997b42011-11-27 15:16:34 +0000144 QString prog;
Zack Rusinf389ae82011-04-10 19:27:28 -0400145 QStringList arguments;
Zack Rusin16ae0362011-04-11 21:30:04 -0400146
José Fonseca62997b42011-11-27 15:16:34 +0000147 if (m_api == trace::API_GL) {
148 prog = QLatin1String("glretrace");
149 } else if (m_api == trace::API_EGL) {
150 prog = QLatin1String("eglretrace");
151 } else {
152 assert(0);
153 return;
154 }
155
Zack Rusin16ae0362011-04-11 21:30:04 -0400156 if (m_doubleBuffered) {
157 arguments << QLatin1String("-db");
José Fonseca1872d962011-06-01 19:49:13 +0100158 } else {
159 arguments << QLatin1String("-sb");
Zack Rusin16ae0362011-04-11 21:30:04 -0400160 }
161
Dan McCabe66dfdda2012-03-05 17:20:39 -0800162 if (m_captureState || m_captureThumbnails) {
163 if (m_captureState) {
164 arguments << QLatin1String("-D");
165 arguments << QString::number(m_captureCall);
166 }
167 if (m_captureThumbnails) {
168 arguments << QLatin1String("-s"); // emit snapshots
169 arguments << QLatin1String("-"); // emit to stdout
170 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400171 } else {
172 if (m_benchmarking) {
173 arguments << QLatin1String("-b");
174 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400175 }
176
177 arguments << m_fileName;
178
José Fonseca62997b42011-11-27 15:16:34 +0000179 m_process->start(prog, arguments);
Zack Rusinf389ae82011-04-10 19:27:28 -0400180}
181
182
José Fonseca56cd8ac2011-11-24 16:30:49 +0000183void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
Zack Rusin3acde362011-04-06 01:11:55 -0400184{
Dan McCabe66dfdda2012-03-05 17:20:39 -0800185 QByteArray output;
Zack Rusinf389ae82011-04-10 19:27:28 -0400186 QString msg;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400187 QString errStr = m_process->readAllStandardError();
Zack Rusin3acde362011-04-06 01:11:55 -0400188
189#if 0
190 qDebug()<<"Process finished = ";
Zack Rusinb39e1c62011-04-19 23:09:26 -0400191 qDebug()<<"\terr = "<<errStr;
Zack Rusin3acde362011-04-06 01:11:55 -0400192 qDebug()<<"\tout = "<<output;
193#endif
José Fonseca56cd8ac2011-11-24 16:30:49 +0000194
195 if (exitStatus != QProcess::NormalExit) {
196 msg = QLatin1String("Process crashed");
197 } else if (exitCode != 0) {
198 msg = QLatin1String("Process exited with non zero exit code");
Zack Rusinf389ae82011-04-10 19:27:28 -0400199 } else {
Dan McCabe66dfdda2012-03-05 17:20:39 -0800200 if (m_captureState || m_captureThumbnails) {
201 if (m_captureState) {
202 bool ok = false;
203 output = m_process->readAllStandardOutput();
204 QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
205 ApiTraceState *state = new ApiTraceState(parsedJson);
206 emit foundState(state);
207 msg = tr("State fetched.");
208 }
209 if (m_captureThumbnails) {
210 m_process->setReadChannel(QProcess::StandardOutput);
211
212 QList<QImage> thumbnails;
213
214 while (!m_process->atEnd()) {
215 unsigned channels = 0;
216 unsigned width = 0;
217 unsigned height = 0;
218
219 char header[512];
220 qint64 headerSize = 0;
221 int headerLines = 3; // assume no optional comment line
222
223 for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
224 qint64 headerRead = m_process->readLine(&header[headerSize], sizeof(header) - headerSize);
225
226 // if header actually contains optional comment line, ...
227 if (headerLine == 1 && header[headerSize] == '#') {
228 ++headerLines;
229 }
230
231 headerSize += headerRead;
232 }
233
234 const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
235
236 // if invalid PNM header was encountered, ...
237 if (header == headerEnd) {
238 qDebug() << "error: invalid snapshot stream encountered\n";
239 break;
240 }
241
242 //qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height << "\n";
243
244 QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
245
246 int rowBytes = channels * width;
247 for (int y = 0; y < height; ++y) {
248 unsigned char *scanLine = snapshot.scanLine(y);
249 m_process->read((char *) scanLine, rowBytes);
250 }
251
252 QImage thumbnail = snapshot.scaled(16, 16, Qt::KeepAspectRatio, Qt::FastTransformation);
253 thumbnails.append(thumbnail);
254 }
255
256 emit foundThumbnails(thumbnails);
257 msg = tr("Thumbnails fetched.");
258 }
José Fonseca56cd8ac2011-11-24 16:30:49 +0000259 } else {
Dan McCabe66dfdda2012-03-05 17:20:39 -0800260 output = m_process->readAllStandardOutput();
José Fonseca56cd8ac2011-11-24 16:30:49 +0000261 msg = QString::fromUtf8(output);
262 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400263 }
264
Zack Rusinb39e1c62011-04-19 23:09:26 -0400265 QStringList errorLines = errStr.split('\n');
Zack Rusin10fd4772011-09-14 01:45:12 -0400266 QList<ApiTraceError> errors;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400267 QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
268 foreach(QString line, errorLines) {
269 if (regexp.indexIn(line) != -1) {
Zack Rusin10fd4772011-09-14 01:45:12 -0400270 ApiTraceError error;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400271 error.callIndex = regexp.cap(1).toInt();
272 error.type = regexp.cap(2);
273 error.message = regexp.cap(3);
274 errors.append(error);
275 }
276 }
277 if (!errors.isEmpty()) {
278 emit retraceErrors(errors);
279 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400280 emit finished(msg);
Zack Rusin3acde362011-04-06 01:11:55 -0400281}
282
Zack Rusinf389ae82011-04-10 19:27:28 -0400283void RetraceProcess::replayError(QProcess::ProcessError err)
Zack Rusin3acde362011-04-06 01:11:55 -0400284{
José Fonseca56cd8ac2011-11-24 16:30:49 +0000285 /*
286 * XXX: this function is likely unnecessary and should be eliminated given
287 * that replayFinished is always called, even on errors.
288 */
289
290#if 0
Zack Rusin3acde362011-04-06 01:11:55 -0400291 qDebug()<<"Process error = "<<err;
292 qDebug()<<"\terr = "<<m_process->readAllStandardError();
293 qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
José Fonseca56cd8ac2011-11-24 16:30:49 +0000294#endif
Zack Rusin3acde362011-04-06 01:11:55 -0400295
296 emit error(
297 tr("Couldn't execute the replay file '%1'").arg(m_fileName));
298}
299
Zack Rusin10fd4772011-09-14 01:45:12 -0400300Q_DECLARE_METATYPE(QList<ApiTraceError>);
Zack Rusinf389ae82011-04-10 19:27:28 -0400301RetraceProcess::RetraceProcess(QObject *parent)
302 : QObject(parent)
303{
304 m_process = new QProcess(this);
305 m_jsonParser = new QJson::Parser();
306
Zack Rusin10fd4772011-09-14 01:45:12 -0400307 qRegisterMetaType<QList<ApiTraceError> >();
Zack Rusinb39e1c62011-04-19 23:09:26 -0400308
Zack Rusinf389ae82011-04-10 19:27:28 -0400309 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
José Fonseca56cd8ac2011-11-24 16:30:49 +0000310 this, SLOT(replayFinished(int, QProcess::ExitStatus)));
Zack Rusinf389ae82011-04-10 19:27:28 -0400311 connect(m_process, SIGNAL(error(QProcess::ProcessError)),
312 this, SLOT(replayError(QProcess::ProcessError)));
313}
314
315QProcess * RetraceProcess::process() const
316{
317 return m_process;
318}
319
320QString RetraceProcess::fileName() const
321{
322 return m_fileName;
323}
324
325void RetraceProcess::setFileName(const QString &name)
326{
327 m_fileName = name;
328}
329
José Fonseca62997b42011-11-27 15:16:34 +0000330void RetraceProcess::setAPI(trace::API api)
331{
332 m_api = api;
333}
334
Zack Rusinf389ae82011-04-10 19:27:28 -0400335bool RetraceProcess::isBenchmarking() const
336{
337 return m_benchmarking;
338}
339
340void RetraceProcess::setBenchmarking(bool bench)
341{
342 m_benchmarking = bench;
343}
344
345bool RetraceProcess::isDoubleBuffered() const
346{
347 return m_doubleBuffered;
348}
349
350void RetraceProcess::setDoubleBuffered(bool db)
351{
352 m_doubleBuffered = db;
353}
354
355void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
356{
357 m_captureCall = num;
358}
359
360qlonglong RetraceProcess::captureAtCallNumber() const
361{
362 return m_captureCall;
363}
364
365bool RetraceProcess::captureState() const
366{
367 return m_captureState;
368}
369
370void RetraceProcess::setCaptureState(bool enable)
371{
372 m_captureState = enable;
373}
374
Dan McCabe66dfdda2012-03-05 17:20:39 -0800375bool RetraceProcess::captureThumbnails() const
376{
377 return m_captureThumbnails;
378}
379
380void RetraceProcess::setCaptureThumbnails(bool enable)
381{
382 m_captureThumbnails = enable;
383}
384
Zack Rusinf389ae82011-04-10 19:27:28 -0400385void RetraceProcess::terminate()
386{
387 if (m_process) {
388 m_process->terminate();
389 emit finished(tr("Process terminated."));
390 }
391}
392
393void Retracer::cleanup()
394{
395 quit();
396}
397
398RetraceProcess::~RetraceProcess()
399{
400 delete m_jsonParser;
401}
402
Zack Rusin3acde362011-04-06 01:11:55 -0400403#include "retracer.moc"