blob: 073361718c82bd7f88eca2f5a99b3a9dd4d3e5f7 [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
Zack Rusin3acde362011-04-06 01:11:55 -04005#include <QDebug>
Zack Rusinf389ae82011-04-10 19:27:28 -04006#include <QVariant>
7
8#include <qjson/parser.h>
Zack Rusin3acde362011-04-06 01:11:55 -04009
10Retracer::Retracer(QObject *parent)
Zack Rusinf389ae82011-04-10 19:27:28 -040011 : QThread(parent),
Zack Rusin404a1ef2011-04-19 23:49:56 -040012 m_benchmarking(false),
Zack Rusin3acde362011-04-06 01:11:55 -040013 m_doubleBuffered(true),
14 m_captureState(false),
Zack Rusinf389ae82011-04-10 19:27:28 -040015 m_captureCall(0)
Zack Rusin3acde362011-04-06 01:11:55 -040016{
Zack Rusinf389ae82011-04-10 19:27:28 -040017#ifdef Q_OS_WIN
18 QString format = QLatin1String("%1;");
19#else
20 QString format = QLatin1String("%1:");
21#endif
José Fonseca27440922011-11-01 08:27:12 +000022 QString buildPath = format.arg(APITRACE_BINARY_DIR);
Zack Rusinf389ae82011-04-10 19:27:28 -040023 m_processEnvironment = QProcessEnvironment::systemEnvironment();
24 m_processEnvironment.insert("PATH", buildPath +
25 m_processEnvironment.value("PATH"));
26
27 qputenv("PATH",
28 m_processEnvironment.value("PATH").toLatin1());
Zack Rusin3acde362011-04-06 01:11:55 -040029}
30
31QString Retracer::fileName() const
32{
33 return m_fileName;
34}
35
36void Retracer::setFileName(const QString &name)
37{
38 m_fileName = name;
39}
40
41bool Retracer::isBenchmarking() const
42{
43 return m_benchmarking;
44}
45
46void Retracer::setBenchmarking(bool bench)
47{
48 m_benchmarking = bench;
49}
50
51bool Retracer::isDoubleBuffered() const
52{
53 return m_doubleBuffered;
54}
55
56void Retracer::setDoubleBuffered(bool db)
57{
58 m_doubleBuffered = db;
59}
60
Zack Rusin3acde362011-04-06 01:11:55 -040061void Retracer::setCaptureAtCallNumber(qlonglong num)
62{
63 m_captureCall = num;
64}
65
66qlonglong Retracer::captureAtCallNumber() const
67{
68 return m_captureCall;
69}
70
71bool Retracer::captureState() const
72{
73 return m_captureState;
74}
75
76void Retracer::setCaptureState(bool enable)
77{
78 m_captureState = enable;
79}
80
Zack Rusinf389ae82011-04-10 19:27:28 -040081
82void Retracer::run()
83{
84 RetraceProcess *retrace = new RetraceProcess();
85 retrace->process()->setProcessEnvironment(m_processEnvironment);
86
87 retrace->setFileName(m_fileName);
88 retrace->setBenchmarking(m_benchmarking);
89 retrace->setDoubleBuffered(m_doubleBuffered);
90 retrace->setCaptureState(m_captureState);
91 retrace->setCaptureAtCallNumber(m_captureCall);
92
93 connect(retrace, SIGNAL(finished(const QString&)),
94 this, SLOT(cleanup()));
95 connect(retrace, SIGNAL(error(const QString&)),
96 this, SLOT(cleanup()));
97 connect(retrace, SIGNAL(finished(const QString&)),
98 this, SIGNAL(finished(const QString&)));
99 connect(retrace, SIGNAL(error(const QString&)),
100 this, SIGNAL(error(const QString&)));
Zack Rusined40bc62011-08-28 17:11:02 -0400101 connect(retrace, SIGNAL(foundState(ApiTraceState*)),
102 this, SIGNAL(foundState(ApiTraceState*)));
Zack Rusin10fd4772011-09-14 01:45:12 -0400103 connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
104 this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
Zack Rusinf389ae82011-04-10 19:27:28 -0400105
106 retrace->start();
107
108 exec();
109
110 /* means we need to kill the process */
111 if (retrace->process()->state() != QProcess::NotRunning) {
112 retrace->terminate();
113 }
114
115 delete retrace;
116}
117
118
119void RetraceProcess::start()
120{
121 QStringList arguments;
Zack Rusin16ae0362011-04-11 21:30:04 -0400122
123 if (m_doubleBuffered) {
124 arguments << QLatin1String("-db");
José Fonseca1872d962011-06-01 19:49:13 +0100125 } else {
126 arguments << QLatin1String("-sb");
Zack Rusin16ae0362011-04-11 21:30:04 -0400127 }
128
Zack Rusinf389ae82011-04-10 19:27:28 -0400129 if (m_captureState) {
130 arguments << QLatin1String("-D");
131 arguments << QString::number(m_captureCall);
132 } else {
133 if (m_benchmarking) {
134 arguments << QLatin1String("-b");
135 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400136 }
137
138 arguments << m_fileName;
139
140 m_process->start(QLatin1String("glretrace"), arguments);
141}
142
143
José Fonseca56cd8ac2011-11-24 16:30:49 +0000144void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
Zack Rusin3acde362011-04-06 01:11:55 -0400145{
146 QByteArray output = m_process->readAllStandardOutput();
Zack Rusinf389ae82011-04-10 19:27:28 -0400147 QString msg;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400148 QString errStr = m_process->readAllStandardError();
Zack Rusin3acde362011-04-06 01:11:55 -0400149
150#if 0
151 qDebug()<<"Process finished = ";
Zack Rusinb39e1c62011-04-19 23:09:26 -0400152 qDebug()<<"\terr = "<<errStr;
Zack Rusin3acde362011-04-06 01:11:55 -0400153 qDebug()<<"\tout = "<<output;
154#endif
José Fonseca56cd8ac2011-11-24 16:30:49 +0000155
156 if (exitStatus != QProcess::NormalExit) {
157 msg = QLatin1String("Process crashed");
158 } else if (exitCode != 0) {
159 msg = QLatin1String("Process exited with non zero exit code");
Zack Rusinf389ae82011-04-10 19:27:28 -0400160 } else {
José Fonseca56cd8ac2011-11-24 16:30:49 +0000161 if (m_captureState) {
162 bool ok = false;
163 QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
164 ApiTraceState *state = new ApiTraceState(parsedJson);
165 emit foundState(state);
166 msg = tr("State fetched.");
167 } else {
168 msg = QString::fromUtf8(output);
169 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400170 }
171
Zack Rusinb39e1c62011-04-19 23:09:26 -0400172 QStringList errorLines = errStr.split('\n');
Zack Rusin10fd4772011-09-14 01:45:12 -0400173 QList<ApiTraceError> errors;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400174 QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
175 foreach(QString line, errorLines) {
176 if (regexp.indexIn(line) != -1) {
Zack Rusin10fd4772011-09-14 01:45:12 -0400177 ApiTraceError error;
Zack Rusinb39e1c62011-04-19 23:09:26 -0400178 error.callIndex = regexp.cap(1).toInt();
179 error.type = regexp.cap(2);
180 error.message = regexp.cap(3);
181 errors.append(error);
182 }
183 }
184 if (!errors.isEmpty()) {
185 emit retraceErrors(errors);
186 }
Zack Rusinf389ae82011-04-10 19:27:28 -0400187 emit finished(msg);
Zack Rusin3acde362011-04-06 01:11:55 -0400188}
189
Zack Rusinf389ae82011-04-10 19:27:28 -0400190void RetraceProcess::replayError(QProcess::ProcessError err)
Zack Rusin3acde362011-04-06 01:11:55 -0400191{
José Fonseca56cd8ac2011-11-24 16:30:49 +0000192 /*
193 * XXX: this function is likely unnecessary and should be eliminated given
194 * that replayFinished is always called, even on errors.
195 */
196
197#if 0
Zack Rusin3acde362011-04-06 01:11:55 -0400198 qDebug()<<"Process error = "<<err;
199 qDebug()<<"\terr = "<<m_process->readAllStandardError();
200 qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
José Fonseca56cd8ac2011-11-24 16:30:49 +0000201#endif
Zack Rusin3acde362011-04-06 01:11:55 -0400202
203 emit error(
204 tr("Couldn't execute the replay file '%1'").arg(m_fileName));
205}
206
Zack Rusin10fd4772011-09-14 01:45:12 -0400207Q_DECLARE_METATYPE(QList<ApiTraceError>);
Zack Rusinf389ae82011-04-10 19:27:28 -0400208RetraceProcess::RetraceProcess(QObject *parent)
209 : QObject(parent)
210{
211 m_process = new QProcess(this);
212 m_jsonParser = new QJson::Parser();
213
Zack Rusin10fd4772011-09-14 01:45:12 -0400214 qRegisterMetaType<QList<ApiTraceError> >();
Zack Rusinb39e1c62011-04-19 23:09:26 -0400215
Zack Rusinf389ae82011-04-10 19:27:28 -0400216 connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
José Fonseca56cd8ac2011-11-24 16:30:49 +0000217 this, SLOT(replayFinished(int, QProcess::ExitStatus)));
Zack Rusinf389ae82011-04-10 19:27:28 -0400218 connect(m_process, SIGNAL(error(QProcess::ProcessError)),
219 this, SLOT(replayError(QProcess::ProcessError)));
220}
221
222QProcess * RetraceProcess::process() const
223{
224 return m_process;
225}
226
227QString RetraceProcess::fileName() const
228{
229 return m_fileName;
230}
231
232void RetraceProcess::setFileName(const QString &name)
233{
234 m_fileName = name;
235}
236
237bool RetraceProcess::isBenchmarking() const
238{
239 return m_benchmarking;
240}
241
242void RetraceProcess::setBenchmarking(bool bench)
243{
244 m_benchmarking = bench;
245}
246
247bool RetraceProcess::isDoubleBuffered() const
248{
249 return m_doubleBuffered;
250}
251
252void RetraceProcess::setDoubleBuffered(bool db)
253{
254 m_doubleBuffered = db;
255}
256
257void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
258{
259 m_captureCall = num;
260}
261
262qlonglong RetraceProcess::captureAtCallNumber() const
263{
264 return m_captureCall;
265}
266
267bool RetraceProcess::captureState() const
268{
269 return m_captureState;
270}
271
272void RetraceProcess::setCaptureState(bool enable)
273{
274 m_captureState = enable;
275}
276
277void RetraceProcess::terminate()
278{
279 if (m_process) {
280 m_process->terminate();
281 emit finished(tr("Process terminated."));
282 }
283}
284
285void Retracer::cleanup()
286{
287 quit();
288}
289
290RetraceProcess::~RetraceProcess()
291{
292 delete m_jsonParser;
293}
294
Zack Rusin3acde362011-04-06 01:11:55 -0400295#include "retracer.moc"