]> git.cworth.org Git - apitrace/blob - gui/traceloader.cpp
d09f0fe7fa019beeca4d4e3eb13133fc8e11c61d
[apitrace] / gui / traceloader.cpp
1 #include "traceloader.h"
2
3 #include "apitrace.h"
4 #include <QDebug>
5 #include <QFile>
6 #include <QStack>
7
8 #define FRAMES_TO_CACHE 100
9
10 static ApiTraceCall *
11 apiCallFromTraceCall(const trace::Call *call,
12                      const QHash<QString, QUrl> &helpHash,
13                      ApiTraceFrame *frame,
14                      ApiTraceCall *parentCall,
15                      TraceLoader *loader)
16 {
17     ApiTraceCall *apiCall;
18
19     if (parentCall)
20         apiCall = new ApiTraceCall(parentCall, loader, call);
21     else
22         apiCall = new ApiTraceCall(frame, loader, call);
23
24     apiCall->setHelpUrl(helpHash.value(apiCall->name()));
25
26     return apiCall;
27 }
28
29 TraceLoader::TraceLoader(QObject *parent)
30     : QObject(parent)
31 {
32 }
33
34 TraceLoader::~TraceLoader()
35 {
36     m_parser.close();
37     qDeleteAll(m_signatures);
38     qDeleteAll(m_enumSignatures);
39 }
40
41 void TraceLoader::loadTrace(const QString &filename)
42 {
43     if (m_helpHash.isEmpty()) {
44         loadHelpFile();
45     }
46
47     if (!m_frameBookmarks.isEmpty()) {
48         qDeleteAll(m_signatures);
49         qDeleteAll(m_enumSignatures);
50         m_signatures.clear();
51         m_enumSignatures.clear();
52         m_frameBookmarks.clear();
53         m_createdFrames.clear();
54         m_parser.close();
55     }
56
57     if (!m_parser.open(filename.toLatin1())) {
58         qDebug() << "error: failed to open " << filename;
59         return;
60     }
61
62     emit startedParsing();
63
64     if (m_parser.supportsOffsets()) {
65         scanTrace();
66     } else {
67         //Load the entire file into memory
68         parseTrace();
69     }
70     emit guessedApi(static_cast<int>(m_parser.api));
71     emit finishedParsing();
72 }
73
74 void TraceLoader::loadFrame(ApiTraceFrame *currentFrame)
75 {
76     fetchFrameContents(currentFrame);
77 }
78
79 int TraceLoader::numberOfFrames() const
80 {
81     return m_frameBookmarks.size();
82 }
83
84 int TraceLoader::numberOfCallsInFrame(int frameIdx) const
85 {
86     if (frameIdx >= m_frameBookmarks.size()) {
87         return 0;
88     }
89     FrameBookmarks::const_iterator itr =
90             m_frameBookmarks.find(frameIdx);
91     return itr->numberOfCalls;
92 }
93
94 void TraceLoader::loadHelpFile()
95 {
96     QFile file(":/resources/glreference.tsv");
97     if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
98         QString line;
99         while (!file.atEnd()) {
100             line = file.readLine();
101             QString function = line.section('\t', 0, 0).trimmed();
102             QUrl url = QUrl(line.section('\t', 1, 1).trimmed());
103             //qDebug()<<"function = "<<function<<", url = "<<url.toString();
104             m_helpHash.insert(function, url);
105         }
106     } else {
107         qWarning() << "Couldn't open reference file "
108                    << file.fileName();
109     }
110     file.close();
111 }
112
113 void TraceLoader::scanTrace()
114 {
115     QList<ApiTraceFrame*> frames;
116     ApiTraceFrame *currentFrame = 0;
117
118     trace::Call *call;
119     trace::ParseBookmark startBookmark;
120     int numOfFrames = 0;
121     int numOfCalls = 0;
122     int lastPercentReport = 0;
123
124     m_parser.getBookmark(startBookmark);
125
126     while ((call = m_parser.scan_call())) {
127         ++numOfCalls;
128
129         if (call->flags & trace::CALL_FLAG_END_FRAME) {
130             FrameBookmark frameBookmark(startBookmark);
131             frameBookmark.numberOfCalls = numOfCalls;
132
133             currentFrame = new ApiTraceFrame();
134             currentFrame->number = numOfFrames;
135             currentFrame->setNumChildren(numOfCalls);
136             currentFrame->setLastCallIndex(call->no);
137             frames.append(currentFrame);
138
139             m_createdFrames.append(currentFrame);
140             m_frameBookmarks[numOfFrames] = frameBookmark;
141             ++numOfFrames;
142
143             if (m_parser.percentRead() - lastPercentReport >= 5) {
144                 emit parsed(m_parser.percentRead());
145                 lastPercentReport = m_parser.percentRead();
146             }
147             m_parser.getBookmark(startBookmark);
148             numOfCalls = 0;
149         }
150         delete call;
151     }
152
153     if (numOfCalls) {
154         //trace::File::Bookmark endBookmark = m_parser.currentBookmark();
155         FrameBookmark frameBookmark(startBookmark);
156         frameBookmark.numberOfCalls = numOfCalls;
157
158         currentFrame = new ApiTraceFrame();
159         currentFrame->number = numOfFrames;
160         currentFrame->setNumChildren(numOfCalls);
161         frames.append(currentFrame);
162
163         m_createdFrames.append(currentFrame);
164         m_frameBookmarks[numOfFrames] = frameBookmark;
165         ++numOfFrames;
166     }
167
168     emit parsed(100);
169
170     emit framesLoaded(frames);
171 }
172
173 void TraceLoader::parseTrace()
174 {
175     QList<ApiTraceFrame*> frames;
176     ApiTraceFrame *currentFrame = 0;
177     int frameCount = 0;
178     QStack<ApiTraceCall*> groups;
179     QVector<ApiTraceCall*> topLevelItems;
180     QVector<ApiTraceCall*> allCalls;
181     quint64 binaryDataSize = 0;
182
183     int lastPercentReport = 0;
184
185     trace::Call *call = m_parser.parse_call();
186     while (call) {
187         //std::cout << *call;
188         if (!currentFrame) {
189             currentFrame = new ApiTraceFrame();
190             currentFrame->number = frameCount;
191             ++frameCount;
192         }
193         ApiTraceCall *apiCall =
194             apiCallFromTraceCall(call, m_helpHash, currentFrame, groups.isEmpty() ? 0 : groups.top(), this);
195         allCalls.append(apiCall);
196         if (groups.count() == 0) {
197             topLevelItems.append(apiCall);
198         }
199         if (call->flags & trace::CALL_FLAG_MARKER_PUSH) {
200             groups.push(apiCall);
201         } else if (call->flags & trace::CALL_FLAG_MARKER_POP) {
202             groups.top()->finishedAddingChildren();
203             groups.pop();
204         }
205         if (!groups.isEmpty()) {
206             groups.top()->addChild(apiCall);
207         }
208         if (apiCall->hasBinaryData()) {
209             QByteArray data =
210                 apiCall->arguments()[apiCall->binaryDataIndex()].toByteArray();
211             binaryDataSize += data.size();
212         }
213         if (call->flags & trace::CALL_FLAG_END_FRAME) {
214             allCalls.squeeze();
215             topLevelItems.squeeze();
216             if (topLevelItems.count() == allCalls.count()) {
217                 currentFrame->setCalls(allCalls, allCalls, binaryDataSize);
218             } else {
219                 currentFrame->setCalls(topLevelItems, allCalls, binaryDataSize);
220             }
221             allCalls.clear();
222             groups.clear();
223             topLevelItems.clear();
224             frames.append(currentFrame);
225             currentFrame = 0;
226             binaryDataSize = 0;
227             if (frames.count() >= FRAMES_TO_CACHE) {
228                 emit framesLoaded(frames);
229                 frames.clear();
230             }
231             if (m_parser.percentRead() - lastPercentReport >= 5) {
232                 emit parsed(m_parser.percentRead());
233                 lastPercentReport = m_parser.percentRead();
234             }
235         }
236         delete call;
237         call = m_parser.parse_call();
238     }
239
240     //last frames won't have markers
241     //  it's just a bunch of Delete calls for every object
242     //  after the last SwapBuffers
243     if (currentFrame) {
244         allCalls.squeeze();
245         if (topLevelItems.count() == allCalls.count()) {
246             currentFrame->setCalls(allCalls, allCalls, binaryDataSize);
247         } else {
248             currentFrame->setCalls(topLevelItems, allCalls, binaryDataSize);
249         }
250         frames.append(currentFrame);
251         currentFrame = 0;
252     }
253     if (frames.count()) {
254         emit framesLoaded(frames);
255     }
256 }
257
258
259 ApiTraceCallSignature * TraceLoader::signature(unsigned id)
260 {
261     if (id >= m_signatures.count()) {
262         m_signatures.resize(id + 1);
263         return NULL;
264     } else {
265         return m_signatures[id];
266     }
267 }
268
269 void TraceLoader::addSignature(unsigned id, ApiTraceCallSignature *signature)
270 {
271     m_signatures[id] = signature;
272 }
273
274 ApiTraceEnumSignature * TraceLoader::enumSignature(unsigned id)
275 {
276     if (id >= m_enumSignatures.count()) {
277         m_enumSignatures.resize(id + 1);
278         return NULL;
279     } else {
280         return m_enumSignatures[id];
281     }
282 }
283
284 void TraceLoader::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature)
285 {
286     m_enumSignatures[id] = signature;
287 }
288
289 void TraceLoader::searchNext(const ApiTrace::SearchRequest &request)
290 {
291     Q_ASSERT(m_parser.supportsOffsets());
292     if (m_parser.supportsOffsets()) {
293         int startFrame = m_createdFrames.indexOf(request.frame);
294         const FrameBookmark &frameBookmark = m_frameBookmarks[startFrame];
295         m_parser.setBookmark(frameBookmark.start);
296         trace::Call *call = 0;
297         while ((call = m_parser.parse_call())) {
298
299             if (callContains(call, request.text, request.cs)) {
300                 unsigned frameIdx = callInFrame(call->no);
301                 ApiTraceFrame *frame = m_createdFrames[frameIdx];
302                 const QVector<ApiTraceCall*> calls =
303                         fetchFrameContents(frame);
304                 for (int i = 0; i < calls.count(); ++i) {
305                     if (calls[i]->index() == call->no) {
306                         emit searchResult(request, ApiTrace::SearchResult_Found,
307                                           calls[i]);
308                         break;
309                     }
310                 }
311                 delete call;
312                 return;
313             }
314
315             delete call;
316         }
317     }
318     emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
319 }
320
321 void TraceLoader::searchPrev(const ApiTrace::SearchRequest &request)
322 {
323     Q_ASSERT(m_parser.supportsOffsets());
324     if (m_parser.supportsOffsets()) {
325         int startFrame = m_createdFrames.indexOf(request.frame);
326         trace::Call *call = 0;
327         QList<trace::Call*> frameCalls;
328         int frameIdx = startFrame;
329
330         const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
331         int numCallsToParse = frameBookmark.numberOfCalls;
332         m_parser.setBookmark(frameBookmark.start);
333
334         while ((call = m_parser.parse_call())) {
335
336             frameCalls.append(call);
337             --numCallsToParse;
338
339             if (numCallsToParse == 0) {
340                 bool foundCall = searchCallsBackwards(frameCalls,
341                                                       frameIdx,
342                                                       request);
343
344                 qDeleteAll(frameCalls);
345                 frameCalls.clear();
346                 if (foundCall) {
347                     return;
348                 }
349
350                 --frameIdx;
351
352                 if (frameIdx >= 0) {
353                     const FrameBookmark &frameBookmark =
354                             m_frameBookmarks[frameIdx];
355                     m_parser.setBookmark(frameBookmark.start);
356                     numCallsToParse = frameBookmark.numberOfCalls;
357                 }
358             }
359         }
360     }
361     emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
362 }
363
364 bool TraceLoader::searchCallsBackwards(const QList<trace::Call*> &calls,
365                                        int frameIdx,
366                                        const ApiTrace::SearchRequest &request)
367 {
368     for (int i = calls.count() - 1; i >= 0; --i) {
369         trace::Call *call = calls[i];
370         if (callContains(call, request.text, request.cs)) {
371             ApiTraceFrame *frame = m_createdFrames[frameIdx];
372             const QVector<ApiTraceCall*> apiCalls =
373                     fetchFrameContents(frame);
374             for (int i = 0; i < apiCalls.count(); ++i) {
375                 if (apiCalls[i]->index() == call->no) {
376                     emit searchResult(request,
377                                       ApiTrace::SearchResult_Found,
378                                       apiCalls[i]);
379                     break;
380                 }
381             }
382             return true;
383         }
384     }
385     return false;
386 }
387
388 int TraceLoader::callInFrame(int callIdx) const
389 {
390     unsigned numCalls = 0;
391
392     for (int frameIdx = 0; frameIdx < m_frameBookmarks.size(); ++frameIdx) {
393         const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
394         unsigned firstCall = numCalls;
395         unsigned endCall = numCalls + frameBookmark.numberOfCalls;
396         if (firstCall <= callIdx && endCall > callIdx) {
397             return frameIdx;
398         }
399         numCalls = endCall;
400     }
401     Q_ASSERT(!"call not in the trace");
402     return 0;
403 }
404
405 bool TraceLoader::callContains(trace::Call *call,
406                                const QString &str,
407                                Qt::CaseSensitivity sensitivity)
408 {
409     /*
410      * FIXME: do string comparison directly on trace::Call
411      */
412     ApiTraceCall *apiCall = apiCallFromTraceCall(call, m_helpHash,
413                                                  0, 0, this);
414     bool result = apiCall->contains(str, sensitivity);
415     delete apiCall;
416     return result;
417 }
418
419 QVector<ApiTraceCall*>
420 TraceLoader::fetchFrameContents(ApiTraceFrame *currentFrame)
421 {
422     Q_ASSERT(currentFrame);
423
424     if (currentFrame->isLoaded()) {
425         return currentFrame->calls();
426     }
427
428     if (m_parser.supportsOffsets()) {
429         unsigned frameIdx = currentFrame->number;
430         int numOfCalls = numberOfCallsInFrame(frameIdx);
431
432         if (numOfCalls) {
433             quint64 binaryDataSize = 0;
434             QStack<ApiTraceCall*> groups;
435             QVector<ApiTraceCall*> topLevelItems;
436             QVector<ApiTraceCall*> allCalls(numOfCalls);
437             const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
438
439             m_parser.setBookmark(frameBookmark.start);
440
441             trace::Call *call;
442             int parsedCalls = 0;
443             while ((call = m_parser.parse_call())) {
444                 ApiTraceCall *apiCall =
445                     apiCallFromTraceCall(call, m_helpHash,
446                                          currentFrame, groups.isEmpty() ? 0 : groups.top(), this);
447                 Q_ASSERT(apiCall);
448                 Q_ASSERT(parsedCalls < calls.size());
449                 allCalls[parsedCalls++] = apiCall;
450                 if (groups.count() == 0) {
451                     topLevelItems.append(apiCall);
452                 }
453                 if (!groups.isEmpty()) {
454                     groups.top()->addChild(apiCall);
455                 }
456                 if (call->flags & trace::CALL_FLAG_MARKER_PUSH) {
457                     groups.push(apiCall);
458                 } else if (call->flags & trace::CALL_FLAG_MARKER_POP) {
459                     groups.top()->finishedAddingChildren();
460                     groups.pop();
461                 }
462                 if (apiCall->hasBinaryData()) {
463                     QByteArray data =
464                         apiCall->arguments()[
465                             apiCall->binaryDataIndex()].toByteArray();
466                     binaryDataSize += data.size();
467                 }
468
469                 delete call;
470
471                 if (apiCall->flags() & trace::CALL_FLAG_END_FRAME) {
472                     break;
473                 }
474
475             }
476             // There can be fewer parsed calls when call in different
477             // threads cross the frame boundary
478             Q_ASSERT(parsedCalls <= numOfCalls);
479             Q_ASSERT(parsedCalls <= calls.size());
480             allCalls.resize(parsedCalls);
481             allCalls.squeeze();
482
483             Q_ASSERT(parsedCalls <= currentFrame->numChildrenToLoad());
484             if (topLevelItems.count() == allCalls.count()) {
485                 emit frameContentsLoaded(currentFrame, allCalls,
486                                          allCalls, binaryDataSize);
487             } else {
488                 emit frameContentsLoaded(currentFrame, topLevelItems,
489                                          allCalls, binaryDataSize);
490             }
491             return allCalls;
492         }
493     }
494     return QVector<ApiTraceCall*>();
495 }
496
497 void TraceLoader::findFrameStart(ApiTraceFrame *frame)
498 {
499     if (!frame->isLoaded()) {
500         loadFrame(frame);
501     }
502     emit foundFrameStart(frame);
503 }
504
505 void TraceLoader::findFrameEnd(ApiTraceFrame *frame)
506 {
507     if (!frame->isLoaded()) {
508         loadFrame(frame);
509     }
510     emit foundFrameEnd(frame);
511 }
512
513 void TraceLoader::findCallIndex(int index)
514 {
515     int frameIdx = callInFrame(index);
516     ApiTraceFrame *frame = m_createdFrames[frameIdx];
517     QVector<ApiTraceCall*> calls = fetchFrameContents(frame);
518     QVector<ApiTraceCall*>::const_iterator itr;
519     ApiTraceCall *call = 0;
520     for (itr = calls.constBegin(); itr != calls.constEnd(); ++itr) {
521         if ((*itr)->index() == index) {
522             call = *itr;
523         }
524     }
525     if (call) {
526         emit foundCallIndex(call);
527     }
528 }
529
530 void TraceLoader::search(const ApiTrace::SearchRequest &request)
531 {
532     if (request.direction == ApiTrace::SearchRequest::Next) {
533         searchNext(request);
534     } else {
535         searchPrev(request);
536     }
537 }
538
539 #include "traceloader.moc"