]> git.cworth.org Git - apitrace/blob - gui/apitrace.cpp
gui: Fix crash on trigger actions without loaded trace-file.
[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*>,quint64)),
23             this,
24             SLOT(loaderFrameLoaded(ApiTraceFrame*,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->numChildren();
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*> &calls,
267                                  quint64 binaryDataSize)
268 {
269     Q_ASSERT(frame->numChildrenToLoad() == calls.size());
270
271     if (!frame->isLoaded()) {
272         emit beginLoadingFrame(frame, calls.size());
273         frame->setCalls(calls, binaryDataSize);
274         emit endLoadingFrame(frame);
275         m_loadingFrames.remove(frame);
276     }
277
278     if (!m_queuedErrors.isEmpty()) {
279         QList< QPair<ApiTraceFrame*, ApiTraceError> >::iterator itr;
280         for (itr = m_queuedErrors.begin(); itr != m_queuedErrors.end();
281              ++itr) {
282             const ApiTraceError &error = (*itr).second;
283             if ((*itr).first == frame) {
284                 ApiTraceCall *call = frame->callWithIndex(error.callIndex);
285
286                 if (!call) {
287                     continue;
288                 }
289
290                 call->setError(error.message);
291                 m_queuedErrors.erase(itr);
292
293                 if (call->hasError()) {
294                     m_errors.insert(call);
295                 } else {
296                     m_errors.remove(call);
297                 }
298                 emit changed(call);
299             }
300         }
301     }
302 }
303
304 void ApiTrace::findNext(ApiTraceFrame *frame,
305                         ApiTraceCall *from,
306                         const QString &str,
307                         Qt::CaseSensitivity sensitivity)
308 {
309     ApiTraceCall *foundCall = 0;
310     int frameIdx = m_frames.indexOf(frame);
311     SearchRequest request(SearchRequest::Next,
312                           frame, from, str, sensitivity);
313
314     if (frame->isLoaded()) {
315         foundCall = frame->findNextCall(from, str, sensitivity);
316         if (foundCall) {
317             emit findResult(request, SearchResult_Found, foundCall);
318             return;
319         }
320
321         //if the frame is loaded we already searched it above
322         // so skip it
323         frameIdx += 1;
324     }
325
326     //for the rest of the frames we search from beginning
327     request.from = 0;
328     for (int i = frameIdx; i < m_frames.count(); ++i) {
329         ApiTraceFrame *frame = m_frames[i];
330         request.frame = frame;
331         if (!frame->isLoaded()) {
332             emit loaderSearch(request);
333             return;
334         } else {
335             ApiTraceCall *call = frame->findNextCall(0, str, sensitivity);
336             if (call) {
337                 emit findResult(request, SearchResult_Found, call);
338                 return;
339             }
340         }
341     }
342     emit findResult(request, SearchResult_Wrapped, 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     SearchRequest request(SearchRequest::Prev,
353                           frame, from, str, sensitivity);
354
355     if (frame->isLoaded()) {
356         foundCall = frame->findPrevCall(from, str, sensitivity);
357         if (foundCall) {
358             emit findResult(request, SearchResult_Found, foundCall);
359             return;
360         }
361
362         //if the frame is loaded we already searched it above
363         // so skip it
364         frameIdx -= 1;
365     }
366
367     request.from = 0;
368     for (int i = frameIdx; i >= 0; --i) {
369         ApiTraceFrame *frame = m_frames[i];
370         request.frame = frame;
371         if (!frame->isLoaded()) {
372             emit loaderSearch(request);
373             return;
374         } else {
375             ApiTraceCall *call = frame->findPrevCall(0, str, sensitivity);
376             if (call) {
377                 emit findResult(request, SearchResult_Found, call);
378                 return;
379             }
380         }
381     }
382     emit findResult(request, SearchResult_Wrapped, 0);
383 }
384
385 void ApiTrace::loaderSearchResult(const ApiTrace::SearchRequest &request,
386                                   ApiTrace::SearchResult result,
387                                   ApiTraceCall *call)
388 {
389     //qDebug()<<"Search result = "<<result
390     //       <<", call is = "<<call;
391     emit findResult(request, result, call);
392 }
393
394 void ApiTrace::findFrameStart(ApiTraceFrame *frame)
395 {
396     if (!frame)
397         return;
398
399     if (frame->isLoaded()) {
400         emit foundFrameStart(frame);
401     } else {
402         emit loaderFindFrameStart(frame);
403     }
404 }
405
406 void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
407 {
408     if (!frame)
409         return;
410
411     if (frame->isLoaded()) {
412         emit foundFrameEnd(frame);
413     } else {
414         emit loaderFindFrameEnd(frame);
415     }
416 }
417
418 void ApiTrace::findCallIndex(int index)
419 {
420     int frameIdx = callInFrame(index);
421     ApiTraceFrame *frame = 0;
422
423     if (frameIdx < 0) {
424         emit foundCallIndex(0);
425         return;
426     }
427
428     frame = m_frames[frameIdx];
429
430     if (frame) {
431         if (frame->isLoaded()) {
432             ApiTraceCall *call = frame->callWithIndex(index);
433             emit foundCallIndex(call);
434         } else {
435             emit loaderFindCallIndex(index);
436         }
437     }
438 }
439
440 int ApiTrace::callInFrame(int callIdx) const
441 {
442     unsigned numCalls = 0;
443
444     for (int frameIdx = 0; frameIdx < m_frames.size(); ++frameIdx) {
445         const ApiTraceFrame *frame = m_frames[frameIdx];
446         unsigned numCallsInFrame =  frame->isLoaded()
447                 ? frame->numChildren()
448                 : frame->numChildrenToLoad();
449         unsigned firstCall = numCalls;
450         unsigned endCall = numCalls + numCallsInFrame;
451         if (firstCall <= callIdx && endCall > callIdx) {
452             return frameIdx;
453         }
454         numCalls = endCall;
455     }
456
457     return -1;
458 }
459
460 void ApiTrace::setCallError(const ApiTraceError &error)
461 {
462     int frameIdx = callInFrame(error.callIndex);
463     ApiTraceFrame *frame = 0;
464
465     if (frameIdx < 0) {
466         return;
467     }
468     frame = m_frames[frameIdx];
469
470     if (frame->isLoaded()) {
471         ApiTraceCall *call = frame->callWithIndex(error.callIndex);
472         call->setError(error.message);
473         if (call->hasError()) {
474             m_errors.insert(call);
475         } else {
476             m_errors.remove(call);
477         }
478         emit changed(call);
479     } else {
480         loadFrame(frame);
481         m_queuedErrors.append(qMakePair(frame, error));
482     }
483 }
484
485 bool ApiTrace::isFrameLoading(ApiTraceFrame *frame) const
486 {
487     return m_loadingFrames.contains(frame);
488 }
489
490 void ApiTrace::bindThumbnailsToFrames(const QList<QImage> &thumbnails)
491 {
492     QList<ApiTraceFrame *> frames = m_frames;
493
494     QList<QImage>::const_iterator thumbnail = thumbnails.begin();
495
496     foreach (ApiTraceFrame *frame, frames) {
497         if (thumbnail != thumbnails.end()) {
498             frame->setThumbnail(*thumbnail);
499
500             ++thumbnail;
501
502             emit changed(frame);
503         }
504     }
505 }
506
507 #include "apitrace.moc"