blob: fa220117f0c5bb9de24d490d0b5d5c07f2ed7e53 [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{
Zack Rusinf389ae82011-04-10 19:27:28 -0400185 QString msg;
José Fonseca56cd8ac2011-11-24 16:30:49 +0000186
187 if (exitStatus != QProcess::NormalExit) {
188 msg = QLatin1String("Process crashed");
189 } else if (exitCode != 0) {
190 msg = QLatin1String("Process exited with non zero exit code");
Zack Rusinf389ae82011-04-10 19:27:28 -0400191 } else {
Dan McCabe66dfdda2012-03-05 17:20:39 -0800192 if (m_captureState || m_captureThumbnails) {
193 if (m_captureState) {
194 bool ok = false;
José Fonseca676cd172012-03-24 09:46:24 +0000195 m_process->setReadChannel(QProcess::StandardOutput);
196 QVariantMap parsedJson = m_jsonParser->parse(m_process, &ok).toMap();
Dan McCabe66dfdda2012-03-05 17:20:39 -0800197 ApiTraceState *state = new ApiTraceState(parsedJson);
198 emit foundState(state);
199 msg = tr("State fetched.");
200 }
201 if (m_captureThumbnails) {
202 m_process->setReadChannel(QProcess::StandardOutput);
203
204 QList<QImage> thumbnails;
205
206 while (!m_process->atEnd()) {
207 unsigned channels = 0;
208 unsigned width = 0;
209 unsigned height = 0;
210
211 char header[512];
212 qint64 headerSize = 0;
213 int headerLines = 3; // assume no optional comment line
214
215 for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
216 qint64 headerRead = m_process->readLine(&header[headerSize], sizeof(header) - headerSize);
217
218 // if header actually contains optional comment line, ...
219 if (headerLine == 1 && header[headerSize] == '#') {
220 ++headerLines;
221 }
222
223 headerSize += headerRead;
224 }
225
226 const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
227
228 // if invalid PNM header was encountered, ...
229 if (header == headerEnd) {
230 qDebug() << "error: invalid snapshot stream encountered\n";
231 break;
232 }
233
234 //qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height << "\n";
235
236 QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
237
238 int rowBytes = channels * width;
239 for (int y = 0; y < height; ++y) {
240 unsigned char *scanLine = snapshot.scanLine(y);
241 m_process->read((char *) scanLine, rowBytes);
242 }
243
244 QImage thumbnail = snapshot.scaled(16, 16, Qt::KeepAspectRatio, Qt::FastTransformation);
245 thumbnails.append(thumbnail);
246 }
247
248 emit foundThumbnails(thumbnails);
249 msg = tr("Thumbnails fetched.");
250 }
José Fonseca56cd8ac2011-11-24 16:30:49 +0000251 } else {
José Fonseca676cd172012-03-24 09:46:24 +0000252 QByteArray output;
Dan McCabe66dfdda2012-03-05 17:20:39 -0800253 output = m_process->readAllStandardOutput();
José Fonseca56cd8ac2011-11-24 16:30:49 +0000254 msg = QString::fromUtf8(output);
255 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400256 }
257
José Fonseca8fdf56c2012-03-24 10:06:56 +0000258 m_process->setReadChannel(QProcess::StandardError);
Zack Rusin10fd4772011-09-14 01:45:12 -0400259 QList<ApiTraceError> errors;
José Fonseca8fdf56c2012-03-24 10:06:56 +0000260 QRegExp regexp("(^\\d+): +(\\b\\w+\\b): ([^\\r\\n]+)[\\r\\n]*$");
261 while (!m_process->atEnd()) {
262 QString line = m_process->readLine();
Zack Rusinb39e1c62011-04-19 23:09:26 -0400263 if (regexp.indexIn(line) != -1) {
Zack Rusin10fd4772011-09-14 01:45:12 -0400264 ApiTraceError error;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400265 error.callIndex = regexp.cap(1).toInt();
266 error.type = regexp.cap(2);
267 error.message = regexp.cap(3);
268 errors.append(error);
269 }
270 }
271 if (!errors.isEmpty()) {
272 emit retraceErrors(errors);
273 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400274 emit finished(msg);
Zack Rusin3acde362011-04-06 01:11:55 -0400275}
276
Zack Rusinf389ae82011-04-10 19:27:28 -0400277void RetraceProcess::replayError(QProcess::ProcessError err)
Zack Rusin3acde362011-04-06 01:11:55 -0400278{
José Fonseca56cd8ac2011-11-24 16:30:49 +0000279 /*
280 * XXX: this function is likely unnecessary and should be eliminated given
281 * that replayFinished is always called, even on errors.
282 */
283
284#if 0
Zack Rusin3acde362011-04-06 01:11:55 -0400285 qDebug()<<"Process error = "<<err;
286 qDebug()<<"\terr = "<<m_process->readAllStandardError();
287 qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
José Fonseca56cd8ac2011-11-24 16:30:49 +0000288#endif
Zack Rusin3acde362011-04-06 01:11:55 -0400289
290 emit error(
291 tr("Couldn't execute the replay file '%1'").arg(m_fileName));
292}
293
Zack Rusin10fd4772011-09-14 01:45:12 -0400294Q_DECLARE_METATYPE(QList<ApiTraceError>);
Zack Rusinf389ae82011-04-10 19:27:28 -0400295RetraceProcess::RetraceProcess(QObject *parent)
296 : QObject(parent)
297{
298 m_process = new QProcess(this);
299 m_jsonParser = new QJson::Parser();
300
Zack Rusin10fd4772011-09-14 01:45:12 -0400301 qRegisterMetaType<QList<ApiTraceError> >();
Zack Rusinb39e1c62011-04-19 23:09:26 -0400302
Zack Rusinf389ae82011-04-10 19:27:28 -0400303 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
José Fonseca56cd8ac2011-11-24 16:30:49 +0000304 this, SLOT(replayFinished(int, QProcess::ExitStatus)));
Zack Rusinf389ae82011-04-10 19:27:28 -0400305 connect(m_process, SIGNAL(error(QProcess::ProcessError)),
306 this, SLOT(replayError(QProcess::ProcessError)));
307}
308
309QProcess * RetraceProcess::process() const
310{
311 return m_process;
312}
313
314QString RetraceProcess::fileName() const
315{
316 return m_fileName;
317}
318
319void RetraceProcess::setFileName(const QString &name)
320{
321 m_fileName = name;
322}
323
José Fonseca62997b42011-11-27 15:16:34 +0000324void RetraceProcess::setAPI(trace::API api)
325{
326 m_api = api;
327}
328
Zack Rusinf389ae82011-04-10 19:27:28 -0400329bool RetraceProcess::isBenchmarking() const
330{
331 return m_benchmarking;
332}
333
334void RetraceProcess::setBenchmarking(bool bench)
335{
336 m_benchmarking = bench;
337}
338
339bool RetraceProcess::isDoubleBuffered() const
340{
341 return m_doubleBuffered;
342}
343
344void RetraceProcess::setDoubleBuffered(bool db)
345{
346 m_doubleBuffered = db;
347}
348
349void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
350{
351 m_captureCall = num;
352}
353
354qlonglong RetraceProcess::captureAtCallNumber() const
355{
356 return m_captureCall;
357}
358
359bool RetraceProcess::captureState() const
360{
361 return m_captureState;
362}
363
364void RetraceProcess::setCaptureState(bool enable)
365{
366 m_captureState = enable;
367}
368
Dan McCabe66dfdda2012-03-05 17:20:39 -0800369bool RetraceProcess::captureThumbnails() const
370{
371 return m_captureThumbnails;
372}
373
374void RetraceProcess::setCaptureThumbnails(bool enable)
375{
376 m_captureThumbnails = enable;
377}
378
Zack Rusinf389ae82011-04-10 19:27:28 -0400379void RetraceProcess::terminate()
380{
381 if (m_process) {
382 m_process->terminate();
383 emit finished(tr("Process terminated."));
384 }
385}
386
387void Retracer::cleanup()
388{
389 quit();
390}
391
392RetraceProcess::~RetraceProcess()
393{
394 delete m_jsonParser;
395}
396
Zack Rusin3acde362011-04-06 01:11:55 -0400397#include "retracer.moc"