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