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