]> git.cworth.org Git - apitrace/blobdiff - gui/traceloader.cpp
gui: Handle calls stranded between frames gracefully (issue #117).
[apitrace] / gui / traceloader.cpp
index 0303ee72efd55f631127d3da49b2575cb3b2bb1b..6110c5972d6416752bc82a1c0472812625907549 100644 (file)
@@ -1,12 +1,13 @@
 #include "traceloader.h"
 
+#include "apitrace.h"
 #include <QDebug>
 #include <QFile>
 
 #define FRAMES_TO_CACHE 100
 
 static ApiTraceCall *
-apiCallFromTraceCall(const Trace::Call *call,
+apiCallFromTraceCall(const trace::Call *call,
                      const QHash<QString, QUrl> &helpHash,
                      ApiTraceFrame *frame,
                      TraceLoader *loader)
@@ -19,14 +20,15 @@ apiCallFromTraceCall(const Trace::Call *call,
 }
 
 TraceLoader::TraceLoader(QObject *parent)
-    : QObject(parent),
-      m_frameMarker(ApiTrace::FrameMarker_SwapBuffers)
+    : QObject(parent)
 {
 }
 
 TraceLoader::~TraceLoader()
 {
     m_parser.close();
+    qDeleteAll(m_signatures);
+    qDeleteAll(m_enumSignatures);
 }
 
 void TraceLoader::loadTrace(const QString &filename)
@@ -35,240 +37,190 @@ void TraceLoader::loadTrace(const QString &filename)
         loadHelpFile();
     }
 
+    if (!m_frameBookmarks.isEmpty()) {
+        qDeleteAll(m_signatures);
+        qDeleteAll(m_enumSignatures);
+        m_signatures.clear();
+        m_enumSignatures.clear();
+        m_frameBookmarks.clear();
+        m_createdFrames.clear();
+        m_parser.close();
+    }
+
     if (!m_parser.open(filename.toLatin1())) {
         qDebug() << "error: failed to open " << filename;
         return;
     }
-    qDebug()<<"load trace with "<<filename;
+
     emit startedParsing();
 
-    qDebug() <<"\t support offsets = "<<m_parser.supportsOffsets();
     if (m_parser.supportsOffsets()) {
-       scanTrace();
+        scanTrace();
     } else {
-       //Load the entire file into memory
-       parseTrace();
+        //Load the entire file into memory
+        parseTrace();
     }
-
+    emit guessedApi(static_cast<int>(m_parser.api));
     emit finishedParsing();
 }
 
