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