]> git.cworth.org Git - apitrace/blob - gui/apitrace.cpp
Implement jump to.
[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_calls);
74     qDeleteAll(m_frames);
75     delete m_loader;
76     delete m_saver;
77 }
78
79 bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
80                                   ApiTrace::FrameMarker marker)
81 {
82     if (!call)
83         return false;
84
85     switch (marker) {
86     case FrameMarker_SwapBuffers:
87         return call->name().contains(QLatin1String("SwapBuffers")) ||
88                call->name() == QLatin1String("CGLFlushDrawable") ||
89                call->name() == QLatin1String("glFrameTerminatorGREMEDY");
90     case FrameMarker_Flush:
91         return call->name() == QLatin1String("glFlush");
92     case FrameMarker_Finish:
93         return call->name() == QLatin1String("glFinish");
94     case FrameMarker_Clear:
95         return call->name() == QLatin1String("glClear");
96     }
97
98     Q_ASSERT(!"unknown frame marker");
99
100     return false;
101 }
102
103 bool ApiTrace::isEmpty() const
104 {
105     return m_calls.isEmpty();
106 }
107
108 QString ApiTrace::fileName() const
109 {
110     if (edited())
111         return m_tempFileName;
112
113     return m_fileName;
114 }
115
116 ApiTrace::FrameMarker ApiTrace::frameMarker() const
117 {
118     return m_frameMarker;
119 }
120
121 QVector<ApiTraceCall*> ApiTrace::calls() const
122 {
123     return m_calls;
124 }
125
126 int ApiTrace::numCalls() const
127 {
128     return m_calls.count();
129 }
130
131 QList<ApiTraceFrame*> ApiTrace::frames() const
132 {
133     return m_frames;
134 }
135
136 ApiTraceFrame * ApiTrace::frameAt(int idx) const
137 {
138     return m_frames.value(idx);
139 }
140
141 int ApiTrace::numFrames() const
142 {
143     return m_frames.count();
144 }
145
146 int ApiTrace::numCallsInFrame(int idx) const
147 {
148     const ApiTraceFrame *frame = frameAt(idx);
149     if (frame)
150         return frame->numChildren();
151     else
152         return 0;
153 }
154
155 void ApiTrace::setFileName(const QString &name)
156 {
157     if (m_fileName != name) {
158         m_fileName = name;
159
160         m_frames.clear();
161         m_calls.clear();
162         m_errors.clear();
163         m_editedCalls.clear();
164         m_needsSaving = false;
165         emit invalidated();
166
167 //        m_loader->loadTrace(m_fileName);
168         emit loadTrace(m_fileName);
169     }
170 }
171
172 void ApiTrace::setFrameMarker(FrameMarker marker)
173 {
174     if (m_frameMarker != marker) {
175         emit framesInvalidated();
176
177         qDeleteAll(m_frames);
178         m_frames.clear();
179         detectFrames();
180     }
181 }
182
183 void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
184 {
185     QVector<ApiTraceCall*> calls;
186     int currentFrames = m_frames.count();
187     int numNewFrames = frames.count();
188
189     emit beginAddingFrames(currentFrames, numNewFrames);
190
191     m_frames += frames;
192
193     int currentCalls = m_calls.count();
194     int numNewCalls = 0;
195     foreach(ApiTraceFrame *frame, frames) {
196         frame->setParentTrace(this);
197         numNewCalls += frame->numChildren();
198         calls += frame->calls();
199     }
200     m_calls.reserve(m_calls.count() + calls.count() + 1);
201     m_calls += calls;
202
203     emit endAddingFrames();
204     emit callsAdded(currentCalls, numNewCalls);
205 }
206
207 void ApiTrace::detectFrames()
208 {
209     if (m_calls.isEmpty())
210         return;
211
212     emit beginAddingFrames(0, m_frames.count());
213
214     ApiTraceFrame *currentFrame = 0;
215     foreach(ApiTraceCall *apiCall, m_calls) {
216         if (!currentFrame) {
217             currentFrame = new ApiTraceFrame(this);
218             currentFrame->number = m_frames.count();
219             currentFrame->setLoaded(true);
220         }
221         apiCall->setParentFrame(currentFrame);
222         currentFrame->addCall(apiCall);
223         if (ApiTrace::isCallAFrameMarker(apiCall,
224                                          m_frameMarker)) {
225             m_frames.append(currentFrame);
226             currentFrame = 0;
227         }
228     }
229     //last frames won't have markers
230     //  it's just a bunch of Delete calls for every object
231     //  after the last SwapBuffers
232     if (currentFrame) {
233         m_frames.append(currentFrame);
234         currentFrame = 0;
235     }
236     emit endAddingFrames();
237 }
238
239 ApiTraceCall * ApiTrace::callWithIndex(int idx) const
240 {
241     for (int i = 0; i < m_calls.count(); ++i) {
242         ApiTraceCall *call = m_calls[i];
243         if (call->index() == idx)
244             return call;
245     }
246     return NULL;
247 }
248
249 ApiTraceState ApiTrace::defaultState() const
250 {
251     ApiTraceFrame *frame = frameAt(0);
252     if (!frame || !frame->hasState())
253         return ApiTraceState();
254
255     return *frame->state();
256 }
257
258 void ApiTrace::callEdited(ApiTraceCall *call)
259 {
260     if (!m_editedCalls.contains(call)) {
261         //lets generate a temp filename
262         QString tempPath = QDir::tempPath();
263         m_tempFileName = QString::fromLatin1("%1/%2.edited")
264                          .arg(tempPath)
265                          .arg(m_fileName);
266     }
267     m_editedCalls.insert(call);
268     m_needsSaving = true;
269
270     emit changed(call);
271 }
272
273 void ApiTrace::callReverted(ApiTraceCall *call)
274 {
275     m_editedCalls.remove(call);
276
277     if (m_editedCalls.isEmpty()) {
278         m_needsSaving = false;
279     }
280     emit changed(call);
281 }
282
283 bool ApiTrace::edited() const
284 {
285     return !m_editedCalls.isEmpty();
286 }
287
288 bool ApiTrace::needsSaving() const
289 {
290     return m_needsSaving;
291 }
292
293 void ApiTrace::save()
294 {
295     QFileInfo fi(m_tempFileName);
296     QDir dir;
297     emit startedSaving();
298     dir.mkpath(fi.absolutePath());
299     m_saver->saveFile(m_tempFileName, m_calls);
300 }
301
302 void ApiTrace::slotSaved()
303 {
304     m_needsSaving = false;
305 }
306
307 bool ApiTrace::isSaving() const
308 {
309     return m_saver->isRunning();
310 }
311
312 void ApiTrace::callError(ApiTraceCall *call)
313 {
314     Q_ASSERT(call);
315
316     if (call->hasError())
317         m_errors.insert(call);
318     else
319         m_errors.remove(call);
320
321     emit changed(call);
322 }
323
324 bool ApiTrace::hasErrors() const
325 {
326     return !m_errors.isEmpty();
327 }
328
329 void ApiTrace::loadFrame(ApiTraceFrame *frame)
330 {
331     Q_ASSERT(!frame->loaded());
332     emit requestFrame(frame);
333 }
334
335 void ApiTrace::finishedParsing()
336 {
337     ApiTraceFrame *firstFrame = m_frames[0];
338     if (firstFrame && !firstFrame->loaded()) {
339         loadFrame(firstFrame);
340     }
341 }
342
343 void ApiTrace::loaderFrameLoaded(ApiTraceFrame *frame,
344                                  const QVector<ApiTraceCall*> &calls,
345                                  quint64 binaryDataSize)
346 {
347     Q_ASSERT(frame->numChildrenToLoad() == calls.size());
348     emit beginLoadingFrame(frame, calls.size());
349     frame->setCalls(calls, binaryDataSize);
350     emit endLoadingFrame(frame);
351 }
352
353 void ApiTrace::findNext(ApiTraceFrame *frame,
354                         ApiTraceCall *from,
355                         const QString &str,
356                         Qt::CaseSensitivity sensitivity)
357 {
358     ApiTraceCall *foundCall = 0;
359     int frameIdx = m_frames.indexOf(frame);
360
361     if (frame->loaded()) {
362         foundCall = frame->findNextCall(from, str, sensitivity);
363         if (foundCall) {
364             emit findResult(SearchFound, foundCall);
365             return;
366         }
367
368         //if the frame is loaded we already searched it above
369         // so skip it
370         frameIdx += 1;
371     }
372
373     for (int i = frameIdx; i < m_frames.count(); ++i) {
374         ApiTraceFrame *frame = m_frames[i];
375         if (!frame->loaded()) {
376             emit loaderSearchNext(i, str, sensitivity);
377             return;
378         } else {
379             ApiTraceCall *call = frame->findNextCall(0, str, sensitivity);
380             if (call) {
381                 emit findResult(SearchFound, call);
382                 return;
383             }
384         }
385     }
386     emit findResult(SearchWrapped, 0);
387 }
388
389 void ApiTrace::findPrev(ApiTraceFrame *frame,
390                         ApiTraceCall *from,
391                         const QString &str,
392                         Qt::CaseSensitivity sensitivity)
393 {
394     ApiTraceCall *foundCall = 0;
395     int frameIdx = m_frames.indexOf(frame);
396
397     if (frame->loaded()) {
398         foundCall = frame->findPrevCall(from, str, sensitivity);
399         if (foundCall) {
400             emit findResult(SearchFound, foundCall);
401             return;
402         }
403
404         //if the frame is loaded we already searched it above
405         // so skip it
406         frameIdx -= 1;
407     }
408
409     for (int i = frameIdx; i >= 0; --i) {
410         ApiTraceFrame *frame = m_frames[i];
411         if (!frame->loaded()) {
412             emit loaderSearchPrev(i, str, sensitivity);
413             return;
414         } else {
415             ApiTraceCall *call = frame->findPrevCall(0, str, sensitivity);
416             if (call) {
417                 emit findResult(SearchFound, call);
418                 return;
419             }
420         }
421     }
422     emit findResult(SearchWrapped, 0);
423 }
424
425 void ApiTrace::loaderSearchResult(ApiTrace::SearchResult result,
426                                   ApiTraceCall *call)
427 {
428     //qDebug()<<"Search result = "<<result
429     //       <<", call is = "<<call;
430     emit findResult(result, call);
431 }
432
433 void ApiTrace::findFrameStart(ApiTraceFrame *frame)
434 {
435     if (frame->loaded()) {
436         emit foundFrameStart(frame);
437     } else {
438         emit loaderFindFrameStart(frame);
439     }
440 }
441
442 void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
443 {
444     if (frame->loaded()) {
445         emit foundFrameEnd(frame);
446     } else {
447         emit loaderFindFrameEnd(frame);
448     }
449 }
450
451 void ApiTrace::findCallIndex(int index)
452 {
453     int frameIdx = callInFrame(index);
454     ApiTraceFrame *frame = 0;
455
456     if (frameIdx < 0) {
457         emit foundCallIndex(0);
458         return;
459     }
460
461     frame = m_frames[frameIdx];
462
463     if (frame) {
464         if (frame->loaded()) {
465             ApiTraceCall *call = frame->callWithIndex(index);
466             emit foundCallIndex(call);
467         } else {
468             emit loaderFindCallIndex(index);
469         }
470     }
471 }
472
473 int ApiTrace::callInFrame(int callIdx) const
474 {
475     unsigned numCalls = 0;
476
477     for (int frameIdx = 0; frameIdx <= m_frames.size(); ++frameIdx) {
478         const ApiTraceFrame *frame = m_frames[frameIdx];
479         unsigned numCallsInFrame =  frame->loaded()
480                 ? frame->numChildren()
481                 : frame->numChildrenToLoad();
482         unsigned firstCall = numCalls;
483         unsigned endCall = numCalls + numCallsInFrame;
484         if (firstCall <= callIdx && endCall > callIdx) {
485             return frameIdx;
486         }
487         numCalls = endCall;
488     }
489
490     return -1;
491 }
492
493 #include "apitrace.moc"