]> git.cworth.org Git - apitrace/blob - gui/apitrace.cpp
Properly handle and propagate retrace errors.
[apitrace] / gui / apitrace.cpp
1 #include "apitrace.h"
2
3 #include "traceloader.h"
4 #include "saverthread.h"
5
6 #include <QDebug>
7 #include <QDir>
8 #include <QThread>
9
10 ApiTrace::ApiTrace()
11     : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers),
12       m_needsSaving(false)
13 {
14     m_loader = new TraceLoader();
15
16     connect(this, SIGNAL(loadTrace(QString)),
17             m_loader, SLOT(loadTrace(QString)));
18     connect(this, SIGNAL(requestFrame(ApiTraceFrame*)),
19             m_loader, SLOT(loadFrame(ApiTraceFrame*)));
20     connect(m_loader, SIGNAL(framesLoaded(const QList<ApiTraceFrame*>)),
21             this, SLOT(addFrames(const QList<ApiTraceFrame*>)));
22     connect(m_loader,
23             SIGNAL(frameContentsLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)),
24             this,
25             SLOT(loaderFrameLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)));
26     connect(m_loader, SIGNAL(finishedParsing()),
27             this, SLOT(finishedParsing()));
28     connect(this, SIGNAL(loaderSearchNext(int,QString,Qt::CaseSensitivity)),
29             m_loader, SLOT(searchNext(int,QString,Qt::CaseSensitivity)));
30     connect(this, SIGNAL(loaderSearchPrev(int,QString,Qt::CaseSensitivity)),
31             m_loader, SLOT(searchPrev(int,QString,Qt::CaseSensitivity)));
32     connect(m_loader,
33             SIGNAL(searchResult(ApiTrace::SearchResult,ApiTraceCall*)),
34             this,
35             SLOT(loaderSearchResult(ApiTrace::SearchResult,ApiTraceCall*)));
36     connect(this, SIGNAL(loaderFindFrameStart(ApiTraceFrame*)),
37             m_loader, SLOT(findFrameStart(ApiTraceFrame*)));
38     connect(this, SIGNAL(loaderFindFrameEnd(ApiTraceFrame*)),
39             m_loader, SLOT(findFrameEnd(ApiTraceFrame*)));
40     connect(m_loader, SIGNAL(foundFrameStart(ApiTraceFrame*)),
41             this, SIGNAL(foundFrameStart(ApiTraceFrame*)));
42     connect(m_loader, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
43             this, SIGNAL(foundFrameEnd(ApiTraceFrame*)));
44     connect(this, SIGNAL(loaderFindCallIndex(int)),
45             m_loader, SLOT(findCallIndex(int)));
46     connect(m_loader, SIGNAL(foundCallIndex(ApiTraceCall*)),
47             this, SIGNAL(foundCallIndex(ApiTraceCall*)));
48
49
50     connect(m_loader, SIGNAL(startedParsing()),
51             this, SIGNAL(startedLoadingTrace()));
52     connect(m_loader, SIGNAL(parsed(int)),
53             this, SIGNAL(loaded(int)));
54     connect(m_loader, SIGNAL(finishedParsing()),
55             this, SIGNAL(finishedLoadingTrace()));
56
57
58     m_saver = new SaverThread(this);
59     connect(m_saver, SIGNAL(traceSaved()),
60             this, SLOT(slotSaved()));
61     connect(m_saver, SIGNAL(traceSaved()),
62             this, SIGNAL(saved()));
63
64     m_loaderThread = new QThread();
65     m_loader->moveToThread(m_loaderThread);
66     m_loaderThread->start();
67 }
68
69 ApiTrace::~ApiTrace()
70 {
71     m_loaderThread->quit();
72     m_loaderThread->deleteLater();
73     qDeleteAll(m_frames);
74     delete m_loader;
75     delete m_saver;
76 }
77
78 bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
79                                   ApiTrace::FrameMarker marker)
80 {
81     if (!call)
82         return false;
83
84     switch (marker) {
85     case FrameMarker_SwapBuffers:
86         return call->name().contains(QLatin1String("SwapBuffers")) ||
87                call->name() == QLatin1String("CGLFlushDrawable") ||
88                call->name() == QLatin1String("glFrameTerminatorGREMEDY");
89     case FrameMarker_Flush:
90         return call->name() == QLatin1String("glFlush");
91     case FrameMarker_Finish:
92         return call->name() == QLatin1String("glFinish");
93     case FrameMarker_Clear:
94         return call->name() == QLatin1String("glClear");
95     }
96
97     Q_ASSERT(!"unknown frame marker");
98
99     return false;
100 }
101
102 bool ApiTrace::isEmpty() const
103 {
104     return m_frames.isEmpty();
105 }
106
107 QString ApiTrace::fileName() const
108 {
109     if (edited())
110         return m_tempFileName;
111
112     return m_fileName;
113 }
114
115 ApiTrace::FrameMarker ApiTrace::frameMarker() const
116 {
117     return m_frameMarker;
118 }
119
120 QList<ApiTraceFrame*> ApiTrace::frames() const
121 {
122     return m_frames;
123 }
124
125 ApiTraceFrame * ApiTrace::frameAt(int idx) const
126 {
127     return m_frames.value(idx);
128 }
129
130 int ApiTrace::numFrames() const
131 {
132     return m_frames.count();
133 }
134
135 int ApiTrace::numCallsInFrame(int idx) const
136 {
137     const ApiTraceFrame *frame = frameAt(idx);
138     if (frame)
139         return frame->numChildren();
140     else
141         return 0;
142 }
143
144 void ApiTrace::setFileName(const QString &name)
145 {
146     if (m_fileName != name) {
147         m_fileName = name;
148
149         m_frames.clear();
150         m_errors.clear();
151         m_editedCalls.clear();
152         m_needsSaving = false;
153         emit invalidated();
154
155 //        m_loader->loadTrace(m_fileName);
156         emit loadTrace(m_fileName);
157     }
158 }
159
160 void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
161 {
162     QVector<ApiTraceCall*> calls;
163     int currentFrames = m_frames.count();
164     int numNewFrames = frames.count();
165
166     emit beginAddingFrames(currentFrames, numNewFrames);
167
168     m_frames += frames;
169
170     int numNewCalls = 0;
171     foreach(ApiTraceFrame *frame, frames) {
172         frame->setParentTrace(this);
173         numNewCalls += frame->numChildren();
174         calls += frame->calls();
175     }
176
177     emit endAddingFrames();
178 }
179
180 ApiTraceCall * ApiTrace::callWithIndex(int idx) const
181 {
182     for (int i = 0; i < m_frames.count(); ++i) {
183         ApiTraceCall *call = m_frames[i]->callWithIndex(idx);
184         if (call)
185             return call;
186     }
187     return NULL;
188 }
189
190 ApiTraceState ApiTrace::defaultState() const
191 {
192     ApiTraceFrame *frame = frameAt(0);
193     if (!frame || !frame->hasState())
194         return ApiTraceState();
195
196     return *frame->state();
197 }
198
199 void ApiTrace::callEdited(ApiTraceCall *call)
200 {
201     if (!m_editedCalls.contains(call)) {
202         //lets generate a temp filename
203         QString tempPath = QDir::tempPath();
204         m_tempFileName = QString::fromLatin1("%1/%2.edited")
205                          .arg(tempPath)
206                          .arg(m_fileName);
207     }
208     m_editedCalls.insert(call);
209     m_needsSaving = true;
210
211     emit changed(call);
212 }
213
214 void ApiTrace::callReverted(ApiTraceCall *call)
215 {
216     m_editedCalls.remove(call);
217
218     if (m_editedCalls.isEmpty()) {
219         m_needsSaving = false;
220     }
221     emit changed(call);
222 }
223
224 bool ApiTrace::edited() const
225 {
226     return !m_editedCalls.isEmpty();
227 }
228
229 bool ApiTrace::needsSaving() const
230 {
231     return m_needsSaving;
232 }
233
234 void ApiTrace::save()
235 {
236     QFileInfo fi(m_tempFileName);
237     QDir dir;
238     emit startedSaving();
239     dir.mkpath(fi.absolutePath());
240     m_saver->saveFile(m_tempFileName,
241                       m_fileName,
242                       m_editedCalls);
243 }
244
245 void ApiTrace::slotSaved()
246 {
247     m_needsSaving = false;
248 }
249
250 bool ApiTrace::isSaving() const
251 {
252     return m_saver->isRunning();
253 }
254
255 bool ApiTrace::hasErrors() const
256 {
257     return !m_errors.isEmpty();
258 }
259
260 void ApiTrace::loadFrame(ApiTraceFrame *frame)
261 {
262     Q_ASSERT(!frame->loaded());
263     emit requestFrame(frame);
264 }
265
266 void ApiTrace::finishedParsing()
267 {
268     ApiTraceFrame *firstFrame = m_frames[0];
269     if (firstFrame && !firstFrame->loaded()) {
270         loadFrame(firstFrame);
271     }
272 }
273
274 void ApiTrace::loaderFrameLoaded(ApiTraceFrame *frame,
275                                  const QVector<ApiTraceCall*> &calls,
276                                  quint64 binaryDataSize)
277 {
278     Q_ASSERT(frame->numChildrenToLoad() == calls.size());
279     emit beginLoadingFrame(frame, calls.size());
280     frame->setCalls(calls, binaryDataSize);
281     emit endLoadingFrame(frame);
282
283     if (!m_queuedErrors.isEmpty()) {
284         QList< QPair<ApiTraceFrame*, ApiTraceError> >::iterator itr;
285         for (itr = m_queuedErrors.begin(); itr != m_queuedErrors.end();
286              ++itr) {
287             const ApiTraceError &error = (*itr).second;
288             if ((*itr).first == frame) {
289                 ApiTraceCall *call = frame->callWithIndex(error.callIndex);
290
291                 if (!call) {
292                     continue;
293                 }
294
295                 call->setError(error.message);
296                 m_queuedErrors.erase(itr);
297
298                 if (call->hasError()) {
299                     m_errors.insert(call);
300                 } else {
301                     m_errors.remove(call);
302                 }
303                 emit changed(call);
304             }
305         }
306     }
307 }
308
309 void ApiTrace::findNext(ApiTraceFrame *frame,
310                         ApiTraceCall *from,
311                         const QString &str,
312                         Qt::CaseSensitivity sensitivity)
313 {
314     ApiTraceCall *foundCall = 0;
315     int frameIdx = m_frames.indexOf(frame);
316
317     if (frame->loaded()) {
318         foundCall = frame->findNextCall(from, str, sensitivity);
319         if (foundCall) {
320             emit findResult(SearchFound, foundCall);
321             return;
322         }
323
324         //if the frame is loaded we already searched it above
325         // so skip it
326         frameIdx += 1;
327     }
328
329     for (int i = frameIdx; i < m_frames.count(); ++i) {
330         ApiTraceFrame *frame = m_frames[i];
331         if (!frame->loaded()) {
332             emit loaderSearchNext(i, str, sensitivity);
333             return;
334         } else {
335             ApiTraceCall *call = frame->findNextCall(0, str, sensitivity);
336             if (call) {
337                 emit findResult(SearchFound, call);
338                 return;
339             }
340         }
341     }
342     emit findResult(SearchWrapped, 0);
343 }
344
345 void ApiTrace::findPrev(ApiTraceFrame *frame,
346                         ApiTraceCall *from,
347                         const QString &str,
348                         Qt::CaseSensitivity sensitivity)
349 {
350     ApiTraceCall *foundCall = 0;
351     int frameIdx = m_frames.indexOf(frame);
352
353     if (frame->loaded()) {
354         foundCall = frame->findPrevCall(from, str, sensitivity);
355         if (foundCall) {
356             emit findResult(SearchFound, foundCall);
357             return;
358         }
359
360         //if the frame is loaded we already searched it above
361         // so skip it
362         frameIdx -= 1;
363     }
364
365     for (int i = frameIdx; i >= 0; --i) {
366         ApiTraceFrame *frame = m_frames[i];
367         if (!frame->loaded()) {
368             emit loaderSearchPrev(i, str, sensitivity);
369             return;
370         } else {
371             ApiTraceCall *call = frame->findPrevCall(0, str, sensitivity);
372             if (call) {
373                 emit findResult(SearchFound, call);
374                 return;
375             }
376         }
377     }
378     emit findResult(SearchWrapped, 0);
379 }
380
381 void ApiTrace::loaderSearchResult(ApiTrace::SearchResult result,
382                                   ApiTraceCall *call)
383 {
384     //qDebug()<<"Search result = "<<result
385     //       <<", call is = "<<call;
386     emit findResult(result, call);
387 }
388
389 void ApiTrace::findFrameStart(ApiTraceFrame *frame)
390 {
391     if (frame->loaded()) {
392         emit foundFrameStart(frame);
393     } else {
394         emit loaderFindFrameStart(frame);
395     }
396 }
397
398 void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
399 {
400     if (frame->loaded()) {
401         emit foundFrameEnd(frame);
402     } else {
403         emit loaderFindFrameEnd(frame);
404     }
405 }
406
407 void ApiTrace::findCallIndex(int index)
408 {
409     int frameIdx = callInFrame(index);
410     ApiTraceFrame *frame = 0;
411
412     if (frameIdx < 0) {
413         emit foundCallIndex(0);
414         return;
415     }
416
417     frame = m_frames[frameIdx];
418
419     if (frame) {
420         if (frame->loaded()) {
421             ApiTraceCall *call = frame->callWithIndex(index);
422             emit foundCallIndex(call);
423         } else {
424             emit loaderFindCallIndex(index);
425         }
426     }
427 }
428
429 int ApiTrace::callInFrame(int callIdx) const
430 {
431     unsigned numCalls = 0;
432
433     for (int frameIdx = 0; frameIdx <= m_frames.size(); ++frameIdx) {
434         const ApiTraceFrame *frame = m_frames[frameIdx];
435         unsigned numCallsInFrame =  frame->loaded()
436                 ? frame->numChildren()
437                 : frame->numChildrenToLoad();
438         unsigned firstCall = numCalls;
439         unsigned endCall = numCalls + numCallsInFrame;
440         if (firstCall <= callIdx && endCall > callIdx) {
441             return frameIdx;
442         }
443         numCalls = endCall;
444     }
445
446     return -1;
447 }
448
449 void ApiTrace::setCallError(const ApiTraceError &error)
450 {
451     int frameIdx = callInFrame(error.callIndex);
452     ApiTraceFrame *frame = 0;
453
454     if (frameIdx < 0) {
455         return;
456     }
457     frame = m_frames[frameIdx];
458
459     if (frame->loaded()) {
460         ApiTraceCall *call = frame->callWithIndex(error.callIndex);
461         call->setError(error.message);
462         if (call->hasError()) {
463             m_errors.insert(call);
464         } else {
465             m_errors.remove(call);
466         }
467         emit changed(call);
468     } else {
469         emit requestFrame(frame);
470         m_queuedErrors.append(qMakePair(frame, error));
471     }
472 }
473
474 #include "apitrace.moc"