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