3 #include "apitracecall.h"
13 #include <qjson/parser.h>
15 Retracer::Retracer(QObject *parent)
17 m_benchmarking(false),
18 m_doubleBuffered(true),
19 m_captureState(false),
23 QString format = QLatin1String("%1;");
25 QString format = QLatin1String("%1:");
27 QString buildPath = format.arg(APITRACE_BINARY_DIR);
28 m_processEnvironment = QProcessEnvironment::systemEnvironment();
29 m_processEnvironment.insert("PATH", buildPath +
30 m_processEnvironment.value("PATH"));
33 m_processEnvironment.value("PATH").toLatin1());
36 QString Retracer::fileName() const
41 void Retracer::setFileName(const QString &name)
46 void Retracer::setAPI(trace::API api)
51 bool Retracer::isBenchmarking() const
53 return m_benchmarking;
56 void Retracer::setBenchmarking(bool bench)
58 m_benchmarking = bench;
61 bool Retracer::isDoubleBuffered() const
63 return m_doubleBuffered;
66 void Retracer::setDoubleBuffered(bool db)
68 m_doubleBuffered = db;
71 void Retracer::setCaptureAtCallNumber(qlonglong num)
76 qlonglong Retracer::captureAtCallNumber() const
81 bool Retracer::captureState() const
83 return m_captureState;
86 void Retracer::setCaptureState(bool enable)
88 m_captureState = enable;
91 bool Retracer::captureThumbnails() const
93 return m_captureThumbnails;
96 void Retracer::setCaptureThumbnails(bool enable)
98 m_captureThumbnails = enable;
104 RetraceProcess *retrace = new RetraceProcess();
105 retrace->process()->setProcessEnvironment(m_processEnvironment);
107 retrace->setFileName(m_fileName);
108 retrace->setAPI(m_api);
109 retrace->setBenchmarking(m_benchmarking);
110 retrace->setDoubleBuffered(m_doubleBuffered);
111 retrace->setCaptureState(m_captureState);
112 retrace->setCaptureThumbnails(m_captureThumbnails);
113 retrace->setCaptureAtCallNumber(m_captureCall);
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&)));
123 connect(retrace, SIGNAL(foundState(ApiTraceState*)),
124 this, SIGNAL(foundState(ApiTraceState*)));
125 connect(retrace, SIGNAL(foundThumbnails(const QList<QImage>&)),
126 this, SIGNAL(foundThumbnails(const QList<QImage>&)));
127 connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
128 this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
134 /* means we need to kill the process */
135 if (retrace->process()->state() != QProcess::NotRunning) {
136 retrace->terminate();
143 void RetraceProcess::start()
146 QStringList arguments;
148 if (m_api == trace::API_GL) {
149 prog = QLatin1String("glretrace");
150 } else if (m_api == trace::API_EGL) {
151 prog = QLatin1String("eglretrace");
157 if (m_doubleBuffered) {
158 arguments << QLatin1String("-db");
160 arguments << QLatin1String("-sb");
163 if (m_captureState || m_captureThumbnails) {
164 if (m_captureState) {
165 arguments << QLatin1String("-D");
166 arguments << QString::number(m_captureCall);
168 if (m_captureThumbnails) {
169 arguments << QLatin1String("-s"); // emit snapshots
170 arguments << QLatin1String("-"); // emit to stdout
173 if (m_benchmarking) {
174 arguments << QLatin1String("-b");
178 arguments << m_fileName;
180 m_process->start(prog, arguments);
184 void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
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");
193 if (m_captureState || m_captureThumbnails) {
194 if (m_captureState) {
196 m_process->setReadChannel(QProcess::StandardOutput);
197 QVariantMap parsedJson = m_jsonParser->parse(m_process, &ok).toMap();
198 ApiTraceState *state = new ApiTraceState(parsedJson);
199 emit foundState(state);
200 msg = tr("State fetched.");
202 if (m_captureThumbnails) {
203 m_process->setReadChannel(QProcess::StandardOutput);
205 QList<QImage> thumbnails;
207 while (!m_process->atEnd()) {
208 unsigned channels = 0;
213 qint64 headerSize = 0;
214 int headerLines = 3; // assume no optional comment line
216 for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
217 qint64 headerRead = m_process->readLine(&header[headerSize], sizeof(header) - headerSize);
219 // if header actually contains optional comment line, ...
220 if (headerLine == 1 && header[headerSize] == '#') {
224 headerSize += headerRead;
227 const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
229 // if invalid PNM header was encountered, ...
230 if (header == headerEnd) {
231 qDebug() << "error: invalid snapshot stream encountered\n";
235 //qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height << "\n";
237 QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
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);
245 QImage thumb = thumbnail(snapshot);
246 thumbnails.append(thumb);
249 emit foundThumbnails(thumbnails);
250 msg = tr("Thumbnails fetched.");
254 output = m_process->readAllStandardOutput();
255 msg = QString::fromUtf8(output);
259 m_process->setReadChannel(QProcess::StandardError);
260 QList<ApiTraceError> errors;
261 QRegExp regexp("(^\\d+): +(\\b\\w+\\b): ([^\\r\\n]+)[\\r\\n]*$");
262 while (!m_process->atEnd()) {
263 QString line = m_process->readLine();
264 if (regexp.indexIn(line) != -1) {
266 error.callIndex = regexp.cap(1).toInt();
267 error.type = regexp.cap(2);
268 error.message = regexp.cap(3);
269 errors.append(error);
272 if (!errors.isEmpty()) {
273 emit retraceErrors(errors);
278 void RetraceProcess::replayError(QProcess::ProcessError err)
281 * XXX: this function is likely unnecessary and should be eliminated given
282 * that replayFinished is always called, even on errors.
286 qDebug()<<"Process error = "<<err;
287 qDebug()<<"\terr = "<<m_process->readAllStandardError();
288 qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
292 tr("Couldn't execute the replay file '%1'").arg(m_fileName));
295 Q_DECLARE_METATYPE(QList<ApiTraceError>);
296 RetraceProcess::RetraceProcess(QObject *parent)
299 m_process = new QProcess(this);
300 m_jsonParser = new QJson::Parser();
302 qRegisterMetaType<QList<ApiTraceError> >();
304 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
305 this, SLOT(replayFinished(int, QProcess::ExitStatus)));
306 connect(m_process, SIGNAL(error(QProcess::ProcessError)),
307 this, SLOT(replayError(QProcess::ProcessError)));
310 QProcess * RetraceProcess::process() const
315 QString RetraceProcess::fileName() const
320 void RetraceProcess::setFileName(const QString &name)
325 void RetraceProcess::setAPI(trace::API api)
330 bool RetraceProcess::isBenchmarking() const
332 return m_benchmarking;
335 void RetraceProcess::setBenchmarking(bool bench)
337 m_benchmarking = bench;
340 bool RetraceProcess::isDoubleBuffered() const
342 return m_doubleBuffered;
345 void RetraceProcess::setDoubleBuffered(bool db)
347 m_doubleBuffered = db;
350 void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
355 qlonglong RetraceProcess::captureAtCallNumber() const
357 return m_captureCall;
360 bool RetraceProcess::captureState() const
362 return m_captureState;
365 void RetraceProcess::setCaptureState(bool enable)
367 m_captureState = enable;
370 bool RetraceProcess::captureThumbnails() const
372 return m_captureThumbnails;
375 void RetraceProcess::setCaptureThumbnails(bool enable)
377 m_captureThumbnails = enable;
380 void RetraceProcess::terminate()
383 m_process->terminate();
384 emit finished(tr("Process terminated."));
388 void Retracer::cleanup()
393 RetraceProcess::~RetraceProcess()
398 #include "retracer.moc"