-void TraceLoader::loadFrame(int frameIdx)
+void TraceLoader::loadFrame(ApiTraceFrame *currentFrame)
 {
-    if (m_parser.supportsOffsets()) {
-        int numOfCalls = numberOfCallsInFrame(frameIdx);
-        if (numOfCalls) {
-            const FrameOffset &frameOffset = m_frameOffsets[frameIdx];
-            std::vector<Trace::Call*> calls(numOfCalls);
-            m_parser.setCurrentOffset(frameOffset.start);
-            m_parser.setCurrentCallNumber(frameOffset.callNumber);
-
-            Trace::Call *call;
-            int parsedCalls = 0;
-            while ((call = m_parser.parse_call())) {
-
-                calls[parsedCalls] = call;
-                ++parsedCalls;
-
-                if (isCallAFrameMarker(call)) {
-                    break;
-                }
-
-            }
-            assert(parsedCalls == numOfCalls);
-//            emit parsedFrame();
-        }
-    }
-}
-
-void TraceLoader::setFrameMarker(ApiTrace::FrameMarker marker)
-{
-    m_frameMarker = marker;
-}
-
-bool TraceLoader::isCallAFrameMarker(const Trace::Call *call) const
-{
-    std::string name = call->name();
-
-    switch (m_frameMarker) {
-    case ApiTrace::FrameMarker_SwapBuffers:
-       return  name.find("SwapBuffers") != std::string::npos ||
-               name == "CGLFlushDrawable" ||
-               name == "glFrameTerminatorGREMEDY";
-       break;
-    case ApiTrace::FrameMarker_Flush:
-       return name == "glFlush";
-       break;
-    case ApiTrace::FrameMarker_Finish:
-       return name == "glFinish";
-       break;
-    case ApiTrace::FrameMarker_Clear:
-       return name == "glClear";
-       break;
-    }
-    return false;
+    fetchFrameContents(currentFrame);
 }
 
 int TraceLoader::numberOfFrames() const
 {
-    return m_frameOffsets.size();
+    return m_frameBookmarks.size();
 }
 
 int TraceLoader::numberOfCallsInFrame(int frameIdx) const
 {
-    if (frameIdx > m_frameOffsets.size()) {
+    if (frameIdx >= m_frameBookmarks.size()) {
         return 0;
     }
-    FrameOffsets::const_iterator itr =
-          m_frameOffsets.find(frameIdx);
+    FrameBookmarks::const_iterator itr =
+            m_frameBookmarks.find(frameIdx);
     return itr->numberOfCalls;
 }
 
 void TraceLoader::loadHelpFile()
 {
-   QFile file(":/resources/glreference.tsv");
-   if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-      QString line;
-      while (!file.atEnd()) {
-         line = file.readLine();
-         QString function = line.section('\t', 0, 0).trimmed();
-         QUrl url = QUrl(line.section('\t', 1, 1).trimmed());
-         //qDebug()<<"function = "<<function<<", url = "<<url.toString();
-         m_helpHash.insert(function, url);
-      }
-   } else {
-      qWarning() << "Couldn't open reference file "
-                 << file.fileName();
-   }
-   file.close();
+    QFile file(":/resources/glreference.tsv");
+    if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+        QString line;
+        while (!file.atEnd()) {
+            line = file.readLine();
+            QString function = line.section('\t', 0, 0).trimmed();
+            QUrl url = QUrl(line.section('\t', 1, 1).trimmed());
+            //qDebug()<<"function = "<<function<<", url = "<<url.toString();
+            m_helpHash.insert(function, url);
+        }
+    } else {
+        qWarning() << "Couldn't open reference file "
+                   << file.fileName();
+    }
+    file.close();
 }
 
 void TraceLoader::scanTrace()
 {
-   QList<ApiTraceFrame*> frames;
-   ApiTraceFrame *currentFrame = 0;
-
-   Trace::Call *call;
-   Trace::File::Offset startOffset;
-   int numOfFrames = 0;
-   int numOfCalls = 0;
-   unsigned callNum = 0;
-   int lastPercentReport = 0;
-
-   startOffset = m_parser.currentOffset();
-   callNum = m_parser.currentCallNumber();
-
-   while ((call = m_parser.scan_call())) {
-       ++numOfCalls;
-
-       if (isCallAFrameMarker(call)) {
-           Trace::File::Offset endOffset = m_parser.currentOffset();
-           FrameOffset frameOffset(startOffset);
-           frameOffset.numberOfCalls = numOfCalls;
-           frameOffset.callNumber = callNum;
-
-           currentFrame = new ApiTraceFrame();
-           currentFrame->number = numOfFrames;
-           currentFrame->setNumChildren(numOfCalls);
-           frames.append(currentFrame);
-
-           m_frameOffsets[numOfFrames] = frameOffset;
-           ++numOfFrames;
-
-           if (m_parser.percentRead() - lastPercentReport >= 5) {
-               emit parsed(m_parser.percentRead());
-               lastPercentReport = m_parser.percentRead();
-           }
-           startOffset = endOffset;
-           callNum = m_parser.currentCallNumber();
-           numOfCalls = 0;
-       }
-       //call->dump(std::cout, color);
-       delete call;
-   }
-
-   if (numOfCalls) {
-//      Trace::File::Offset endOffset = m_parser.currentOffset();
-      FrameOffset frameOffset(startOffset);
-      frameOffset.numberOfCalls = numOfCalls;
-      frameOffset.callNumber = callNum;
-
-      currentFrame = new ApiTraceFrame();
-      currentFrame->number = numOfFrames;
-      currentFrame->setNumChildren(numOfCalls);
-      frames.append(currentFrame);
-
-      m_frameOffsets[numOfFrames] = frameOffset;
-      ++numOfFrames;
-   }
-
-   emit parsed(100);
-
-   emit framesLoaded(frames);
+    QList<ApiTraceFrame*> frames;
+    ApiTraceFrame *currentFrame = 0;
+
+    trace::Call *call;
+    trace::ParseBookmark startBookmark;
+    int numOfFrames = 0;
+    int numOfCalls = 0;
+    int lastPercentReport = 0;
+
+    m_parser.getBookmark(startBookmark);
+
+    while ((call = m_parser.scan_call())) {
+        ++numOfCalls;
+
+        if (call->flags & trace::CALL_FLAG_END_FRAME) {
+            FrameBookmark frameBookmark(startBookmark);
+            frameBookmark.numberOfCalls = numOfCalls;
+
+            currentFrame = new ApiTraceFrame();
+            currentFrame->number = numOfFrames;
+            currentFrame->setNumChildren(numOfCalls);
+            currentFrame->setLastCallIndex(call->no);
+            frames.append(currentFrame);
+
+            m_createdFrames.append(currentFrame);
+            m_frameBookmarks[numOfFrames] = frameBookmark;
+            ++numOfFrames;
+
+            if (m_parser.percentRead() - lastPercentReport >= 5) {
+                emit parsed(m_parser.percentRead());
+                lastPercentReport = m_parser.percentRead();
+            }
+            m_parser.getBookmark(startBookmark);
+            numOfCalls = 0;
+        }
+        delete call;
+    }
+
+    if (numOfCalls) {
+        //trace::File::Bookmark endBookmark = m_parser.currentBookmark();
+        FrameBookmark frameBookmark(startBookmark);
+        frameBookmark.numberOfCalls = numOfCalls;
+
+        currentFrame = new ApiTraceFrame();
+        currentFrame->number = numOfFrames;
+        currentFrame->setNumChildren(numOfCalls);
+        frames.append(currentFrame);
+
+        m_createdFrames.append(currentFrame);
+        m_frameBookmarks[numOfFrames] = frameBookmark;
+        ++numOfFrames;
+    }
+
+    emit parsed(100);
+
+    emit framesLoaded(frames);
 }
 
 void TraceLoader::parseTrace()
 {
-   QList<ApiTraceFrame*> frames;
-   ApiTraceFrame *currentFrame = 0;
-   int frameCount = 0;
-   QVector<ApiTraceCall*> calls;
-   quint64 binaryDataSize = 0;
-
-   int lastPercentReport = 0;
-
-   Trace::Call *call = m_parser.parse_call();
-   while (call) {
-      //std::cout << *call;
-      if (!currentFrame) {
-         currentFrame = new ApiTraceFrame();
-         currentFrame->number = frameCount;
-         ++frameCount;
-      }
-      ApiTraceCall *apiCall =
-            apiCallFromTraceCall(call, m_helpHash, currentFrame, this);
-      calls.append(apiCall);
-      if (apiCall->hasBinaryData()) {
-         QByteArray data =
-               apiCall->arguments()[apiCall->binaryDataIndex()].toByteArray();
-         binaryDataSize += data.size();
-      }
-      if (ApiTrace::isCallAFrameMarker(apiCall,
-                                       m_frameMarker)) {
-         calls.squeeze();
-         currentFrame->setCalls(calls, binaryDataSize);
-         calls.clear();
-         frames.append(currentFrame);
-         currentFrame = 0;
-         binaryDataSize = 0;
-         if (frames.count() >= FRAMES_TO_CACHE) {
-            emit framesLoaded(frames);
-            frames.clear();
-         }
-         if (m_parser.percentRead() - lastPercentReport >= 5) {
-            qDebug()<<"emitting = " << m_parser.percentRead();
-            emit parsed(m_parser.percentRead());
-            lastPercentReport = m_parser.percentRead();
-         }
-      }
-      delete call;
-      call = m_parser.parse_call();
-   }
-
-   //last frames won't have markers
-   //  it's just a bunch of Delete calls for every object
-   //  after the last SwapBuffers
-   if (currentFrame) {
-      if (!frames.count()) {
-         calls.squeeze();
-         currentFrame->setCalls(calls, binaryDataSize);
-      }
-      frames.append(currentFrame);
-      currentFrame = 0;
-   }
-   if (frames.count()) {
-      emit framesLoaded(frames);
-   }
+    QList<ApiTraceFrame*> frames;
+    ApiTraceFrame *currentFrame = 0;
+    int frameCount = 0;
+    QVector<ApiTraceCall*> calls;
+    quint64 binaryDataSize = 0;
+
+    int lastPercentReport = 0;
+
+    trace::Call *call = m_parser.parse_call();
+    while (call) {
+        //std::cout << *call;
+        if (!currentFrame) {
+            currentFrame = new ApiTraceFrame();
+            currentFrame->number = frameCount;
+            ++frameCount;
+        }
+        ApiTraceCall *apiCall =
+                apiCallFromTraceCall(call, m_helpHash, currentFrame, this);
+        calls.append(apiCall);
+        if (apiCall->hasBinaryData()) {
+            QByteArray data =
+                    apiCall->arguments()[apiCall->binaryDataIndex()].toByteArray();
+            binaryDataSize += data.size();
+        }
+        if (call->flags & trace::CALL_FLAG_END_FRAME) {
+            calls.squeeze();
+            currentFrame->setCalls(calls, binaryDataSize);
+            calls.clear();
+            frames.append(currentFrame);
+            currentFrame = 0;
+            binaryDataSize = 0;
+            if (frames.count() >= FRAMES_TO_CACHE) {
+                emit framesLoaded(frames);
+                frames.clear();
+            }
+            if (m_parser.percentRead() - lastPercentReport >= 5) {
+                emit parsed(m_parser.percentRead());
+                lastPercentReport = m_parser.percentRead();
+            }
+        }
+        delete call;
+        call = m_parser.parse_call();
+    }
+
+    //last frames won't have markers
+    //  it's just a bunch of Delete calls for every object
+    //  after the last SwapBuffers
+    if (currentFrame) {
+        calls.squeeze();
+        currentFrame->setCalls(calls, binaryDataSize);
+        frames.append(currentFrame);
+        currentFrame = 0;
+    }
+    if (frames.count()) {
+        emit framesLoaded(frames);
+    }
 }
 
 
