4 #include "apitracecall.h"
13 #include <qjson/parser.h>
15 Q_DECLARE_METATYPE(QList<ApiTraceError>);
17 Retracer::Retracer(QObject *parent)
19 m_benchmarking(false),
20 m_doubleBuffered(true),
21 m_captureState(false),
24 qRegisterMetaType<QList<ApiTraceError> >();
27 QString format = QLatin1String("%1;");
29 QString format = QLatin1String("%1:");
31 QString buildPath = format.arg(APITRACE_BINARY_DIR);
32 m_processEnvironment = QProcessEnvironment::systemEnvironment();
33 m_processEnvironment.insert("PATH", buildPath +
34 m_processEnvironment.value("PATH"));
37 m_processEnvironment.value("PATH").toLatin1());
40 QString Retracer::fileName() const
45 void Retracer::setFileName(const QString &name)
50 void Retracer::setAPI(trace::API api)
55 bool Retracer::isBenchmarking() const
57 return m_benchmarking;
60 void Retracer::setBenchmarking(bool bench)
62 m_benchmarking = bench;
65 bool Retracer::isDoubleBuffered() const
67 return m_doubleBuffered;
70 void Retracer::setDoubleBuffered(bool db)
72 m_doubleBuffered = db;
75 void Retracer::setCaptureAtCallNumber(qlonglong num)
80 qlonglong Retracer::captureAtCallNumber() const
85 bool Retracer::captureState() const
87 return m_captureState;
90 void Retracer::setCaptureState(bool enable)
92 m_captureState = enable;
95 bool Retracer::captureThumbnails() const
97 return m_captureThumbnails;
100 void Retracer::setCaptureThumbnails(bool enable)
102 m_captureThumbnails = enable;
107 * Starting point for the retracing thread.
109 * Overrides QThread::run().
116 * Construct command line
120 QStringList arguments;
122 if (m_api == trace::API_GL) {
123 prog = QLatin1String("glretrace");
124 } else if (m_api == trace::API_EGL) {
125 prog = QLatin1String("eglretrace");
131 if (m_doubleBuffered) {
132 arguments << QLatin1String("-db");
134 arguments << QLatin1String("-sb");
137 if (m_captureState) {
138 arguments << QLatin1String("-D");
139 arguments << QString::number(m_captureCall);
140 } else if (m_captureThumbnails) {
141 arguments << QLatin1String("-s"); // emit snapshots
142 arguments << QLatin1String("-"); // emit to stdout
143 } else if (m_benchmarking) {
144 arguments << QLatin1String("-b");
147 arguments << m_fileName;
155 process.start(prog, arguments);
156 if (!process.waitForStarted(-1)) {
157 emit finished(QLatin1String("Could not start process"));
162 * Process standard output
165 QList<QImage> thumbnails;
166 QVariantMap parsedJson;
168 process.setReadChannel(QProcess::StandardOutput);
169 if (process.waitForReadyRead(-1)) {
170 if (m_captureState) {
172 * Parse JSON from the output.
174 * XXX: QJSON does not wait for QIODevice::waitForReadyRead so we
175 * need to buffer all stdout.
177 * XXX: QJSON's scanner is inneficient as it abuses single
178 * character QIODevice::peek (not cheap), instead of maintaining a
179 * lookahead character on its own.
182 if (!process.waitForFinished(-1)) {
187 QJson::Parser jsonParser;
188 parsedJson = jsonParser.parse(&process, &ok).toMap();
190 msg = QLatin1String("failed to parse JSON");
192 } else if (m_captureThumbnails) {
194 * Parse concatenated PNM images from output.
199 * QProcess::atEnd() documentation is wrong -- it will return
200 * true even when the process is running --, so try to handle
203 if (process.atEnd()) {
204 if (process.state() == QProcess::Running) {
205 if (!process.waitForReadyRead(-1)) {
211 unsigned channels = 0;
216 qint64 headerSize = 0;
217 int headerLines = 3; // assume no optional comment line
219 for (int headerLine = 0; headerLine < headerLines; ++headerLine) {
220 while (!process.canReadLine()) {
221 if (!process.waitForReadyRead(-1)) {
222 qDebug() << "QProcess::waitForReadyRead failed";
227 qint64 headerRead = process.readLine(&header[headerSize], sizeof(header) - headerSize);
229 // if header actually contains optional comment line, ...
230 if (headerLine == 1 && header[headerSize] == '#') {
234 headerSize += headerRead;
237 const char *headerEnd = image::readPNMHeader(header, headerSize, &channels, &width, &height);
239 // if invalid PNM header was encountered, ...
240 if (header == headerEnd) {
241 qDebug() << "error: invalid snapshot stream encountered";
245 // qDebug() << "channels: " << channels << ", width: " << width << ", height: " << height";
247 QImage snapshot = QImage(width, height, channels == 1 ? QImage::Format_Mono : QImage::Format_RGB888);
249 int rowBytes = channels * width;
250 for (int y = 0; y < height; ++y) {
251 unsigned char *scanLine = snapshot.scanLine(y);
253 while (process.bytesAvailable() < rowBytes) {
254 if (!process.waitForReadyRead(-1)) {
255 qDebug() << "QProcess::waitForReadyRead failed";
260 qint64 read = process.read((char *) scanLine, rowBytes);
261 Q_ASSERT(read == rowBytes);
264 QImage thumbnail = snapshot.scaled(16, 16, Qt::KeepAspectRatio, Qt::FastTransformation);
265 thumbnails.append(thumbnail);
268 Q_ASSERT(process.state() != QProcess::Running);
272 output = process.readAllStandardOutput();
273 msg = QString::fromUtf8(output);
278 * Wait for process termination
281 process.waitForFinished(-1);
283 if (process.exitStatus() != QProcess::NormalExit) {
284 msg = QLatin1String("Process crashed");
285 } else if (process.exitCode() != 0) {
286 msg = QLatin1String("Process exited with non zero exit code");
293 QList<ApiTraceError> errors;
294 process.setReadChannel(QProcess::StandardError);
295 QRegExp regexp("(^\\d+): +(\\b\\w+\\b): ([^\\r\\n]+)[\\r\\n]*$");
296 while (!process.atEnd()) {
297 QString line = process.readLine();
298 if (regexp.indexIn(line) != -1) {
300 error.callIndex = regexp.cap(1).toInt();
301 error.type = regexp.cap(2);
302 error.message = regexp.cap(3);
303 errors.append(error);
311 if (m_captureState) {
312 ApiTraceState *state = new ApiTraceState(parsedJson);
313 emit foundState(state);
314 msg = QLatin1String("State fetched.");
317 if (m_captureThumbnails && !thumbnails.isEmpty()) {
318 emit foundThumbnails(thumbnails);
321 if (!errors.isEmpty()) {
322 emit retraceErrors(errors);
328 #include "retracer.moc"