]> git.cworth.org Git - apitrace/blob - gui/retracer.cpp
Prevent segfault on glretrace crash (fixes issue #52).
[apitrace] / gui / retracer.cpp
1 #include "retracer.h"
2
3 #include "apitracecall.h"
4
5 #include <QDebug>
6 #include <QVariant>
7
8 #include <qjson/parser.h>
9
10 Retracer::Retracer(QObject *parent)
11     : QThread(parent),
12       m_benchmarking(false),
13       m_doubleBuffered(true),
14       m_captureState(false),
15       m_captureCall(0)
16 {
17 #ifdef Q_OS_WIN
18     QString format = QLatin1String("%1;");
19 #else
20     QString format = QLatin1String("%1:");
21 #endif
22     QString buildPath = format.arg(APITRACE_BINARY_DIR);
23     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());
29 }
30
31 QString Retracer::fileName() const
32 {
33     return m_fileName;
34 }
35
36 void Retracer::setFileName(const QString &name)
37 {
38     m_fileName = name;
39 }
40
41 bool Retracer::isBenchmarking() const
42 {
43     return m_benchmarking;
44 }
45
46 void Retracer::setBenchmarking(bool bench)
47 {
48     m_benchmarking = bench;
49 }
50
51 bool Retracer::isDoubleBuffered() const
52 {
53     return m_doubleBuffered;
54 }
55
56 void Retracer::setDoubleBuffered(bool db)
57 {
58     m_doubleBuffered = db;
59 }
60
61 void Retracer::setCaptureAtCallNumber(qlonglong num)
62 {
63     m_captureCall = num;
64 }
65
66 qlonglong Retracer::captureAtCallNumber() const
67 {
68     return m_captureCall;
69 }
70
71 bool Retracer::captureState() const
72 {
73     return m_captureState;
74 }
75
76 void Retracer::setCaptureState(bool enable)
77 {
78     m_captureState = enable;
79 }
80
81
82 void 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&)));
101     connect(retrace, SIGNAL(foundState(ApiTraceState*)),
102             this, SIGNAL(foundState(ApiTraceState*)));
103     connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
104             this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
105
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
119 void RetraceProcess::start()
120 {
121     QStringList arguments;
122
123     if (m_doubleBuffered) {
124         arguments << QLatin1String("-db");
125     } else {
126         arguments << QLatin1String("-sb");
127     }
128
129     if (m_captureState) {
130         arguments << QLatin1String("-D");
131         arguments << QString::number(m_captureCall);
132     } else {
133         if (m_benchmarking) {
134             arguments << QLatin1String("-b");
135         }
136     }
137
138     arguments << m_fileName;
139
140     m_process->start(QLatin1String("glretrace"), arguments);
141 }
142
143
144 void RetraceProcess::replayFinished(int exitCode, QProcess::ExitStatus exitStatus)
145 {
146     QByteArray output = m_process->readAllStandardOutput();
147     QString msg;
148     QString errStr = m_process->readAllStandardError();
149
150 #if 0
151     qDebug()<<"Process finished = ";
152     qDebug()<<"\terr = "<<errStr;
153     qDebug()<<"\tout = "<<output;
154 #endif
155
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");
160     } else {
161         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         }
170     }
171
172     QStringList errorLines = errStr.split('\n');
173     QList<ApiTraceError> errors;
174     QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
175     foreach(QString line, errorLines) {
176         if (regexp.indexIn(line) != -1) {
177             ApiTraceError error;
178             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     }
187     emit finished(msg);
188 }
189
190 void RetraceProcess::replayError(QProcess::ProcessError err)
191 {
192     /*
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
198     qDebug()<<"Process error = "<<err;
199     qDebug()<<"\terr = "<<m_process->readAllStandardError();
200     qDebug()<<"\tout = "<<m_process->readAllStandardOutput();
201 #endif
202
203     emit error(
204         tr("Couldn't execute the replay file '%1'").arg(m_fileName));
205 }
206
207 Q_DECLARE_METATYPE(QList<ApiTraceError>);
208 RetraceProcess::RetraceProcess(QObject *parent)
209     : QObject(parent)
210 {
211     m_process = new QProcess(this);
212     m_jsonParser = new QJson::Parser();
213
214     qRegisterMetaType<QList<ApiTraceError> >();
215
216     connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
217             this, SLOT(replayFinished(int, QProcess::ExitStatus)));
218     connect(m_process, SIGNAL(error(QProcess::ProcessError)),
219             this, SLOT(replayError(QProcess::ProcessError)));
220 }
221
222 QProcess * RetraceProcess::process() const
223 {
224     return m_process;
225 }
226
227 QString RetraceProcess::fileName() const
228 {
229     return m_fileName;
230 }
231
232 void RetraceProcess::setFileName(const QString &name)
233 {
234     m_fileName = name;
235 }
236
237 bool RetraceProcess::isBenchmarking() const
238 {
239     return m_benchmarking;
240 }
241
242 void RetraceProcess::setBenchmarking(bool bench)
243 {
244     m_benchmarking = bench;
245 }
246
247 bool RetraceProcess::isDoubleBuffered() const
248 {
249     return m_doubleBuffered;
250 }
251
252 void RetraceProcess::setDoubleBuffered(bool db)
253 {
254     m_doubleBuffered = db;
255 }
256
257 void RetraceProcess::setCaptureAtCallNumber(qlonglong num)
258 {
259     m_captureCall = num;
260 }
261
262 qlonglong RetraceProcess::captureAtCallNumber() const
263 {
264     return m_captureCall;
265 }
266
267 bool RetraceProcess::captureState() const
268 {
269     return m_captureState;
270 }
271
272 void RetraceProcess::setCaptureState(bool enable)
273 {
274     m_captureState = enable;
275 }
276
277 void RetraceProcess::terminate()
278 {
279     if (m_process) {
280         m_process->terminate();
281         emit finished(tr("Process terminated."));
282     }
283 }
284
285 void Retracer::cleanup()
286 {
287     quit();
288 }
289
290 RetraceProcess::~RetraceProcess()
291 {
292     delete m_jsonParser;
293 }
294
295 #include "retracer.moc"