@@ -302,4 +254,235 @@ void TraceLoader::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature
     m_enumSignatures[id] = signature;
 }
 
+void TraceLoader::searchNext(const ApiTrace::SearchRequest &request)
+{
+    Q_ASSERT(m_parser.supportsOffsets());
+    if (m_parser.supportsOffsets()) {
+        int startFrame = m_createdFrames.indexOf(request.frame);
+        const FrameBookmark &frameBookmark = m_frameBookmarks[startFrame];
+        m_parser.setBookmark(frameBookmark.start);
+        trace::Call *call = 0;
+        while ((call = m_parser.parse_call())) {
+
+            if (callContains(call, request.text, request.cs)) {
+                unsigned frameIdx = callInFrame(call->no);
+                ApiTraceFrame *frame = m_createdFrames[frameIdx];
+                const QVector<ApiTraceCall*> calls =
+                        fetchFrameContents(frame);
+                for (int i = 0; i < calls.count(); ++i) {
+                    if (calls[i]->index() == call->no) {
+                        emit searchResult(request, ApiTrace::SearchResult_Found,
+                                          calls[i]);
+                        break;
+                    }
+                }
+                delete call;
+                return;
+            }
+
+            delete call;
+        }
+    }
+    emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
+}
+
+void TraceLoader::searchPrev(const ApiTrace::SearchRequest &request)
+{
+    Q_ASSERT(m_parser.supportsOffsets());
+    if (m_parser.supportsOffsets()) {
+        int startFrame = m_createdFrames.indexOf(request.frame);
+        trace::Call *call = 0;
+        QList<trace::Call*> frameCalls;
+        int frameIdx = startFrame;
+
+        const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
+        int numCallsToParse = frameBookmark.numberOfCalls;
+        m_parser.setBookmark(frameBookmark.start);
+
+        while ((call = m_parser.parse_call())) {
+
+            frameCalls.append(call);
+            --numCallsToParse;
+
+            if (numCallsToParse == 0) {
+                bool foundCall = searchCallsBackwards(frameCalls,
+                                                      frameIdx,
+                                                      request);
+
+                qDeleteAll(frameCalls);
+                frameCalls.clear();
+                if (foundCall) {
+                    return;
+                }
+
+                --frameIdx;
+
+                if (frameIdx >= 0) {
+                    const FrameBookmark &frameBookmark =
+                            m_frameBookmarks[frameIdx];
+                    m_parser.setBookmark(frameBookmark.start);
+                    numCallsToParse = frameBookmark.numberOfCalls;
+                }
+            }
+        }
+    }
+    emit searchResult(request, ApiTrace::SearchResult_NotFound, 0);
+}
+
+bool TraceLoader::searchCallsBackwards(const QList<trace::Call*> &calls,
+                                       int frameIdx,
+                                       const ApiTrace::SearchRequest &request)
+{
+    for (int i = calls.count() - 1; i >= 0; --i) {
+        trace::Call *call = calls[i];
+        if (callContains(call, request.text, request.cs)) {
+            ApiTraceFrame *frame = m_createdFrames[frameIdx];
+            const QVector<ApiTraceCall*> apiCalls =
+                    fetchFrameContents(frame);
+            for (int i = 0; i < apiCalls.count(); ++i) {
+                if (apiCalls[i]->index() == call->no) {
+                    emit searchResult(request,
+                                      ApiTrace::SearchResult_Found,
+                                      apiCalls[i]);
+                    break;
+                }
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+int TraceLoader::callInFrame(int callIdx) const
+{
+    unsigned numCalls = 0;
+
+    for (int frameIdx = 0; frameIdx < m_frameBookmarks.size(); ++frameIdx) {
+        const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
+        unsigned firstCall = numCalls;
+        unsigned endCall = numCalls + frameBookmark.numberOfCalls;
+        if (firstCall <= callIdx && endCall > callIdx) {
+            return frameIdx;
+        }
+        numCalls = endCall;
+    }
+    Q_ASSERT(!"call not in the trace");
+    return 0;
+}
+
+bool TraceLoader::callContains(trace::Call *call,
+                               const QString &str,
+                               Qt::CaseSensitivity sensitivity)
+{
+    /*
+     * FIXME: do string comparison directly on trace::Call
+     */
+    ApiTraceCall *apiCall = apiCallFromTraceCall(call, m_helpHash,
+                                                 0, this);
+    bool result = apiCall->contains(str, sensitivity);
+    delete apiCall;
+    return result;
+}
+
+QVector<ApiTraceCall*>
+TraceLoader::fetchFrameContents(ApiTraceFrame *currentFrame)
+{
+    Q_ASSERT(currentFrame);
+
+    if (currentFrame->isLoaded()) {
+        return currentFrame->calls();
+    }
+
+    if (m_parser.supportsOffsets()) {
+        unsigned frameIdx = currentFrame->number;
+        int numOfCalls = numberOfCallsInFrame(frameIdx);
+
+        if (numOfCalls) {
+            quint64 binaryDataSize = 0;
+            QVector<ApiTraceCall*> calls(numOfCalls);
+            const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
+
+            m_parser.setBookmark(frameBookmark.start);
+
+            trace::Call *call;
+            int parsedCalls = 0;
+            while ((call = m_parser.parse_call())) {
+                ApiTraceCall *apiCall =
+                    apiCallFromTraceCall(call, m_helpHash,
+                                         currentFrame, this);
+                Q_ASSERT(apiCall);
+                Q_ASSERT(parsedCalls < calls.size());
+                calls[parsedCalls++] = apiCall;
+                if (apiCall->hasBinaryData()) {
+                    QByteArray data =
+                        apiCall->arguments()[
+                            apiCall->binaryDataIndex()].toByteArray();
+                    binaryDataSize += data.size();
+                }
+
+                delete call;
+
+                if (apiCall->flags() & trace::CALL_FLAG_END_FRAME) {
+                    break;
+                }
+
+            }
+            // There can be fewer parsed calls when call in different
+            // threads cross the frame boundary
+            Q_ASSERT(parsedCalls <= numOfCalls);
+            Q_ASSERT(parsedCalls <= calls.size());
+            calls.resize(parsedCalls);
+            calls.squeeze();
+
+            Q_ASSERT(parsedCalls <= currentFrame->numChildrenToLoad());
+            emit frameContentsLoaded(currentFrame,
+                                     calls, binaryDataSize);
+            return calls;
+        }
+    }
+    return QVector<ApiTraceCall*>();
+}
+
+void TraceLoader::findFrameStart(ApiTraceFrame *frame)
+{
+    if (!frame->isLoaded()) {
+        loadFrame(frame);
+    }
+    emit foundFrameStart(frame);
+}
+
+void TraceLoader::findFrameEnd(ApiTraceFrame *frame)
+{
+    if (!frame->isLoaded()) {
+        loadFrame(frame);
+    }
+    emit foundFrameEnd(frame);
+}
+
+void TraceLoader::findCallIndex(int index)
+{
+    int frameIdx = callInFrame(index);
+    ApiTraceFrame *frame = m_createdFrames[frameIdx];
+    QVector<ApiTraceCall*> calls = fetchFrameContents(frame);
+    QVector<ApiTraceCall*>::const_iterator itr;
+    ApiTraceCall *call = 0;
+    for (itr = calls.constBegin(); itr != calls.constEnd(); ++itr) {
+        if ((*itr)->index() == index) {
+            call = *itr;
+        }
+    }
+    if (call) {
+        emit foundCallIndex(call);
+    }
+}
+
+void TraceLoader::search(const ApiTrace::SearchRequest &request)
+{
+    if (request.direction == ApiTrace::SearchRequest::Next) {
+        searchNext(request);
+    } else {
+        searchPrev(request);
+    }
+}
+
 #include "traceloader.moc"