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