3 #include "apitracecall.h"
12 #include <qjson/parser.h>
14 Retracer::Retracer(QObject *parent)
16 m_benchmarking(false),
17 m_doubleBuffered(true),
18 m_captureState(false),
22 QString format = QLatin1String("%1;");
24 QString format = QLatin1String("%1:");
26 QString buildPath = format.arg(APITRACE_BINARY_DIR);
27 m_processEnvironment = QProcessEnvironment::systemEnvironment();
28 m_processEnvironment.insert("PATH", buildPath +
29 m_processEnvironment.value("PATH"));
32 m_processEnvironment.value("PATH").toLatin1());
35 QString Retracer::fileName() const
40 void Retracer::setFileName(const QString &name)
45 void Retracer::setAPI(trace::API api)
50 bool Retracer::isBenchmarking() const
52 return m_benchmarking;
55 void Retracer::setBenchmarking(bool bench)
57 m_benchmarking = bench;
60 bool Retracer::isDoubleBuffered() const
62 return m_doubleBuffered;
65 void Retracer::setDoubleBuffered(bool db)
67 m_doubleBuffered = db;
70 void Retracer::setCaptureAtCallNumber(qlonglong num)
75 qlonglong Retracer::captureAtCallNumber() const
80 bool Retracer::captureState() const
82 return m_captureState;
85 void Retracer::setCaptureState(bool enable)
87 m_captureState = enable;
90 bool Retracer::captureThumbnails() const
92 return m_captureThumbnails;
95 void Retracer::setCaptureThumbnails(bool enable)
97 m_captureThumbnails = enable;
103 RetraceProcess *retrace = new RetraceProcess();
104 retrace->process()->setProcessEnvironment(m_processEnvironment);
106 retrace->setFileName(m_fileName);
107 retrace->setAPI(m_api);
108 retrace->setBenchmarking(m_benchmarking);
109 retrace->setDoubleBuffered(m_doubleBuffered);
110 retrace->setCaptureState(m_captureState);
111 retrace->setCaptureThumbnails(m_captureThumbnails);
112 retrace->setCaptureAtCallNumber(m_captureCall);
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&)));
122 connect(retrace, SIGNAL(foundState(ApiTraceState*)),
123 this, SIGNAL(foundState(ApiTraceState*)));
124 connect(retrace, SIGNAL(foundThumbnails(const QList<QImage>&)),
125 this, SIGNAL(foundThumbnails(const QList<QImage>&)));
126 connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
127 this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
133 /* means we need to kill the process */
134 if (retrace->process()->state() != QProcess::NotRunning) {
135 retrace->terminate();
142 void RetraceProcess::start()
145 QStringList arguments;
147 if (m_api == trace::API_GL) {
148 prog = QLatin1String("glretrace");
149 } else if (m_api == trace::API_EGL) {
150 prog = QLatin1String("eglretrace");
156 if (m_doubleBuffered) {
157 arguments << QLatin1String("-db");
159 arguments << QLatin1String("-sb");
162 if (m_captureState || m_captureThumbnails) {
163 if (m_captureState) {
164 arguments << QLatin1String("-D");
165 arguments << QString::number(m_captureCall);
167 if (m_captureThumbnails) {
168 arguments << QLatin1String("-s"); // emit snapshots
169 arguments << QLatin1String("-"); // emit to stdout
172 if (m_benchmarking) {
173 arguments << QLatin1String("-b");
177 arguments << m_fileName;
179 m_process->start(prog, arguments);
183 void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
187 QString errStr = m_process->readAllStandardError();
190 qDebug()<<"Process finished = ";
191 qDebug()<<"\terr = "<<errStr;
192 qDebug()<<"\tout = "<<output;
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");
200 if (m_captureState || m_captureThumbnails) {
201 if (m_captureState) {
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.");
209 if (m_captureThumbnails) {
210 m_process->setReadChannel(QProcess::StandardOutput);
212 QList<QImage> thumbnails;
214 while (!m_process->atEnd()) {
215 unsigned channels = 0;
220 qint64 headerSize = 0;
221 int headerLines = 3; // assume no optional comment line
223 for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
224 qint64 headerRead = m_process->readLine(&header[headerSize], sizeof(header) - headerSize);
226 // if header actually contains optional comment line, ...
227 if (headerLine == 1 && header[headerSize] == '#') {
231 headerSize += headerRead;
234 const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
236 // if invalid PNM header was encountered, ...
237 if (header == headerEnd) {
238 qDebug() << "error: invalid snapshot stream encountered\n";
242 //qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height << "\n";
244 QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
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);
252 QImage thumbnail = snapshot.scaled(16, 16, Qt::KeepAspectRatio, Qt::FastTransformation);
253 thumbnails.append(thumbnail);
256 emit foundThumbnails(thumbnails);
257 msg = tr("Thumbnails fetched.");
260 output = m_process->readAllStandardOutput();
261 msg = QString::fromUtf8(output);
265 QStringList errorLines = errStr.split('\n');
266 QList<ApiTraceError> errors;
267 QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
268 foreach(QString line, errorLines) {
269 if (regexp.indexIn(line) != -1) {
271 error.callIndex = regexp.cap(1).toInt();
272 error.type = regexp.cap(2);
273 error.message = regexp.cap(3);
274 errors.append(error);
277 if (!errors.isEmpty()) {
278 emit retraceErrors(errors);
283 void RetraceProcess::replayError(QProcess::ProcessError err)
286 * XXX: this function is likely unnecessary and should be eliminated given
287 * that replayFinished is always called, even on errors.
291 qDebug()<<"Process error = "<<err;
292 qDebug()<<"\terr = "<<m_process->readAllStandardError();
293 qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
297 tr("Couldn't execute the replay file '%1'").arg(m_fileName));
300 Q_DECLARE_METATYPE(QList<ApiTraceError>);
301 RetraceProcess::RetraceProcess(QObject *parent)
304 m_process = new QProcess(this);
305 m_jsonParser = new QJson::Parser();
307 qRegisterMetaType<QList<ApiTraceError> >();
309 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
310 this, SLOT(replayFinished(int, QProcess::ExitStatus)));
311 connect(m_process, SIGNAL(error(QProcess::ProcessError)),
312 this, SLOT(replayError(QProcess::ProcessError)));
315 QProcess * RetraceProcess::process() const
320 QString RetraceProcess::fileName() const
325 void RetraceProcess::setFileName(const QString &name)
330 void RetraceProcess::setAPI(trace::API api)
335 bool RetraceProcess::isBenchmarking() const
337 return m_benchmarking;
340 void RetraceProcess::setBenchmarking(bool bench)
342 m_benchmarking = bench;
345 bool RetraceProcess::isDoubleBuffered() const
347 return m_doubleBuffered;
350 void RetraceProcess::setDoubleBuffered(bool db)
352 m_doubleBuffered = db;
355 void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
360 qlonglong RetraceProcess::captureAtCallNumber() const
362 return m_captureCall;
365 bool RetraceProcess::captureState() const
367 return m_captureState;
370 void RetraceProcess::setCaptureState(bool enable)
372 m_captureState = enable;
375 bool RetraceProcess::captureThumbnails() const
377 return m_captureThumbnails;
380 void RetraceProcess::setCaptureThumbnails(bool enable)
382 m_captureThumbnails = enable;
385 void RetraceProcess::terminate()
388 m_process->terminate();
389 emit finished(tr("Process terminated."));
393 void Retracer::cleanup()
398 RetraceProcess::~RetraceProcess()
403 #include "retracer.moc"