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