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