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