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