]> git.cworth.org Git - apitrace/commitdiff
Merge branch 'on-demand-loading'
authorZack Rusin <zack@kde.org>
Wed, 21 Sep 2011 04:30:58 +0000 (00:30 -0400)
committerZack Rusin <zack@kde.org>
Wed, 21 Sep 2011 04:36:01 +0000 (00:36 -0400)
Fixes #36

29 files changed:
CMakeLists.txt
gui/CMakeLists.txt
gui/apitrace.cpp
gui/apitrace.h
gui/apitracecall.cpp
gui/apitracecall.h
gui/apitracefilter.cpp
gui/apitracefilter.h
gui/apitracemodel.cpp
gui/apitracemodel.h
gui/main.cpp
gui/mainwindow.cpp
gui/mainwindow.h
gui/retracer.cpp
gui/retracer.h
gui/saverthread.cpp
gui/saverthread.h
gui/traceloader.cpp [new file with mode: 0644]
gui/traceloader.h [new file with mode: 0644]
trace_file.cpp
trace_file.hpp
trace_loader.cpp [new file with mode: 0644]
trace_loader.hpp [new file with mode: 0644]
trace_model.cpp
trace_model.hpp
trace_parser.cpp
trace_parser.hpp
trace_snappyfile.cpp
trace_snappyfile.hpp

index 09bd2c4e429f429c14ab196dd0b57aa7ed8aa89c..9c91c9077313b8ac333d0468b508c94a388102e0 100755 (executable)
@@ -187,6 +187,7 @@ add_library (common
     trace_writer.cpp
     trace_local_writer.cpp
     trace_model_writer.cpp
+    trace_loader.cpp
     image.cpp
     image_bmp.cpp
     image_pnm.cpp
@@ -204,7 +205,6 @@ link_libraries (common)
 add_executable (tracedump tracedump.cpp)
 install (TARGETS tracedump RUNTIME DESTINATION bin) 
 
-
 ##############################################################################
 # API tracers
 
index 6973450a9e5c16a1688ae24c233f8623d319af00..777bc9255c7c1d4c83207ec7ac12e826657e1700 100644 (file)
@@ -12,7 +12,6 @@ set(qapitrace_SRCS
    glsledit.cpp
    imageviewer.cpp
    jumpwidget.cpp
-   loaderthread.cpp
    mainwindow.cpp
    main.cpp
    retracer.cpp
@@ -21,6 +20,7 @@ set(qapitrace_SRCS
    settingsdialog.cpp
    shaderssourcewidget.cpp
    tracedialog.cpp
+   traceloader.cpp
    traceprocess.cpp
    vertexdatainterpreter.cpp
  )
index 51c4755b28194a70b635ac16d3230ef5c03623b1..bc862097099c75c0d48ce1a093ba3aff70711866 100644 (file)
@@ -1,32 +1,75 @@
 #include "apitrace.h"
 
-#include "loaderthread.h"
+#include "traceloader.h"
 #include "saverthread.h"
 
+#include <QDebug>
 #include <QDir>
+#include <QThread>
 
 ApiTrace::ApiTrace()
     : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers),
       m_needsSaving(false)
 {
-    m_loader = new LoaderThread(this);
-    connect(m_loader, SIGNAL(parsedFrames(const QList<ApiTraceFrame*>)),
+    m_loader = new TraceLoader();
+
+    connect(this, SIGNAL(loadTrace(QString)),
+            m_loader, SLOT(loadTrace(QString)));
+    connect(this, SIGNAL(requestFrame(ApiTraceFrame*)),
+            m_loader, SLOT(loadFrame(ApiTraceFrame*)));
+    connect(m_loader, SIGNAL(framesLoaded(const QList<ApiTraceFrame*>)),
             this, SLOT(addFrames(const QList<ApiTraceFrame*>)));
-    connect(m_loader, SIGNAL(started()),
+    connect(m_loader,
+            SIGNAL(frameContentsLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)),
+            this,
+            SLOT(loaderFrameLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)));
+    connect(m_loader, SIGNAL(finishedParsing()),
+            this, SLOT(finishedParsing()));
+    connect(this, SIGNAL(loaderSearchNext(int,QString,Qt::CaseSensitivity)),
+            m_loader, SLOT(searchNext(int,QString,Qt::CaseSensitivity)));
+    connect(this, SIGNAL(loaderSearchPrev(int,QString,Qt::CaseSensitivity)),
+            m_loader, SLOT(searchPrev(int,QString,Qt::CaseSensitivity)));
+    connect(m_loader,
+            SIGNAL(searchResult(ApiTrace::SearchResult,ApiTraceCall*)),
+            this,
+            SLOT(loaderSearchResult(ApiTrace::SearchResult,ApiTraceCall*)));
+    connect(this, SIGNAL(loaderFindFrameStart(ApiTraceFrame*)),
+            m_loader, SLOT(findFrameStart(ApiTraceFrame*)));
+    connect(this, SIGNAL(loaderFindFrameEnd(ApiTraceFrame*)),
+            m_loader, SLOT(findFrameEnd(ApiTraceFrame*)));
+    connect(m_loader, SIGNAL(foundFrameStart(ApiTraceFrame*)),
+            this, SIGNAL(foundFrameStart(ApiTraceFrame*)));
+    connect(m_loader, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
+            this, SIGNAL(foundFrameEnd(ApiTraceFrame*)));
+    connect(this, SIGNAL(loaderFindCallIndex(int)),
+            m_loader, SLOT(findCallIndex(int)));
+    connect(m_loader, SIGNAL(foundCallIndex(ApiTraceCall*)),
+            this, SIGNAL(foundCallIndex(ApiTraceCall*)));
+
+
+    connect(m_loader, SIGNAL(startedParsing()),
             this, SIGNAL(startedLoadingTrace()));
-    connect(m_loader, SIGNAL(finished()),
+    connect(m_loader, SIGNAL(parsed(int)),
+            this, SIGNAL(loaded(int)));
+    connect(m_loader, SIGNAL(finishedParsing()),
             this, SIGNAL(finishedLoadingTrace()));
 
+
     m_saver = new SaverThread(this);
     connect(m_saver, SIGNAL(traceSaved()),
             this, SLOT(slotSaved()));
     connect(m_saver, SIGNAL(traceSaved()),
             this, SIGNAL(saved()));
+
+    m_loaderThread = new QThread();
+    m_loader->moveToThread(m_loaderThread);
+    m_loaderThread->start();
 }
 
 ApiTrace::~ApiTrace()
 {
-    qDeleteAll(m_calls);
+    m_loaderThread->quit();
+    m_loaderThread->deleteLater();
     qDeleteAll(m_frames);
     delete m_loader;
     delete m_saver;
@@ -35,8 +78,9 @@ ApiTrace::~ApiTrace()
 bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
                                   ApiTrace::FrameMarker marker)
 {
-    if (!call)
+    if (!call) {
         return false;
+    }
 
     switch (marker) {
     case FrameMarker_SwapBuffers:
@@ -58,13 +102,14 @@ bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
 
 bool ApiTrace::isEmpty() const
 {
-    return m_calls.isEmpty();
+    return m_frames.isEmpty();
 }
 
 QString ApiTrace::fileName() const
 {
-    if (edited())
+    if (edited()) {
         return m_tempFileName;
+    }
 
     return m_fileName;
 }
@@ -74,21 +119,6 @@ ApiTrace::FrameMarker ApiTrace::frameMarker() const
     return m_frameMarker;
 }
 
-QVector<ApiTraceCall*> ApiTrace::calls() const
-{
-    return m_calls;
-}
-
-ApiTraceCall * ApiTrace::callAt(int idx) const
-{
-    return m_calls.value(idx);
-}
-
-int ApiTrace::numCalls() const
-{
-    return m_calls.count();
-}
-
 QList<ApiTraceFrame*> ApiTrace::frames() const
 {
     return m_frames;
@@ -107,10 +137,11 @@ int ApiTrace::numFrames() const
 int ApiTrace::numCallsInFrame(int idx) const
 {
     const ApiTraceFrame *frame = frameAt(idx);
-    if (frame)
+    if (frame) {
         return frame->numChildren();
-    else
+    } else {
         return 0;
+    }
 }
 
 void ApiTrace::setFileName(const QString &name)
@@ -118,35 +149,18 @@ void ApiTrace::setFileName(const QString &name)
     if (m_fileName != name) {
         m_fileName = name;
 
-        if (m_loader->isRunning()) {
-            m_loader->terminate();
-            m_loader->wait();
-        }
         m_frames.clear();
-        m_calls.clear();
         m_errors.clear();
         m_editedCalls.clear();
         m_needsSaving = false;
         emit invalidated();
 
-        m_loader->loadFile(m_fileName);
-    }
-}
-
-void ApiTrace::setFrameMarker(FrameMarker marker)
-{
-    if (m_frameMarker != marker) {
-        emit framesInvalidated();
-
-        qDeleteAll(m_frames);
-        m_frames.clear();
-        detectFrames();
+        emit loadTrace(m_fileName);
     }
 }
 
 void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
 {
-    QVector<ApiTraceCall*> calls;
     int currentFrames = m_frames.count();
     int numNewFrames = frames.count();
 
@@ -154,57 +168,20 @@ void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
 
     m_frames += frames;
 
-    int currentCalls = m_calls.count();
-    int numNewCalls = 0;
     foreach(ApiTraceFrame *frame, frames) {
-        Q_ASSERT(this == frame->parentTrace());
-        numNewCalls += frame->numChildren();
-        calls += frame->calls();
+        frame->setParentTrace(this);
     }
-    m_calls.reserve(m_calls.count() + calls.count() + 1);
-    m_calls += calls;
-
-    emit endAddingFrames();
-    emit callsAdded(currentCalls, numNewCalls);
-}
-
-void ApiTrace::detectFrames()
-{
-    if (m_calls.isEmpty())
-        return;
-
-    emit beginAddingFrames(0, m_frames.count());
 
-    ApiTraceFrame *currentFrame = 0;
-    foreach(ApiTraceCall *apiCall, m_calls) {
-        if (!currentFrame) {
-            currentFrame = new ApiTraceFrame(this);
-            currentFrame->number = m_frames.count();
-        }
-        apiCall->setParentFrame(currentFrame);
-        currentFrame->addCall(apiCall);
-        if (ApiTrace::isCallAFrameMarker(apiCall,
-                                         m_frameMarker)) {
-            m_frames.append(currentFrame);
-            currentFrame = 0;
-        }
-    }
-    //last frames won't have markers
-    //  it's just a bunch of Delete calls for every object
-    //  after the last SwapBuffers
-    if (currentFrame) {
-        m_frames.append(currentFrame);
-        currentFrame = 0;
-    }
     emit endAddingFrames();
 }
 
 ApiTraceCall * ApiTrace::callWithIndex(int idx) const
 {
-    for (int i = 0; i < m_calls.count(); ++i) {
-        ApiTraceCall *call = m_calls[i];
-        if (call->index() == idx)
+    for (int i = 0; i < m_frames.count(); ++i) {
+        ApiTraceCall *call = m_frames[i]->callWithIndex(idx);
+        if (call) {
             return call;
+        }
     }
     return NULL;
 }
@@ -212,10 +189,16 @@ ApiTraceCall * ApiTrace::callWithIndex(int idx) const
 ApiTraceState ApiTrace::defaultState() const
 {
     ApiTraceFrame *frame = frameAt(0);
-    if (!frame || !frame->hasState())
+    if (!frame || !frame->isLoaded() || frame->isEmpty()) {
+        return ApiTraceState();
+    }
+
+    ApiTraceCall *firstCall = frame->calls().first();
+    if (!firstCall->hasState()) {
         return ApiTraceState();
+    }
 
-    return *frame->state();
+    return *firstCall->state();
 }
 
 void ApiTrace::callEdited(ApiTraceCall *call)
@@ -259,7 +242,9 @@ void ApiTrace::save()
     QDir dir;
     emit startedSaving();
     dir.mkpath(fi.absolutePath());
-    m_saver->saveFile(m_tempFileName, m_calls);
+    m_saver->saveFile(m_tempFileName,
+                      m_fileName,
+                      m_editedCalls);
 }
 
 void ApiTrace::slotSaved()
@@ -272,51 +257,223 @@ bool ApiTrace::isSaving() const
     return m_saver->isRunning();
 }
 
-void ApiTrace::callError(ApiTraceCall *call)
+bool ApiTrace::hasErrors() const
 {
-    Q_ASSERT(call);
+    return !m_errors.isEmpty();
+}
 
-    if (call->hasError())
-        m_errors.insert(call);
-    else
-        m_errors.remove(call);
+void ApiTrace::loadFrame(ApiTraceFrame *frame)
+{
+    Q_ASSERT(!frame->isLoaded());
+    emit requestFrame(frame);
+}
 
-    emit changed(call);
+void ApiTrace::finishedParsing()
+{
+    ApiTraceFrame *firstFrame = m_frames[0];
+    if (firstFrame && !firstFrame->isLoaded()) {
+        loadFrame(firstFrame);
+    }
 }
 
-bool ApiTrace::hasErrors() const
+void ApiTrace::loaderFrameLoaded(ApiTraceFrame *frame,
+                                 const QVector<ApiTraceCall*> &calls,
+                                 quint64 binaryDataSize)
+{
+    Q_ASSERT(frame->numChildrenToLoad() == calls.size());
+    emit beginLoadingFrame(frame, calls.size());
+    frame->setCalls(calls, binaryDataSize);
+    emit endLoadingFrame(frame);
+
+    if (!m_queuedErrors.isEmpty()) {
+        QList< QPair<ApiTraceFrame*, ApiTraceError> >::iterator itr;
+        for (itr = m_queuedErrors.begin(); itr != m_queuedErrors.end();
+             ++itr) {
+            const ApiTraceError &error = (*itr).second;
+            if ((*itr).first == frame) {
+                ApiTraceCall *call = frame->callWithIndex(error.callIndex);
+
+                if (!call) {
+                    continue;
+                }
+
+                call->setError(error.message);
+                m_queuedErrors.erase(itr);
+
+                if (call->hasError()) {
+                    m_errors.insert(call);
+                } else {
+                    m_errors.remove(call);
+                }
+                emit changed(call);
+            }
+        }
+    }
+}
+
+void ApiTrace::findNext(ApiTraceFrame *frame,
+                        ApiTraceCall *from,
+                        const QString &str,
+                        Qt::CaseSensitivity sensitivity)
 {
-    return !m_errors.isEmpty();
+    ApiTraceCall *foundCall = 0;
+    int frameIdx = m_frames.indexOf(frame);
+
+    if (frame->isLoaded()) {
+        foundCall = frame->findNextCall(from, str, sensitivity);
+        if (foundCall) {
+            emit findResult(SearchResult_Found, foundCall);
+            return;
+        }
+
+        //if the frame is loaded we already searched it above
+        // so skip it
+        frameIdx += 1;
+    }
+
+    for (int i = frameIdx; i < m_frames.count(); ++i) {
+        ApiTraceFrame *frame = m_frames[i];
+        if (!frame->isLoaded()) {
+            emit loaderSearchNext(i, str, sensitivity);
+            return;
+        } else {
+            ApiTraceCall *call = frame->findNextCall(0, str, sensitivity);
+            if (call) {
+                emit findResult(SearchResult_Found, call);
+                return;
+            }
+        }
+    }
+    emit findResult(SearchResult_Wrapped, 0);
 }
 
-ApiTraceCallSignature * ApiTrace::signature(unsigned id)
+void ApiTrace::findPrev(ApiTraceFrame *frame,
+                        ApiTraceCall *from,
+                        const QString &str,
+                        Qt::CaseSensitivity sensitivity)
 {
-    if (id >= m_signatures.count()) {
-        m_signatures.resize(id + 1);
-        return NULL;
-    } else {
-        return m_signatures[id];
+    ApiTraceCall *foundCall = 0;
+    int frameIdx = m_frames.indexOf(frame);
+
+    if (frame->isLoaded()) {
+        foundCall = frame->findPrevCall(from, str, sensitivity);
+        if (foundCall) {
+            emit findResult(SearchResult_Found, foundCall);
+            return;
+        }
+
+        //if the frame is loaded we already searched it above
+        // so skip it
+        frameIdx -= 1;
+    }
+
+    for (int i = frameIdx; i >= 0; --i) {
+        ApiTraceFrame *frame = m_frames[i];
+        if (!frame->isLoaded()) {
+            emit loaderSearchPrev(i, str, sensitivity);
+            return;
+        } else {
+            ApiTraceCall *call = frame->findPrevCall(0, str, sensitivity);
+            if (call) {
+                emit findResult(SearchResult_Found, call);
+                return;
+            }
+        }
     }
+    emit findResult(SearchResult_Wrapped, 0);
+}
+
+void ApiTrace::loaderSearchResult(ApiTrace::SearchResult result,
+                                  ApiTraceCall *call)
+{
+    //qDebug()<<"Search result = "<<result
+    //       <<", call is = "<<call;
+    emit findResult(result, call);
 }
 
-void ApiTrace::addSignature(unsigned id, ApiTraceCallSignature *signature)
+void ApiTrace::findFrameStart(ApiTraceFrame *frame)
 {
-    m_signatures[id] = signature;
+    if (frame->isLoaded()) {
+        emit foundFrameStart(frame);
+    } else {
+        emit loaderFindFrameStart(frame);
+    }
 }
 
-ApiTraceEnumSignature * ApiTrace::enumSignature(unsigned id)
+void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
 {
-    if (id >= m_enumSignatures.count()) {
-        m_enumSignatures.resize(id + 1);
-        return NULL;
+    if (frame->isLoaded()) {
+        emit foundFrameEnd(frame);
     } else {
-        return m_enumSignatures[id];
+        emit loaderFindFrameEnd(frame);
     }
 }
 
-void ApiTrace::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature)
+void ApiTrace::findCallIndex(int index)
 {
-    m_enumSignatures[id] = signature;
+    int frameIdx = callInFrame(index);
+    ApiTraceFrame *frame = 0;
+
+    if (frameIdx < 0) {
+        emit foundCallIndex(0);
+        return;
+    }
+
+    frame = m_frames[frameIdx];
+
+    if (frame) {
+        if (frame->isLoaded()) {
+            ApiTraceCall *call = frame->callWithIndex(index);
+            emit foundCallIndex(call);
+        } else {
+            emit loaderFindCallIndex(index);
+        }
+    }
+}
+
+int ApiTrace::callInFrame(int callIdx) const
+{
+    unsigned numCalls = 0;
+
+    for (int frameIdx = 0; frameIdx <= m_frames.size(); ++frameIdx) {
+        const ApiTraceFrame *frame = m_frames[frameIdx];
+        unsigned numCallsInFrame =  frame->isLoaded()
+                ? frame->numChildren()
+                : frame->numChildrenToLoad();
+        unsigned firstCall = numCalls;
+        unsigned endCall = numCalls + numCallsInFrame;
+        if (firstCall <= callIdx && endCall > callIdx) {
+            return frameIdx;
+        }
+        numCalls = endCall;
+    }
+
+    return -1;
+}
+
+void ApiTrace::setCallError(const ApiTraceError &error)
+{
+    int frameIdx = callInFrame(error.callIndex);
+    ApiTraceFrame *frame = 0;
+
+    if (frameIdx < 0) {
+        return;
+    }
+    frame = m_frames[frameIdx];
+
+    if (frame->isLoaded()) {
+        ApiTraceCall *call = frame->callWithIndex(error.callIndex);
+        call->setError(error.message);
+        if (call->hasError()) {
+            m_errors.insert(call);
+        } else {
+            m_errors.remove(call);
+        }
+        emit changed(call);
+    } else {
+        emit requestFrame(frame);
+        m_queuedErrors.append(qMakePair(frame, error));
+    }
 }
 
 #include "apitrace.moc"
index 036a84cdd0b14f150560570c6104b8c543772169..bf90d17daf5577a656893726579f4b3049259fe0 100644 (file)
@@ -6,8 +6,9 @@
 #include <QObject>
 #include <QSet>
 
-class LoaderThread;
+class TraceLoader;
 class SaverThread;
+class QThread;
 
 class ApiTrace : public QObject
 {
@@ -19,6 +20,12 @@ public:
         FrameMarker_Finish,
         FrameMarker_Clear
     };
+    enum SearchResult {
+        SearchResult_NotFound,
+        SearchResult_Found,
+        SearchResult_Wrapped
+    };
+
     static bool isCallAFrameMarker(const ApiTraceCall *call,
                                    FrameMarker marker);
 public:
@@ -33,17 +40,7 @@ public:
 
     ApiTraceState defaultState() const;
 
-    ApiTraceCallSignature *signature(unsigned id);
-    void addSignature(unsigned id, ApiTraceCallSignature *signature);
-
-    ApiTraceEnumSignature *enumSignature(unsigned id);
-    void addEnumSignature(unsigned id, ApiTraceEnumSignature *signature);
-
-
-    QVector<ApiTraceCall*> calls() const;
-    ApiTraceCall *callAt(int idx) const;
     ApiTraceCall *callWithIndex(int idx) const;
-    int numCalls() const;
 
     QList<ApiTraceFrame*> frames() const;
     ApiTraceFrame *frameAt(int idx) const;
@@ -63,27 +60,67 @@ public:
 
 public slots:
     void setFileName(const QString &name);
-    void setFrameMarker(FrameMarker marker);
     void save();
+    void loadFrame(ApiTraceFrame *frame);
+    void findNext(ApiTraceFrame *frame,
+                  ApiTraceCall *call,
+                  const QString &str,
+                  Qt::CaseSensitivity sensitivity);
+    void findPrev(ApiTraceFrame *frame,
+                  ApiTraceCall *call,
+                  const QString &str,
+                  Qt::CaseSensitivity sensitivity);
+    void findFrameStart(ApiTraceFrame *frame);
+    void findFrameEnd(ApiTraceFrame *frame);
+    void findCallIndex(int index);
+    void setCallError(const ApiTraceError &error);
+
 
 signals:
+    void loadTrace(const QString &name);
+    void requestFrame(ApiTraceFrame *frame);
     void startedLoadingTrace();
+    void loaded(int percent);
     void finishedLoadingTrace();
     void invalidated();
     void framesInvalidated();
     void changed(ApiTraceCall *call);
     void startedSaving();
     void saved();
+    void findResult(ApiTrace::SearchResult result,
+                    ApiTraceCall *call);
 
     void beginAddingFrames(int oldCount, int numAdded);
     void endAddingFrames();
-    void callsAdded(int oldCount, int numAdded);
+    void beginLoadingFrame(ApiTraceFrame *frame, int numAdded);
+    void endLoadingFrame(ApiTraceFrame *frame);
+    void foundFrameStart(ApiTraceFrame *frame);
+    void foundFrameEnd(ApiTraceFrame *frame);
+    void foundCallIndex(ApiTraceCall *call);
+
+signals:
+    void loaderSearchNext(int startFrame,
+                          const QString &str,
+                          Qt::CaseSensitivity sensitivity);
+    void loaderSearchPrev(int startFrame,
+                          const QString &str,
+                          Qt::CaseSensitivity sensitivity);
+    void loaderFindFrameStart(ApiTraceFrame *frame);
+    void loaderFindFrameEnd(ApiTraceFrame *frame);
+    void loaderFindCallIndex(int index);
 
 private slots:
     void addFrames(const QList<ApiTraceFrame*> &frames);
     void slotSaved();
+    void finishedParsing();
+    void loaderFrameLoaded(ApiTraceFrame *frame,
+                           const QVector<ApiTraceCall*> &calls,
+                           quint64 binaryDataSize);
+    void loaderSearchResult(ApiTrace::SearchResult result,
+                            ApiTraceCall *call);
+
 private:
-    void detectFrames();
+    int callInFrame(int callIdx) const;
 private:
     QString m_fileName;
     QString m_tempFileName;
@@ -93,7 +130,8 @@ private:
 
     FrameMarker m_frameMarker;
 
-    LoaderThread *m_loader;
+    TraceLoader *m_loader;
+    QThread     *m_loaderThread;
     SaverThread  *m_saver;
 
     QSet<ApiTraceCall*> m_editedCalls;
@@ -101,8 +139,7 @@ private:
     bool m_needsSaving;
 
     QSet<ApiTraceCall*> m_errors;
-    QVector<ApiTraceCallSignature*> m_signatures;
-    QVector<ApiTraceEnumSignature*> m_enumSignatures;
+    QList< QPair<ApiTraceFrame*, ApiTraceError> > m_queuedErrors;
 };
 
 #endif
index bd30aaaa89aff9a333ea84f13dc534e2b62d4a42..67fc955957805308f71fb8b9c0ac06c37933fe4e 100644 (file)
@@ -1,6 +1,7 @@
 #include "apitracecall.h"
 
 #include "apitrace.h"
+#include "traceloader.h"
 #include "trace_model.hpp"
 
 #include <QDebug>
@@ -175,15 +176,15 @@ void VariantVisitor::visit(Trace::Enum *e)
 {
     ApiTraceEnumSignature *sig = 0;
 
-    if (m_trace) {
-        sig = m_trace->enumSignature(e->sig->id);
+    if (m_loader) {
+        sig = m_loader->enumSignature(e->sig->id);
     }
     if (!sig) {
         sig = new ApiTraceEnumSignature(
             QString::fromStdString(e->sig->name),
             QVariant(e->sig->value));
-        if (m_trace) {
-            m_trace->addEnumSignature(e->sig->id, sig);
+        if (m_loader) {
+            m_loader->addEnumSignature(e->sig->id, sig);
         }
     }
 
@@ -536,6 +537,22 @@ const QList<ApiFramebuffer> & ApiTraceState::framebuffers() const
     return m_framebuffers;
 }
 
+ApiFramebuffer ApiTraceState::colorBuffer() const
+{
+    foreach (ApiFramebuffer fbo, m_framebuffers) {
+        if (fbo.type() == QLatin1String("GL_BACK")) {
+            return fbo;
+        }
+    }
+    foreach (ApiFramebuffer fbo, m_framebuffers) {
+        if (fbo.type() == QLatin1String("GL_FRONT")) {
+            return fbo;
+        }
+    }
+    return ApiFramebuffer();
+}
+
+
 ApiTraceCallSignature::ApiTraceCallSignature(const QString &name,
                                              const QStringList &argNames)
     : m_name(name),
@@ -600,17 +617,15 @@ void ApiTraceEvent::setState(ApiTraceState *state)
     m_state = state;
 }
 
-ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame, const Trace::Call *call)
+ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame,
+                           TraceLoader *loader,
+                           const Trace::Call *call)
     : ApiTraceEvent(ApiTraceEvent::Call),
       m_parentFrame(parentFrame)
 {
-    ApiTrace *trace = parentTrace();
-
-    Q_ASSERT(trace);
-
     m_index = call->no;
 
-    m_signature = trace->signature(call->sig->id);
+    m_signature = loader->signature(call->sig->id);
 
     if (!m_signature) {
         QString name = QString::fromStdString(call->sig->name);
@@ -620,16 +635,16 @@ ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame, const Trace::Call *call)
             argNames += QString::fromStdString(call->sig->arg_names[i]);
         }
         m_signature = new ApiTraceCallSignature(name, argNames);
-        trace->addSignature(call->sig->id, m_signature);
+        loader->addSignature(call->sig->id, m_signature);
     }
     if (call->ret) {
-        VariantVisitor retVisitor(trace);
+        VariantVisitor retVisitor(loader);
         call->ret->visit(retVisitor);
         m_returnValue = retVisitor.variant();
     }
     m_argValues.reserve(call->args.size());
     for (int i = 0; i < call->args.size(); ++i) {
-        VariantVisitor argVisitor(trace);
+        VariantVisitor argVisitor(loader);
         call->args[i]->visit(argVisitor);
         m_argValues.append(argVisitor.variant());
         if (m_argValues[i].type() == QVariant::ByteArray) {
@@ -658,11 +673,8 @@ QString ApiTraceCall::error() const
 void ApiTraceCall::setError(const QString &msg)
 {
     if (m_error != msg) {
-        ApiTrace *trace = parentTrace();
         m_error = msg;
         m_richText = QString();
-        if (trace)
-            trace->callError(this);
     }
 }
 
@@ -920,35 +932,51 @@ int ApiTraceCall::numChildren() const
     return 0;
 }
 
+bool ApiTraceCall::contains(const QString &str,
+                            Qt::CaseSensitivity sensitivity) const
+{
+    QString txt = searchText();
+    return txt.contains(str, sensitivity);
+}
+
+
 ApiTraceFrame::ApiTraceFrame(ApiTrace *parentTrace)
     : ApiTraceEvent(ApiTraceEvent::Frame),
       m_parentTrace(parentTrace),
-      m_binaryDataSize(0)
+      m_binaryDataSize(0),
+      m_loaded(false),
+      m_callsToLoad(0),
+      m_lastCallIndex(0)
 {
 }
 
+ApiTraceFrame::~ApiTraceFrame()
+{
+    qDeleteAll(m_calls);
+}
+
 QStaticText ApiTraceFrame::staticText() const
 {
     if (m_staticText && !m_staticText->text().isEmpty())
         return *m_staticText;
 
-    QString richText;
+    QString richText = QObject::tr(
+                "<span style=\"font-weight:bold\">Frame %1</span>"
+                "&nbsp;&nbsp;&nbsp;"
+                "<span style=\"font-style:italic;font-size:small;font-weight:lighter;\"> "
+                "(%2 calls)</span>")
+            .arg(number)
+            .arg(m_loaded ? m_calls.count() : m_callsToLoad);
 
     //mark the frame if it uploads more than a meg a frame
     if (m_binaryDataSize > (1024*1024)) {
         richText =
             QObject::tr(
-                "<span style=\"font-weight:bold;\">"
-                "Frame&nbsp;%1</span>"
+                "%1"
                 "<span style=\"font-style:italic;\">"
                 "&nbsp;&nbsp;&nbsp;&nbsp;(%2MB)</span>")
-            .arg(number)
+            .arg(richText)
             .arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2);
-    } else {
-        richText =
-            QObject::tr(
-                "<span style=\"font-weight:bold\">Frame %1</span>")
-            .arg(number);
     }
 
     if (!m_staticText)
@@ -992,6 +1020,18 @@ ApiTraceCall * ApiTraceFrame::call(int idx) const
     return m_calls.value(idx);
 }
 
+
+ApiTraceCall * ApiTraceFrame::callWithIndex(int index) const
+{
+    QVector<ApiTraceCall*>::const_iterator itr;
+    for (itr = m_calls.constBegin(); itr != m_calls.constEnd(); ++itr) {
+        if ((*itr)->index() == index) {
+            return *itr;
+        }
+    }
+    return 0;
+}
+
 int ApiTraceFrame::callIndex(ApiTraceCall *call) const
 {
     return m_calls.indexOf(call);
@@ -999,7 +1039,11 @@ int ApiTraceFrame::callIndex(ApiTraceCall *call) const
 
 bool ApiTraceFrame::isEmpty() const
 {
-    return m_calls.isEmpty();
+    if (m_loaded) {
+        return m_calls.isEmpty();
+    } else {
+        return m_callsToLoad == 0;
+    }
 }
 
 int ApiTraceFrame::binaryDataSize() const
@@ -1012,4 +1056,90 @@ void ApiTraceFrame::setCalls(const QVector<ApiTraceCall*> &calls,
 {
     m_calls = calls;
     m_binaryDataSize = binaryDataSize;
+    m_loaded = true;
+    delete m_staticText;
+    m_staticText = 0;
+}
+
+bool ApiTraceFrame::isLoaded() const
+{
+    return m_loaded;
+}
+
+void ApiTraceFrame::setLoaded(bool l)
+{
+    m_loaded = l;
+}
+
+void ApiTraceFrame::setNumChildren(int num)
+{
+    m_callsToLoad = num;
+}
+
+void ApiTraceFrame::setParentTrace(ApiTrace *parent)
+{
+    m_parentTrace = parent;
+}
+
+int ApiTraceFrame::numChildrenToLoad() const
+{
+    return m_callsToLoad;
+}
+
+ApiTraceCall *
+ApiTraceFrame::findNextCall(ApiTraceCall *from,
+                            const QString &str,
+                            Qt::CaseSensitivity sensitivity) const
+{
+    Q_ASSERT(m_loaded);
+
+    int callIndex = 0;
+
+    if (from) {
+        callIndex = m_calls.indexOf(from) + 1;
+    }
+
+    for (int i = callIndex; i < m_calls.count(); ++i) {
+        ApiTraceCall *call = m_calls[i];
+        if (call->contains(str, sensitivity)) {
+            return call;
+        }
+    }
+    return 0;
+}
+
+ApiTraceCall *
+ApiTraceFrame::findPrevCall(ApiTraceCall *from,
+                            const QString &str,
+                            Qt::CaseSensitivity sensitivity) const
+{
+    Q_ASSERT(m_loaded);
+
+    int callIndex = m_calls.count() - 1;
+
+    if (from) {
+        callIndex = m_calls.indexOf(from) - 1;
+    }
+
+    for (int i = callIndex; i >= 0; --i) {
+        ApiTraceCall *call = m_calls[i];
+        if (call->contains(str, sensitivity)) {
+            return call;
+        }
+    }
+    return 0;
+}
+
+void ApiTraceFrame::setLastCallIndex(unsigned index)
+{
+    m_lastCallIndex = index;
+}
+
+unsigned ApiTraceFrame::lastCallIndex() const
+{
+    if (m_loaded && !m_calls.isEmpty()) {
+        return m_calls.last()->index();
+    } else {
+        return m_lastCallIndex;
+    }
 }
index 33648f2f28b054803d15e8f38b0ab1cd2b05e011..12b0216a6e6ee4763da8d915d4cabbbb12b42858 100644 (file)
 
 
 class ApiTrace;
+class TraceLoader;
 
 class VariantVisitor : public Trace::Visitor
 {
 public:
-    VariantVisitor(ApiTrace *trace)
-        : m_trace(trace)
+    VariantVisitor(TraceLoader *loader)
+        : m_loader(loader)
     {}
     virtual void visit(Trace::Null *);
     virtual void visit(Trace::Bool *node);
@@ -37,10 +38,18 @@ public:
         return m_variant;
     }
 private:
-    ApiTrace *m_trace;
+    TraceLoader *m_loader;
     QVariant m_variant;
 };
 
+
+struct ApiTraceError
+{
+    int callIndex;
+    QString type;
+    QString message;
+};
+
 class ApiTraceEnumSignature
 {
 public:
@@ -160,6 +169,7 @@ public:
     const QList<ApiTexture> & textures() const;
     const QList<ApiFramebuffer> & framebuffers() const;
 
+    ApiFramebuffer colorBuffer() const;
 private:
     QVariantMap m_parameters;
     QMap<QString, QString> m_shaderSources;
@@ -233,7 +243,8 @@ Q_DECLARE_METATYPE(ApiTraceEvent*);
 class ApiTraceCall : public ApiTraceEvent
 {
 public:
-    ApiTraceCall(ApiTraceFrame *parentFrame, const Trace::Call *tcall);
+    ApiTraceCall(ApiTraceFrame *parentFrame, TraceLoader *loader,
+                 const Trace::Call *tcall);
     ~ApiTraceCall();
 
     int index() const;
@@ -257,6 +268,9 @@ public:
     QVector<QVariant> editedValues() const;
     void revert();
 
+    bool contains(const QString &str,
+                  Qt::CaseSensitivity sensitivity) const;
+
     ApiTrace *parentTrace() const;
 
     QString toHtml() const;
@@ -284,28 +298,50 @@ Q_DECLARE_METATYPE(ApiTraceCall*);
 class ApiTraceFrame : public ApiTraceEvent
 {
 public:
-    ApiTraceFrame(ApiTrace *parent);
+    ApiTraceFrame(ApiTrace *parent=0);
+    ~ApiTraceFrame();
     int number;
 
     bool isEmpty() const;
 
+    void setParentTrace(ApiTrace *parent);
     ApiTrace *parentTrace() const;
 
+    void setNumChildren(int num);
     int numChildren() const;
+    int numChildrenToLoad() const;
     QStaticText staticText() const;
 
     int callIndex(ApiTraceCall *call) const;
     ApiTraceCall *call(int idx) const;
+    ApiTraceCall *callWithIndex(int index) const;
     void addCall(ApiTraceCall *call);
     QVector<ApiTraceCall*> calls() const;
     void setCalls(const QVector<ApiTraceCall*> &calls,
                   quint64 binaryDataSize);
 
+    ApiTraceCall *findNextCall(ApiTraceCall *from,
+                               const QString &str,
+                               Qt::CaseSensitivity sensitivity) const;
+
+    ApiTraceCall *findPrevCall(ApiTraceCall *from,
+                               const QString &str,
+                               Qt::CaseSensitivity sensitivity) const;
+
     int binaryDataSize() const;
+
+    bool isLoaded() const;
+    void setLoaded(bool l);
+
+    void setLastCallIndex(unsigned index);
+    unsigned lastCallIndex() const;
 private:
     ApiTrace *m_parentTrace;
     quint64 m_binaryDataSize;
     QVector<ApiTraceCall*> m_calls;
+    bool m_loaded;
+    unsigned m_callsToLoad;
+    unsigned m_lastCallIndex;
 };
 Q_DECLARE_METATYPE(ApiTraceFrame*);
 
index 2bf7bf8d007ec8b2131c4446d555b822044b7d26..cfa930d74f7c8752974027350e8867be3ae2d4ac 100644 (file)
@@ -89,12 +89,6 @@ void ApiTraceFilter::setFilterOptions(ApiTraceFilter::FilterOptions opts)
     }
 }
 
-QModelIndex ApiTraceFilter::callIndex(int callIdx) const
-{
-    ApiTraceModel *model = static_cast<ApiTraceModel *>(sourceModel());
-    QModelIndex index = model->callIndex(callIdx);
-    return mapFromSource(index);
-}
 
 QModelIndex ApiTraceFilter::indexForCall(ApiTraceCall *call) const
 {
index 217938c82a0fa2775175317afff428c58bca6a49..30c92a1287ff55794892068686d745c7d0155fcc 100644 (file)
@@ -27,7 +27,6 @@ public:
     void setFilterRegexp(const QRegExp &regexp);
     QRegExp filterRegexp() const;
 
-    QModelIndex callIndex(int callNum) const;
     QModelIndex indexForCall(ApiTraceCall *call) const;
 protected:
     bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
index 5860cf9530e4d66310d6497db52454b7f9f085fc..7303ae1c31e836bb2e507fe9b5fd34af657fa574 100644 (file)
@@ -1,9 +1,10 @@
 #include "apitracemodel.h"
 
 #include "apitracecall.h"
-#include "loaderthread.h"
+#include "traceloader.h"
 #include "trace_parser.hpp"
 
+#include <QBuffer>
 #include <QDebug>
 #include <QImage>
 #include <QVariant>
@@ -52,22 +53,53 @@ QVariant ApiTraceModel::data(const QModelIndex &index, int role) const
                     .arg(call->name())
                     .arg(stateText);
         } else {
+            const char *htmlTempl =
+                    "<div>\n"
+                    "<div>\n"
+                    "%1"
+                    "<span style=\"font-weight:bold; font-size:large; vertical-align:center; padding-bottom: 30px \">\n"
+                    "Frame %2</span>\n"
+                    "</div>\n"
+                    "<div >%3 calls%4</div>\n"
+                    "</div>\n";
+
+
             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(itm);
-            QString text = QObject::tr("%1)&nbsp;Frame&nbsp;")
-                           .arg(frame->number);
-            int binaryDataSize = frame->binaryDataSize() / 1024;
-            if (!frame->hasState())
-                return QObject::tr(
-                    "<b>%1&nbsp;</b>(binary&nbsp;data&nbsp;size&nbsp;=&nbsp;%2kB)")
-                    .arg(text)
-                    .arg(binaryDataSize);
-            else
-                return QObject::tr(
-                    "<b>%1&nbsp;(binary&nbsp;data&nbsp;size&nbsp;=&nbsp;%2kB)</b>"
-                    "<br/>%3")
-                    .arg(text)
-                    .arg(binaryDataSize)
-                    .arg(stateText);
+            QString thumbStr, sizeStr;
+
+            if (frame->hasState()) {
+                static const char *imgTempl =
+                        "<img style=\"float:left;\" "
+                        "src=\"data:image/png;base64,%1\"/>\n";
+                static const char *sizeTempl =
+                        ", %1kb";
+
+                ApiFramebuffer fbo = frame->state()->colorBuffer();
+                QImage thumb = fbo.thumb();
+                if (!thumb.isNull()) {
+                    QByteArray ba;
+                    QBuffer buffer(&ba);
+                    buffer.open(QIODevice::WriteOnly);
+                    thumb.save(&buffer, "PNG");
+                    thumbStr = tr(imgTempl).arg(
+                                QString(buffer.data().toBase64()));
+                }
+
+                int binaryDataSize = frame->binaryDataSize() / 1024;
+                if (binaryDataSize > 0) {
+                    sizeStr = tr(sizeTempl).arg(binaryDataSize);
+                }
+            }
+
+            int numCalls = frame->isLoaded()
+                    ? frame->numChildren()
+                    : frame->numChildrenToLoad();
+
+            return tr(htmlTempl)
+                    .arg(thumbStr)
+                    .arg(frame->number)
+                    .arg(numCalls)
+                    .arg(sizeStr);
         }
     }
     case ApiTraceModel::EventRole:
@@ -218,6 +250,11 @@ void ApiTraceModel::setApiTrace(ApiTrace *trace)
             this, SLOT(endAddingFrames()));
     connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
             this, SLOT(callChanged(ApiTraceCall*)));
+    connect(m_trace, SIGNAL(beginLoadingFrame(ApiTraceFrame*,int)),
+            this, SLOT(beginLoadingFrame(ApiTraceFrame*,int)));
+    connect(m_trace, SIGNAL(endLoadingFrame(ApiTraceFrame*)),
+            this, SLOT(endLoadingFrame(ApiTraceFrame*)));
+
 }
 
 const ApiTrace * ApiTraceModel::apiTrace() const
@@ -264,12 +301,6 @@ void ApiTraceModel::stateSetOnEvent(ApiTraceEvent *event)
     }
 }
 
-QModelIndex ApiTraceModel::callIndex(int callNum) const
-{
-    ApiTraceCall *call = m_trace->callWithIndex(callNum);
-    return indexForCall(call);
-}
-
 QModelIndex ApiTraceModel::indexForCall(ApiTraceCall *call) const
 {
     if (!call) {
@@ -313,4 +344,58 @@ void ApiTraceModel::endAddingFrames()
     endInsertRows();
 }
 
+bool ApiTraceModel::canFetchMore(const QModelIndex &parent) const
+{
+    if (parent.isValid()) {
+        ApiTraceEvent *event = item(parent);
+        if (event && event->type() == ApiTraceEvent::Frame) {
+            ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+            return !frame->isLoaded() && !m_loadingFrames.contains(frame);
+        } else
+            return false;
+    } else {
+        return false;
+    }
+}
+
+void ApiTraceModel::fetchMore(const QModelIndex &parent)
+{
+    if (parent.isValid()) {
+        ApiTraceEvent *event = item(parent);
+        if (event && event->type() == ApiTraceEvent::Frame) {
+            ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+            QModelIndex index = createIndex(frame->number, 0, frame);
+
+            Q_ASSERT(!frame->isLoaded());
+            m_loadingFrames.insert(frame);
+
+            m_trace->loadFrame(frame);
+        }
+    }
+}
+
+void ApiTraceModel::beginLoadingFrame(ApiTraceFrame *frame, int numAdded)
+{
+    QModelIndex index = createIndex(frame->number, 0, frame);
+    beginInsertRows(index, 0, numAdded - 1);
+}
+
+void ApiTraceModel::endLoadingFrame(ApiTraceFrame *frame)
+{
+    QModelIndex index = createIndex(frame->number, 0, frame);
+#if 0
+    qDebug()<<"Frame loaded = "<<frame->loaded();
+    qDebug()<<"\tframe idx = "<<frame->number;
+    qDebug()<<"\tis empty = "<<frame->isEmpty();
+    qDebug()<<"\tnum children = "<<frame->numChildren();
+    qDebug()<<"\tindex is "<<index;
+#endif
+
+    endInsertRows();
+
+    emit dataChanged(index, index);
+
+    m_loadingFrames.remove(frame);
+}
+
 #include "apitracemodel.moc"
index 2cdd7d0bfe6fd9e1cfcbd83f25bfe4a018aab262..fe6b5cea69deb5f0702c8af239066b1db5b806bb 100644 (file)
@@ -4,11 +4,13 @@
 
 #include <QAbstractItemModel>
 #include <QModelIndex>
+#include <QSet>
 #include <QVariant>
 
 class ApiTrace;
 class ApiTraceCall;
 class ApiTraceEvent;
+class ApiTraceFrame;
 
 class ApiTraceModel : public QAbstractItemModel
 {
@@ -25,7 +27,6 @@ public:
     const ApiTrace *apiTrace() const;
     void stateSetOnEvent(ApiTraceEvent *event);
 
-    QModelIndex callIndex(int callNum) const;
     QModelIndex indexForCall(ApiTraceCall *call) const;
 
 public:
@@ -45,6 +46,8 @@ public:
                     const QModelIndex &parent = QModelIndex());
     bool removeRows(int position, int rows,
                     const QModelIndex &parent = QModelIndex());
+    virtual bool canFetchMore(const QModelIndex & parent) const;
+    virtual void fetchMore(const QModelIndex &parent);
     /* } QAbstractItemModel; */
 
 private slots:
@@ -52,12 +55,15 @@ private slots:
     void beginAddingFrames(int oldCount, int numAdded);
     void endAddingFrames();
     void callChanged(ApiTraceCall *call);
+    void beginLoadingFrame(ApiTraceFrame *frame, int numAdded);
+    void endLoadingFrame(ApiTraceFrame *frame);
 
 private:
     ApiTraceEvent *item(const QModelIndex &index) const;
 
 private:
     ApiTrace *m_trace;
+    QSet<ApiTraceFrame*> m_loadingFrames;
 };
 
 #endif
index 3f8cfa6b6bcb7b884e4c47ba27da0018dc9fb1b0..6043b750ded5a2b17d513f2e0e67e36e38b24843 100644 (file)
@@ -1,5 +1,6 @@
 #include "mainwindow.h"
 
+#include "apitrace.h"
 #include "apitracecall.h"
 
 #include <QApplication>
@@ -7,13 +8,20 @@
 #include <QVariant>
 
 Q_DECLARE_METATYPE(QList<ApiTraceFrame*>);
+Q_DECLARE_METATYPE(QVector<ApiTraceCall*>);
+Q_DECLARE_METATYPE(Qt::CaseSensitivity);
+Q_DECLARE_METATYPE(ApiTrace::SearchResult);
+
 
 int main(int argc, char **argv)
 {
     QApplication app(argc, argv);
 
     qRegisterMetaType<QList<ApiTraceFrame*> >();
+    qRegisterMetaType<QVector<ApiTraceCall*> >();
     qRegisterMetaType<ApiTraceState>();
+    qRegisterMetaType<Qt::CaseSensitivity>();
+    qRegisterMetaType<ApiTrace::SearchResult>();
     MainWindow window;
 
     window.show();
index 591c557e55567eda81a93c1341eadf7cc727aa7a..8d7af8a0a1f4a5c24041f7a511e0c1930f1c06b6 100644 (file)
@@ -47,8 +47,6 @@ MainWindow::MainWindow()
 
 void MainWindow::createTrace()
 {
-    TraceDialog dialog;
-
     if (!m_traceProcess->canTrace()) {
         QMessageBox::warning(
             this,
@@ -57,6 +55,7 @@ void MainWindow::createTrace()
         return;
     }
 
+    TraceDialog dialog;
     if (dialog.exec() == QDialog::Accepted) {
         qDebug()<< "App : " <<dialog.applicationPath();
         qDebug()<< "  Arguments: "<<dialog.arguments();
@@ -69,11 +68,11 @@ void MainWindow::createTrace()
 void MainWindow::openTrace()
 {
     QString fileName =
-        QFileDialog::getOpenFileName(
-            this,
-            tr("Open Trace"),
-            QDir::homePath(),
-            tr("Trace Files (*.trace)"));
+            QFileDialog::getOpenFileName(
+                this,
+                tr("Open Trace"),
+                QDir::homePath(),
+                tr("Trace Files (*.trace)"));
 
     if (!fileName.isEmpty() && QFile::exists(fileName)) {
         newTraceFile(fileName);
@@ -104,8 +103,8 @@ void MainWindow::callItemSelected(const QModelIndex &index)
             QByteArray data =
                 call->arguments()[call->binaryDataIndex()].toByteArray();
             m_vdataInterpreter->setData(data);
-            QVector<QVariant> args = call->arguments();
 
+            QVector<QVariant> args = call->arguments();
             for (int i = 0; i < call->argNames().count(); ++i) {
                 QString name = call->argNames()[i];
                 if (name == QLatin1String("stride")) {
@@ -117,8 +116,9 @@ void MainWindow::callItemSelected(const QModelIndex &index)
                 } else if (name == QLatin1String("type")) {
                     QString val = args[i].toString();
                     int textIndex = m_ui.vertexTypeCB->findText(val);
-                    if (textIndex >= 0)
+                    if (textIndex >= 0) {
                         m_ui.vertexTypeCB->setCurrentIndex(textIndex);
+                    }
                 }
             }
         }
@@ -127,15 +127,17 @@ void MainWindow::callItemSelected(const QModelIndex &index)
     } else {
         if (event && event->type() == ApiTraceEvent::Frame) {
             m_selectedEvent = static_cast<ApiTraceFrame*>(event);
-        } else
+        } else {
             m_selectedEvent = 0;
+        }
         m_ui.detailsDock->hide();
         m_ui.vertexDataDock->hide();
     }
     if (m_selectedEvent && m_selectedEvent->hasState()) {
         fillStateForFrame();
-    } else
+    } else {
         m_ui.stateDock->hide();
+    }
 }
 
 void MainWindow::replayStart()
@@ -207,8 +209,9 @@ void MainWindow::replayFinished(const QString &output)
     m_stateEvent = 0;
     m_ui.actionShowErrorsDock->setEnabled(m_trace->hasErrors());
     m_ui.errorsDock->setVisible(m_trace->hasErrors());
-    if (!m_trace->hasErrors())
+    if (!m_trace->hasErrors()) {
         m_ui.errorsTreeWidget->clear();
+    }
 
     statusBar()->showMessage(
         tr("Replaying finished!"), 2000);
@@ -251,8 +254,9 @@ void MainWindow::finishedLoadingTrace()
 
 void MainWindow::replayTrace(bool dumpState)
 {
-    if (m_trace->fileName().isEmpty())
+    if (m_trace->fileName().isEmpty()) {
         return;
+    }
 
     m_retracer->setFileName(m_trace->fileName());
     m_retracer->setCaptureState(dumpState);
@@ -268,7 +272,7 @@ void MainWindow::replayTrace(bool dumpState)
                 qDebug()<<"tried to get a state for an empty frame";
                 return;
             }
-            index = frame->calls().first()->index();
+            index = frame->lastCallIndex();
         } else {
             qDebug()<<"Unknown event type";
             return;
@@ -279,12 +283,13 @@ void MainWindow::replayTrace(bool dumpState)
 
     m_ui.actionStop->setEnabled(true);
     m_progressBar->show();
-    if (dumpState)
+    if (dumpState) {
         statusBar()->showMessage(
             tr("Looking up the state..."));
-    else
+    } else {
         statusBar()->showMessage(
             tr("Replaying the trace file..."));
+    }
 }
 
 void MainWindow::lookupState()
@@ -339,10 +344,12 @@ variantToString(const QVariant &var, QString &str)
 }
 
 static QTreeWidgetItem *
-variantToItem(const QString &key, const QVariant &var, const QVariant &defaultVar);
+variantToItem(const QString &key, const QVariant &var,
+              const QVariant &defaultVar);
 
 static void
-variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap, QList<QTreeWidgetItem *> &items)
+variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap,
+                  QList<QTreeWidgetItem *> &items)
 {
     QVariantMap::const_iterator itr;
     for (itr = map.constBegin(); itr != map.constEnd(); ++itr) {
@@ -358,7 +365,8 @@ variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap, QList<Q
 }
 
 static void
-variantListToItems(const QVector<QVariant> &lst, const QVector<QVariant> &defaultLst,
+variantListToItems(const QVector<QVariant> &lst,
+                   const QVector<QVariant> &defaultLst,
                    QList<QTreeWidgetItem *> &items)
 {
     for (int i = 0; i < lst.count(); ++i) {
@@ -398,7 +406,8 @@ isVariantDeep(const QVariant &var)
 }
 
 static QTreeWidgetItem *
-variantToItem(const QString &key, const QVariant &var, const QVariant &defaultVar)
+variantToItem(const QString &key, const QVariant &var,
+              const QVariant &defaultVar)
 {
     if (var == defaultVar) {
         return NULL;
@@ -442,13 +451,12 @@ static void addSurfaceItem(const ApiSurface &surface,
                            QTreeWidgetItem *parent,
                            QTreeWidget *tree)
 {
-    int width = surface.size().width();
-    int height = surface.size().height();
     QIcon icon(QPixmap::fromImage(surface.thumb()));
     QTreeWidgetItem *item = new QTreeWidgetItem(parent);
-
     item->setIcon(0, icon);
 
+    int width = surface.size().width();
+    int height = surface.size().height();
     QString descr =
         QString::fromLatin1("%1, %2 x %3")
         .arg(label)
@@ -465,8 +473,9 @@ static void addSurfaceItem(const ApiSurface &surface,
 
 void MainWindow::fillStateForFrame()
 {
-    if (!m_selectedEvent || !m_selectedEvent->hasState())
+    if (!m_selectedEvent || !m_selectedEvent->hasState()) {
         return;
+    }
 
     if (m_nonDefaultsLookupEvent) {
         m_ui.nonDefaultsCB->blockSignals(true);
@@ -513,8 +522,9 @@ void MainWindow::fillStateForFrame()
             QTreeWidgetItem *textureItem =
                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
             textureItem->setText(0, tr("Textures"));
-            if (textures.count() <= 6)
+            if (textures.count() <= 6) {
                 textureItem->setExpanded(true);
+            }
 
             for (int i = 0; i < textures.count(); ++i) {
                 const ApiTexture &texture =
@@ -528,8 +538,9 @@ void MainWindow::fillStateForFrame()
             QTreeWidgetItem *fboItem =
                 new QTreeWidgetItem(m_ui.surfacesTreeWidget);
             fboItem->setText(0, tr("Framebuffers"));
-            if (fbos.count() <= 6)
+            if (fbos.count() <= 6) {
                 fboItem->setExpanded(true);
+            }
 
             for (int i = 0; i < fbos.count(); ++i) {
                 const ApiFramebuffer &fbo =
@@ -561,8 +572,9 @@ void MainWindow::showSurfacesMenu(const QPoint &pos)
 {
     QTreeWidget *tree = m_ui.surfacesTreeWidget;
     QTreeWidgetItem *item = tree->itemAt(pos);
-    if (!item)
+    if (!item) {
         return;
+    }
 
     QMenu menu(tr("Surfaces"), this);
 
@@ -584,24 +596,28 @@ void MainWindow::showSelectedSurface()
     QTreeWidgetItem *item =
         m_ui.surfacesTreeWidget->currentItem();
 
-    if (!item)
+    if (!item) {
         return;
+    }
 
-    QVariant var = item->data(0, Qt::UserRole);
-    QImage img = var.value<QImage>();
     ImageViewer *viewer = new ImageViewer(this);
 
     QString title;
-    if (currentCall()) {
+    if (selectedCall()) {
         title = tr("QApiTrace - Surface at %1 (%2)")
-                .arg(currentCall()->name())
-                .arg(currentCall()->index());
+                .arg(selectedCall()->name())
+                .arg(selectedCall()->index());
     } else {
         title = tr("QApiTrace - Surface Viewer");
     }
     viewer->setWindowTitle(title);
+
     viewer->setAttribute(Qt::WA_DeleteOnClose, true);
+
+    QVariant var = item->data(0, Qt::UserRole);
+    QImage img = var.value<QImage>();
     viewer->setImage(img);
+
     QRect screenRect = QApplication::desktop()->availableGeometry();
     viewer->resize(qMin(int(0.75 * screenRect.width()), img.width()) + 40,
                    qMin(int(0.75 * screenRect.height()), img.height()) + 40);
@@ -647,7 +663,7 @@ void MainWindow::initObjects()
     m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu);
 
     m_progressBar = new QProgressBar();
-    m_progressBar->setRange(0, 0);
+    m_progressBar->setRange(0, 100);
     statusBar()->addPermanentWidget(m_progressBar);
     m_progressBar->hide();
 
@@ -682,6 +698,8 @@ void MainWindow::initConnections()
 {
     connect(m_trace, SIGNAL(startedLoadingTrace()),
             this, SLOT(startedLoadingTrace()));
+    connect(m_trace, SIGNAL(loaded(int)),
+            this, SLOT(loadProgess(int)));
     connect(m_trace, SIGNAL(finishedLoadingTrace()),
             this, SLOT(finishedLoadingTrace()));
     connect(m_trace, SIGNAL(startedSaving()),
@@ -690,6 +708,14 @@ void MainWindow::initConnections()
             this, SLOT(slotSaved()));
     connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
             this, SLOT(slotTraceChanged(ApiTraceCall*)));
+    connect(m_trace, SIGNAL(findResult(ApiTrace::SearchResult,ApiTraceCall*)),
+            this, SLOT(slotSearchResult(ApiTrace::SearchResult,ApiTraceCall*)));
+    connect(m_trace, SIGNAL(foundFrameStart(ApiTraceFrame*)),
+            this, SLOT(slotFoundFrameStart(ApiTraceFrame*)));
+    connect(m_trace, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
+            this, SLOT(slotFoundFrameEnd(ApiTraceFrame*)));
+    connect(m_trace, SIGNAL(foundCallIndex(ApiTraceCall*)),
+            this, SLOT(slotJumpToResult(ApiTraceCall*)));
 
     connect(m_retracer, SIGNAL(finished(const QString&)),
             this, SLOT(replayFinished(const QString&)));
@@ -697,8 +723,8 @@ void MainWindow::initConnections()
             this, SLOT(replayError(const QString&)));
     connect(m_retracer, SIGNAL(foundState(ApiTraceState*)),
             this, SLOT(replayStateFound(ApiTraceState*)));
-    connect(m_retracer, SIGNAL(retraceErrors(const QList<RetraceError>&)),
-            this, SLOT(slotRetraceErrors(const QList<RetraceError>&)));
+    connect(m_retracer, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
+            this, SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
 
     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
             m_vdataInterpreter, SLOT(interpretData()));
@@ -800,10 +826,7 @@ void MainWindow::slotGoTo()
 
 void MainWindow::slotJumpTo(int callNum)
 {
-    QModelIndex index = m_proxyModel->callIndex(callNum);
-    if (index.isValid()) {
-        m_ui.callView->setCurrentIndex(index);
-    }
+    m_trace->findCallIndex(callNum);
 }
 
 void MainWindow::createdTrace(const QString &path)
@@ -829,103 +852,31 @@ void MainWindow::slotSearch()
 void MainWindow::slotSearchNext(const QString &str,
                                 Qt::CaseSensitivity sensitivity)
 {
-    QModelIndex index = m_ui.callView->currentIndex();
-    ApiTraceEvent *event = 0;
-
-
-    if (!index.isValid()) {
-        index = m_proxyModel->index(0, 0, QModelIndex());
-        if (!index.isValid()) {
-            qDebug()<<"no currently valid index";
-            m_searchWidget->setFound(false);
-            return;
-        }
-    }
-
-    event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
-    ApiTraceCall *call = 0;
+    ApiTraceCall *call = currentCall();
+    ApiTraceFrame *frame = currentFrame();
 
-    if (event->type() == ApiTraceCall::Call)
-        call = static_cast<ApiTraceCall*>(event);
-    else {
-        Q_ASSERT(event->type() == ApiTraceCall::Frame);
-        ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
-        call = frame->call(0);
+    Q_ASSERT(call || frame);
+    if (!frame) {
+        frame = call->parentFrame();
     }
+    Q_ASSERT(frame);
 
-    if (!call) {
-        m_searchWidget->setFound(false);
-        return;
-    }
-    const QVector<ApiTraceCall*> &calls = m_trace->calls();
-    int callNum = calls.indexOf(call);
-
-    for (int i = callNum + 1; i < calls.count(); ++i) {
-        ApiTraceCall *testCall = calls[i];
-        QModelIndex index = m_proxyModel->indexForCall(testCall);
-        /* if it's not valid it means that the proxy model has already
-         * filtered it out */
-        if (index.isValid()) {
-            QString txt = testCall->searchText();
-            if (txt.contains(str, sensitivity)) {
-                m_ui.callView->setCurrentIndex(index);
-                m_searchWidget->setFound(true);
-                return;
-            }
-        }
-    }
-    m_searchWidget->setFound(false);
+    m_trace->findNext(frame, call, str, sensitivity);
 }
 
 void MainWindow::slotSearchPrev(const QString &str,
                                 Qt::CaseSensitivity sensitivity)
 {
-    QModelIndex index = m_ui.callView->currentIndex();
-    ApiTraceEvent *event = 0;
-
-
-    if (!index.isValid()) {
-        index = m_proxyModel->index(0, 0, QModelIndex());
-        if (!index.isValid()) {
-            qDebug()<<"no currently valid index";
-            m_searchWidget->setFound(false);
-            return;
-        }
-    }
-
-    event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
-    ApiTraceCall *call = 0;
+    ApiTraceCall *call = currentCall();
+    ApiTraceFrame *frame = currentFrame();
 
-    if (event->type() == ApiTraceCall::Call)
-        call = static_cast<ApiTraceCall*>(event);
-    else {
-        Q_ASSERT(event->type() == ApiTraceCall::Frame);
-        ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
-        call = frame->call(0);
+    Q_ASSERT(call || frame);
+    if (!frame) {
+        frame = call->parentFrame();
     }
+    Q_ASSERT(frame);
 
-    if (!call) {
-        m_searchWidget->setFound(false);
-        return;
-    }
-    const QVector<ApiTraceCall*> &calls = m_trace->calls();
-    int callNum = calls.indexOf(call);
-
-    for (int i = callNum - 1; i >= 0; --i) {
-        ApiTraceCall *testCall = calls[i];
-        QModelIndex index = m_proxyModel->indexForCall(testCall);
-        /* if it's not valid it means that the proxy model has already
-         * filtered it out */
-        if (index.isValid()) {
-            QString txt = testCall->searchText();
-            if (txt.contains(str, sensitivity)) {
-                m_ui.callView->setCurrentIndex(index);
-                m_searchWidget->setFound(true);
-                return;
-            }
-        }
-    }
-    m_searchWidget->setFound(false);
+    m_trace->findPrev(frame, call, str, sensitivity);
 }
 
 void MainWindow::fillState(bool nonDefaults)
@@ -938,11 +889,17 @@ void MainWindow::fillState(bool nonDefaults)
             m_ui.nonDefaultsCB->blockSignals(false);
             ApiTraceFrame *firstFrame =
                 m_trace->frameAt(0);
-            ApiTraceEvent *oldSelected = m_selectedEvent;
-            if (!firstFrame)
+            if (!firstFrame) {
                 return;
+            }
+            if (!firstFrame->isLoaded()) {
+                m_trace->loadFrame(firstFrame);
+                return;
+            }
+            ApiTraceCall *firstCall = firstFrame->calls().first();
+            ApiTraceEvent *oldSelected = m_selectedEvent;
             m_nonDefaultsLookupEvent = m_selectedEvent;
-            m_selectedEvent = firstFrame;
+            m_selectedEvent = firstCall;
             lookupState();
             m_selectedEvent = oldSelected;
         }
@@ -952,18 +909,20 @@ void MainWindow::fillState(bool nonDefaults)
 
 void MainWindow::customContextMenuRequested(QPoint pos)
 {
-    QMenu menu;
     QModelIndex index = m_ui.callView->indexAt(pos);
 
     callItemSelected(index);
-    if (!index.isValid())
+    if (!index.isValid()) {
         return;
+    }
 
     ApiTraceEvent *event =
         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
-    if (!event)
+    if (!event) {
         return;
+    }
 
+    QMenu menu;
     menu.addAction(QIcon(":/resources/media-record.png"),
                    tr("Lookup state"), this, SLOT(lookupState()));
     if (event->type() == ApiTraceEvent::Call) {
@@ -999,47 +958,28 @@ void MainWindow::slotSaved()
 void MainWindow::slotGoFrameStart()
 {
     ApiTraceFrame *frame = currentFrame();
-    if (!frame || frame->isEmpty()) {
-        return;
-    }
-
-    QVector<ApiTraceCall*>::const_iterator itr;
-    QVector<ApiTraceCall*> calls = frame->calls();
+    ApiTraceCall *call = currentCall();
 
-    itr = calls.constBegin();
-    while (itr != calls.constEnd()) {
-        ApiTraceCall *call = *itr;
-        QModelIndex idx = m_proxyModel->indexForCall(call);
-        if (idx.isValid()) {
-            m_ui.callView->setCurrentIndex(idx);
-            break;
-        }
-        ++itr;
+    if (!frame && call) {
+        frame = call->parentFrame();
     }
+
+    m_trace->findFrameStart(frame);
 }
 
 void MainWindow::slotGoFrameEnd()
 {
     ApiTraceFrame *frame = currentFrame();
-    if (!frame || frame->isEmpty()) {
-        return;
+    ApiTraceCall *call = currentCall();
+
+    if (!frame && call) {
+        frame = call->parentFrame();
     }
-    QVector<ApiTraceCall*>::const_iterator itr;
-    QVector<ApiTraceCall*> calls = frame->calls();
 
-    itr = calls.constEnd();
-    do {
-        --itr;
-        ApiTraceCall *call = *itr;
-        QModelIndex idx = m_proxyModel->indexForCall(call);
-        if (idx.isValid()) {
-            m_ui.callView->setCurrentIndex(idx);
-            break;
-        }
-    } while (itr != calls.constBegin());
+    m_trace->findFrameEnd(frame);
 }
 
-ApiTraceFrame * MainWindow::currentFrame() const
+ApiTraceFrame * MainWindow::selectedFrame() const
 {
     if (m_selectedEvent) {
         if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
@@ -1061,20 +1001,17 @@ void MainWindow::slotTraceChanged(ApiTraceCall *call)
     }
 }
 
-void MainWindow::slotRetraceErrors(const QList<RetraceError> &errors)
+void MainWindow::slotRetraceErrors(const QList<ApiTraceError> &errors)
 {
     m_ui.errorsTreeWidget->clear();
 
-    foreach(RetraceError error, errors) {
-        ApiTraceCall *call = m_trace->callWithIndex(error.callIndex);
-        if (!call)
-            continue;
-        call->setError(error.message);
+    foreach(ApiTraceError error, errors) {
+        m_trace->setCallError(error);
 
         QTreeWidgetItem *item =
             new QTreeWidgetItem(m_ui.errorsTreeWidget);
         item->setData(0, Qt::DisplayRole, error.callIndex);
-        item->setData(0, Qt::UserRole, QVariant::fromValue(call));
+        item->setData(0, Qt::UserRole, error.callIndex);
         QString type = error.type;
         type[0] = type[0].toUpper();
         item->setData(1, Qt::DisplayRole, type);
@@ -1085,19 +1022,13 @@ void MainWindow::slotRetraceErrors(const QList<RetraceError> &errors)
 void MainWindow::slotErrorSelected(QTreeWidgetItem *current)
 {
     if (current) {
-        ApiTraceCall *call =
-            current->data(0, Qt::UserRole).value<ApiTraceCall*>();
-        Q_ASSERT(call);
-        QModelIndex index = m_proxyModel->indexForCall(call);
-        if (index.isValid()) {
-            m_ui.callView->setCurrentIndex(index);
-        } else {
-            statusBar()->showMessage(tr("Call has been filtered out."));
-        }
+        int callIndex =
+            current->data(0, Qt::UserRole).toInt();
+        m_trace->findCallIndex(callIndex);
     }
 }
 
-ApiTraceCall * MainWindow::currentCall() const
+ApiTraceCall * MainWindow::selectedCall() const
 {
     if (m_selectedEvent &&
         m_selectedEvent->type() == ApiTraceEvent::Call) {
@@ -1111,18 +1042,19 @@ void MainWindow::saveSelectedSurface()
     QTreeWidgetItem *item =
         m_ui.surfacesTreeWidget->currentItem();
 
-    if (!item || !m_trace)
+    if (!item || !m_trace) {
         return;
+    }
 
     QVariant var = item->data(0, Qt::UserRole);
     QImage img = var.value<QImage>();
 
     QString imageIndex;
-    if (currentCall()) {
+    if (selectedCall()) {
         imageIndex = tr("_call_%1")
-                     .arg(currentCall()->index());
-    } else if (currentFrame()) {
-        ApiTraceCall *firstCall = currentFrame()->call(0);
+                     .arg(selectedCall()->index());
+    } else if (selectedFrame()) {
+        ApiTraceCall *firstCall = selectedFrame()->call(0);
         if (firstCall) {
             imageIndex = tr("_frame_%1")
                          .arg(firstCall->index());
@@ -1159,4 +1091,135 @@ void MainWindow::saveSelectedSurface()
     statusBar()->showMessage( tr("Saved '%1'").arg(fileName), 5000);
 }
 
+void MainWindow::loadProgess(int percent)
+{
+    m_progressBar->setValue(percent);
+}
+
+void MainWindow::slotSearchResult(ApiTrace::SearchResult result,
+                                  ApiTraceCall *call)
+{
+    switch (result) {
+    case ApiTrace::SearchResult_NotFound:
+        m_searchWidget->setFound(false);
+        break;
+    case ApiTrace::SearchResult_Found: {
+        QModelIndex index = m_proxyModel->indexForCall(call);
+        m_ui.callView->setCurrentIndex(index);
+        m_searchWidget->setFound(true);
+    }
+        break;
+    case ApiTrace::SearchResult_Wrapped:
+        m_searchWidget->setFound(false);
+        break;
+    }
+}
+
+ApiTraceFrame * MainWindow::currentFrame() const
+{
+    QModelIndex index = m_ui.callView->currentIndex();
+    ApiTraceEvent *event = 0;
+
+    if (!index.isValid()) {
+        index = m_proxyModel->index(0, 0, QModelIndex());
+        if (!index.isValid()) {
+            qDebug()<<"no currently valid index";
+            return 0;
+        }
+    }
+
+    event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+    Q_ASSERT(event);
+    if (!event) {
+        return 0;
+    }
+
+    ApiTraceFrame *frame = 0;
+    if (event->type() == ApiTraceCall::Frame) {
+        frame = static_cast<ApiTraceFrame*>(event);
+    }
+    return frame;
+}
+
+ApiTraceCall * MainWindow::currentCall() const
+{
+    QModelIndex index = m_ui.callView->currentIndex();
+    ApiTraceEvent *event = 0;
+
+    if (!index.isValid()) {
+        index = m_proxyModel->index(0, 0, QModelIndex());
+        if (!index.isValid()) {
+            qDebug()<<"no currently valid index";
+            return 0;
+        }
+    }
+
+    event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+    Q_ASSERT(event);
+    if (!event) {
+        return 0;
+    }
+
+    ApiTraceCall *call = 0;
+    if (event->type() == ApiTraceCall::Call) {
+        call = static_cast<ApiTraceCall*>(event);
+    }
+
+    return call;
+
+}
+
+void MainWindow::slotFoundFrameStart(ApiTraceFrame *frame)
+{
+    Q_ASSERT(frame->isLoaded());
+    if (!frame || frame->isEmpty()) {
+        return;
+    }
+
+    QVector<ApiTraceCall*>::const_iterator itr;
+    QVector<ApiTraceCall*> calls = frame->calls();
+
+    itr = calls.constBegin();
+    while (itr != calls.constEnd()) {
+        ApiTraceCall *call = *itr;
+        QModelIndex idx = m_proxyModel->indexForCall(call);
+        if (idx.isValid()) {
+            m_ui.callView->setCurrentIndex(idx);
+            break;
+        }
+        ++itr;
+    }
+}
+
+void MainWindow::slotFoundFrameEnd(ApiTraceFrame *frame)
+{
+    Q_ASSERT(frame->isLoaded());
+    if (!frame || frame->isEmpty()) {
+        return;
+    }
+    QVector<ApiTraceCall*>::const_iterator itr;
+    QVector<ApiTraceCall*> calls = frame->calls();
+
+    itr = calls.constEnd();
+    do {
+        --itr;
+        ApiTraceCall *call = *itr;
+        QModelIndex idx = m_proxyModel->indexForCall(call);
+        if (idx.isValid()) {
+            m_ui.callView->setCurrentIndex(idx);
+            break;
+        }
+    } while (itr != calls.constBegin());
+}
+
+void MainWindow::slotJumpToResult(ApiTraceCall *call)
+{
+    QModelIndex index = m_proxyModel->indexForCall(call);
+    if (index.isValid()) {
+        m_ui.callView->setCurrentIndex(index);
+    } else {
+        statusBar()->showMessage(tr("Call has been filtered out."));
+    }
+}
+
 #include "mainwindow.moc"
index f3e31811717f4a218f60860df2f5e203534125cd..0ae8ab78e9315c619aa99d843b32f27c873e0ea5 100644 (file)
@@ -3,6 +3,8 @@
 
 #include "ui_mainwindow.h"
 
+#include "apitrace.h"
+
 #include <QMainWindow>
 #include <QProcess>
 
@@ -19,7 +21,7 @@ class QModelIndex;
 class QProgressBar;
 class QTreeWidgetItem;
 class QUrl;
-struct RetraceError;
+struct ApiTraceError;
 class Retracer;
 class SearchWidget;
 class ShadersSourceWidget;
@@ -46,6 +48,7 @@ private slots:
     void replayStateFound(ApiTraceState *state);
     void replayError(const QString &msg);
     void startedLoadingTrace();
+    void loadProgess(int percent);
     void finishedLoadingTrace();
     void lookupState();
     void showSettings();
@@ -68,8 +71,13 @@ private slots:
     void slotGoFrameStart();
     void slotGoFrameEnd();
     void slotTraceChanged(ApiTraceCall *call);
-    void slotRetraceErrors(const QList<RetraceError> &errors);
+    void slotRetraceErrors(const QList<ApiTraceError> &errors);
     void slotErrorSelected(QTreeWidgetItem *current);
+    void slotSearchResult(ApiTrace::SearchResult result,
+                          ApiTraceCall *call);
+    void slotFoundFrameStart(ApiTraceFrame *frame);
+    void slotFoundFrameEnd(ApiTraceFrame *frame);
+    void slotJumpToResult(ApiTraceCall *call);
 
 private:
     void initObjects();
@@ -77,9 +85,17 @@ private:
     void newTraceFile(const QString &fileName);
     void replayTrace(bool dumpState);
     void fillStateForFrame();
+
+    /* there's a difference between selected frame/call and
+     * current call/frame. the former implies actual selection
+     * the latter might be just a highlight, e.g. during searching
+     */
+    ApiTraceFrame *selectedFrame() const;
+    ApiTraceCall *selectedCall() const;
     ApiTraceFrame *currentFrame() const;
     ApiTraceCall *currentCall() const;
 
+
 private:
     Ui_MainWindow m_ui;
     ShadersSourceWidget *m_sourcesWidget;
index 500d859aa3f6d73011acc0bbaabd38a58ed19287..251028ce5ab316066dcfd8baac94a6376a91eb5b 100644 (file)
@@ -100,8 +100,8 @@ void Retracer::run()
             this, SIGNAL(error(const QString&)));
     connect(retrace, SIGNAL(foundState(ApiTraceState*)),
             this, SIGNAL(foundState(ApiTraceState*)));
-    connect(retrace, SIGNAL(retraceErrors(const QList<RetraceError>&)),
-            this, SIGNAL(retraceErrors(const QList<RetraceError>&)));
+    connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
+            this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
 
     retrace->start();
 
@@ -163,11 +163,11 @@ void RetraceProcess::replayFinished()
     }
 
     QStringList errorLines = errStr.split('\n');
-    QList<RetraceError> errors;
+    QList<ApiTraceError> errors;
     QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
     foreach(QString line, errorLines) {
         if (regexp.indexIn(line) != -1) {
-            RetraceError error;
+            ApiTraceError error;
             error.callIndex = regexp.cap(1).toInt();
             error.type = regexp.cap(2);
             error.message = regexp.cap(3);
@@ -190,14 +190,14 @@ void RetraceProcess::replayError(QProcess::ProcessError err)
         tr("Couldn't execute the replay file '%1'").arg(m_fileName));
 }
 
-Q_DECLARE_METATYPE(QList<RetraceError>);
+Q_DECLARE_METATYPE(QList<ApiTraceError>);
 RetraceProcess::RetraceProcess(QObject *parent)
     : QObject(parent)
 {
     m_process = new QProcess(this);
     m_jsonParser = new QJson::Parser();
 
-    qRegisterMetaType<QList<RetraceError> >();
+    qRegisterMetaType<QList<ApiTraceError> >();
 
     connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
             this, SLOT(replayFinished()));
index 70170f5ce4605949789083d555ab76307976df7e..4a43c261d7ec10ad1442b909e7414aa63866dd2b 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef RETRACER_H
 #define RETRACER_H
 
+#include "apitracecall.h"
+
 #include <QThread>
 #include <QProcess>
 
@@ -9,12 +11,6 @@ namespace QJson {
     class Parser;
 }
 
-struct RetraceError {
-    int callIndex;
-    QString type;
-    QString message;
-};
-
 /* internal class used by the retracer to run
  * in the thread */
 class RetraceProcess : public QObject
@@ -49,7 +45,7 @@ signals:
     void finished(const QString &output);
     void error(const QString &msg);
     void foundState(ApiTraceState *state);
-    void retraceErrors(const QList<RetraceError> &errors);
+    void retraceErrors(const QList<ApiTraceError> &errors);
 
 private slots:
     void replayFinished();
@@ -91,7 +87,7 @@ signals:
     void finished(const QString &output);
     void foundState(ApiTraceState *state);
     void error(const QString &msg);
-    void retraceErrors(const QList<RetraceError> &errors);
+    void retraceErrors(const QList<ApiTraceError> &errors);
 
 protected:
     virtual void run();
index 5837c9961d92703ade8b0eea4a2bcd423488210d..d5f4d6b0ce5d1461b753b91b6a069792a45563a4 100644 (file)
@@ -1,6 +1,8 @@
 #include "saverthread.h"
 
 #include "trace_writer.hpp"
+#include "trace_model.hpp"
+#include "trace_parser.hpp"
 
 #include <QFile>
 #include <QHash>
@@ -8,7 +10,7 @@
 
 #include <QDebug>
 
-
+#if 0
 static Trace::FunctionSig *
 createFunctionSig(ApiTraceCall *call, unsigned id)
 {
@@ -203,54 +205,164 @@ writeValue(Trace::Writer &writer, const QVariant &var, unsigned &id)
         }
     }
 }
+#endif
+
+class EditVisitor : public Trace::Visitor
+{
+public:
+    EditVisitor(const QVariant &variant)
+        : m_variant(variant),
+          m_editedValue(0)
+    {}
+    virtual void visit(Trace::Null *val)
+    {
+        m_editedValue = val;
+    }
+
+    virtual void visit(Trace::Bool *node)
+    {
+//        Q_ASSERT(m_variant.userType() == QVariant::Bool);
+        bool var = m_variant.toBool();
+        m_editedValue = new Trace::Bool(var);
+    }
+
+    virtual void visit(Trace::SInt *node)
+    {
+//        Q_ASSERT(m_variant.userType() == QVariant::Int);
+        m_editedValue = new Trace::SInt(m_variant.toInt());
+    }
+
+    virtual void visit(Trace::UInt *node)
+    {
+//        Q_ASSERT(m_variant.userType() == QVariant::UInt);
+        m_editedValue = new Trace::SInt(m_variant.toUInt());
+    }
+
+    virtual void visit(Trace::Float *node)
+    {
+        m_editedValue = new Trace::Float(m_variant.toFloat());
+    }
+
+    virtual void visit(Trace::String *node)
+    {
+        QString str = m_variant.toString();
+        m_editedValue = new Trace::String(str.toLocal8Bit().constData());
+    }
+
+    virtual void visit(Trace::Enum *e)
+    {
+        m_editedValue = e;
+    }
+
+    virtual void visit(Trace::Bitmask *bitmask)
+    {
+        m_editedValue = bitmask;
+    }
+
+    virtual void visit(Trace::Struct *str)
+    {
+        m_editedValue = str;
+    }
+
+    virtual void visit(Trace::Array *array)
+    {
+        ApiArray apiArray = m_variant.value<ApiArray>();
+        QVector<QVariant> vals = apiArray.values();
+
+        Trace::Array *newArray = new Trace::Array(vals.count());
+        for (int i = 0; i < vals.count(); ++i) {
+            EditVisitor visitor(vals[i]);
+
+            array->values[i]->visit(visitor);
+            if (array->values[i] == visitor.value()) {
+                //non-editabled
+                delete newArray;
+                m_editedValue = array;
+                return;
+            }
+
+            newArray->values.push_back(visitor.value());
+        }
+        m_editedValue = newArray;
+    }
+
+    virtual void visit(Trace::Blob *blob)
+    {
+        m_editedValue = blob;
+    }
+
+    virtual void visit(Trace::Pointer *ptr)
+    {
+        m_editedValue = ptr;
+    }
+
+    Trace::Value *value() const
+    {
+        return m_editedValue;
+    }
+private:
+    QVariant m_variant;
+    Trace::Value *m_editedValue;
+};
+
+static void
+overwriteValue(Trace::Call *call, const QVariant &val, int index)
+{
+    EditVisitor visitor(val);
+    Trace::Value *origValue = call->args[index];
+    origValue->visit(visitor);
+
+    if (visitor.value() && origValue != visitor.value()) {
+        delete origValue;
+        call->args[index] = visitor.value();
+    }
+}
 
 SaverThread::SaverThread(QObject *parent)
     : QThread(parent)
 {
 }
 
-void SaverThread::saveFile(const QString &fileName,
-                           const QVector<ApiTraceCall*> &calls)
+void SaverThread::saveFile(const QString &writeFileName,
+                           const QString &readFileName,
+                           const QSet<ApiTraceCall*> &editedCalls)
 {
-    m_fileName = fileName;
-    m_calls = calls;
+    m_writeFileName = writeFileName;
+    m_readFileName = readFileName;
+    m_editedCalls = editedCalls;
     start();
 }
 
 void SaverThread::run()
 {
-    unsigned id = 0;
-    qDebug() << "Saving  : " << m_fileName;
+    qDebug() << "Saving  " << m_readFileName
+             << ", to " << m_writeFileName;
+    QMap<int, ApiTraceCall*> callIndexMap;
+
+    foreach(ApiTraceCall *call, m_editedCalls) {
+        callIndexMap.insert(call->index(), call);
+    }
+
     Trace::Writer writer;
-    writer.open(m_fileName.toLocal8Bit());
-    for (int i = 0; i < m_calls.count(); ++i) {
-        ApiTraceCall *call = m_calls[i];
-        Trace::FunctionSig *funcSig = createFunctionSig(call, ++id);
-        unsigned callNo = writer.beginEnter(funcSig);
-        {
-            //args
-            QVector<QVariant> vars = call->arguments();
-            int index = 0;
-            foreach(QVariant var, vars) {
-                writer.beginArg(index++);
-                writeValue(writer, var, ++id);
-                writer.endArg();
-            }
-        }
-        writer.endEnter();
-        writer.beginLeave(callNo);
-        {
-            QVariant ret = call->returnValue();
-            if (!ret.isNull()) {
-                writer.beginReturn();
-                writeValue(writer, ret, ++id);
-                writer.endReturn();
+    writer.open(m_writeFileName.toLocal8Bit());
+
+    Trace::Parser parser;
+    parser.open(m_readFileName.toLocal8Bit());
+
+    Trace::Call *call;
+    while ((call = parser.parse_call())) {
+        if (callIndexMap.contains(call->no)) {
+            QVector<QVariant> values = callIndexMap[call->no]->editedValues();
+            for (int i = 0; i < values.count(); ++i) {
+                const QVariant &val = values[i];
+                overwriteValue(call, val, i);
             }
+            writer.writeCall(call);
+        } else {
+            writer.writeCall(call);
         }
-        writer.endLeave();
-
-        deleteFunctionSig(funcSig);
     }
+
     writer.close();
 
     emit traceSaved();
index 3da502ce98df509b190893cd7b7df39d9f2fab7c..e8c6889f2c80c5cda6a9933f18970889d9713729 100644 (file)
@@ -16,8 +16,9 @@ public:
     SaverThread(QObject *parent=0);
 
 public slots:
-    void saveFile(const QString &fileName,
-                  const QVector<ApiTraceCall*> &calls);
+    void saveFile(const QString &saveFileName,
+                  const QString &readFileName,
+                  const QSet<ApiTraceCall*> &editedCalls);
 
 signals:
     void traceSaved();
@@ -26,8 +27,9 @@ protected:
     virtual void run();
 
 private:
-    QString m_fileName;
-    QVector<ApiTraceCall*> m_calls;
+    QString m_readFileName;
+    QString m_writeFileName;
+    QSet<ApiTraceCall*> m_editedCalls;
 };
 
 
diff --git a/gui/traceloader.cpp b/gui/traceloader.cpp
new file mode 100644 (file)
index 0000000..d970c16
--- /dev/null
@@ -0,0 +1,497 @@
+#include "traceloader.h"
+
+#include "apitrace.h"
+#include <QDebug>
+#include <QFile>
+
+#define FRAMES_TO_CACHE 100
+
+static ApiTraceCall *
+apiCallFromTraceCall(const Trace::Call *call,
+                     const QHash<QString, QUrl> &helpHash,
+                     ApiTraceFrame *frame,
+                     TraceLoader *loader)
+{
+    ApiTraceCall *apiCall = new ApiTraceCall(frame, loader, call);
+
+    apiCall->setHelpUrl(helpHash.value(apiCall->name()));
+
+    return apiCall;
+}
+
+TraceLoader::TraceLoader(QObject *parent)
+    : QObject(parent),
+      m_frameMarker(ApiTrace::FrameMarker_SwapBuffers)
+{
+}
+
+TraceLoader::~TraceLoader()
+{
+    m_parser.close();
+    qDeleteAll(m_signatures);
+    qDeleteAll(m_enumSignatures);
+}
+
+void TraceLoader::loadTrace(const QString &filename)
+{
+    if (m_helpHash.isEmpty()) {
+        loadHelpFile();
+    }
+
+    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();
+    } else {
+        //Load the entire file into memory
+        parseTrace();
+    }
+
+    emit finishedParsing();
+}
+
+void TraceLoader::loadFrame(ApiTraceFrame *currentFrame)
+{
+    fetchFrameContents(currentFrame);
+}
+
+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;
+}
+
+int TraceLoader::numberOfFrames() const
+{
+    return m_frameBookmarks.size();
+}
+
+int TraceLoader::numberOfCallsInFrame(int frameIdx) const
+{
+    if (frameIdx > m_frameBookmarks.size()) {
+        return 0;
+    }
+    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();
+}
+
+void TraceLoader::scanTrace()
+{
+    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 (isCallAFrameMarker(call)) {
+            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) {
+                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);
+    }
+}
+
+
+ApiTraceCallSignature * TraceLoader::signature(unsigned id)
+{
+    if (id >= m_signatures.count()) {
+        m_signatures.resize(id + 1);
+        return NULL;
+    } else {
+        return m_signatures[id];
+    }
+}
+
+void TraceLoader::addSignature(unsigned id, ApiTraceCallSignature *signature)
+{
+    m_signatures[id] = signature;
+}
+
+ApiTraceEnumSignature * TraceLoader::enumSignature(unsigned id)
+{
+    if (id >= m_enumSignatures.count()) {
+        m_enumSignatures.resize(id + 1);
+        return NULL;
+    } else {
+        return m_enumSignatures[id];
+    }
+}
+
+void TraceLoader::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature)
+{
+    m_enumSignatures[id] = signature;
+}
+
+void TraceLoader::searchNext(int startFrame,
+                             const QString &str,
+                             Qt::CaseSensitivity sensitivity)
+{
+    Q_ASSERT(m_parser.supportsOffsets());
+    if (m_parser.supportsOffsets()) {
+        const FrameBookmark &frameBookmark = m_frameBookmarks[startFrame];
+        m_parser.setBookmark(frameBookmark.start);
+        Trace::Call *call = 0;
+        while ((call = m_parser.parse_call())) {
+
+            if (callContains(call, str, sensitivity)) {
+                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(ApiTrace::SearchResult_Found, calls[i]);
+                        break;
+                    }
+                }
+                delete call;
+                return;
+            }
+
+            delete call;
+        }
+    }
+    emit searchResult(ApiTrace::SearchResult_NotFound, 0);
+}
+
+void TraceLoader::searchPrev(int startFrame,
+                             const QString &str,
+                             Qt::CaseSensitivity sensitivity)
+{
+    Q_ASSERT(m_parser.supportsOffsets());
+    if (m_parser.supportsOffsets()) {
+        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,
+                                                      str, sensitivity);
+
+                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(ApiTrace::SearchResult_NotFound, 0);
+}
+
+bool TraceLoader::searchCallsBackwards(const QList<Trace::Call*> &calls,
+                                       int frameIdx,
+                                       const QString &str,
+                                       Qt::CaseSensitivity sensitivity)
+{
+    for (int i = calls.count() - 1; i >= 0; --i) {
+        Trace::Call *call = calls[i];
+        if (callContains(call, str, sensitivity)) {
+            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(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);
+                calls[parsedCalls] = apiCall;
+                Q_ASSERT(calls[parsedCalls]);
+                if (apiCall->hasBinaryData()) {
+                    QByteArray data =
+                        apiCall->arguments()[
+                            apiCall->binaryDataIndex()].toByteArray();
+                    binaryDataSize += data.size();
+                }
+
+                ++parsedCalls;
+
+                delete call;
+
+                if (ApiTrace::isCallAFrameMarker(apiCall, m_frameMarker)) {
+                    break;
+                }
+
+            }
+            assert(parsedCalls == numOfCalls);
+            Q_ASSERT(parsedCalls == calls.size());
+            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;
+        }
+    }
+    Q_ASSERT(call);
+    emit foundCallIndex(call);
+}
+
+#include "traceloader.moc"
diff --git a/gui/traceloader.h b/gui/traceloader.h
new file mode 100644 (file)
index 0000000..4b88ec6
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef TRACELOADER_H
+#define TRACELOADER_H
+
+
+#include "apitrace.h"
+#include "trace_file.hpp"
+#include "trace_parser.hpp"
+
+#include <QObject>
+#include <QList>
+#include <QMap>
+
+class TraceLoader : public QObject
+{
+    Q_OBJECT
+public:
+    TraceLoader(QObject *parent=0);
+    ~TraceLoader();
+
+
+    ApiTraceCallSignature *signature(unsigned id);
+    void addSignature(unsigned id, ApiTraceCallSignature *signature);
+
+    ApiTraceEnumSignature *enumSignature(unsigned id);
+    void addEnumSignature(unsigned id, ApiTraceEnumSignature *signature);
+
+public slots:
+    void loadTrace(const QString &filename);
+    void loadFrame(ApiTraceFrame *frame);
+    void setFrameMarker(ApiTrace::FrameMarker marker);
+    void searchNext(int startFrame,
+                    const QString &str,
+                    Qt::CaseSensitivity sensitivity);
+    void searchPrev(int startFrame,
+                    const QString &str,
+                    Qt::CaseSensitivity sensitivity);
+    void findFrameStart(ApiTraceFrame *frame);
+    void findFrameEnd(ApiTraceFrame *frame);
+    void findCallIndex(int index);
+
+signals:
+    void startedParsing();
+    void parsed(int percent);
+    void finishedParsing();
+
+    void framesLoaded(const QList<ApiTraceFrame*> &frames);
+    void frameContentsLoaded(ApiTraceFrame *frame,
+                             const QVector<ApiTraceCall*> &calls,
+                             quint64 binaryDataSize);
+
+    void searchResult(ApiTrace::SearchResult result, ApiTraceCall *call);
+    void foundFrameStart(ApiTraceFrame *frame);
+    void foundFrameEnd(ApiTraceFrame *frame);
+    void foundCallIndex(ApiTraceCall *call);
+private:
+    struct FrameBookmark {
+        FrameBookmark()
+            : numberOfCalls(0)
+        {}
+        FrameBookmark(const Trace::ParseBookmark &s)
+            : start(s),
+              numberOfCalls(0)
+        {}
+
+        Trace::ParseBookmark start;
+        int numberOfCalls;
+    };
+    bool isCallAFrameMarker(const Trace::Call *call) const;
+    int numberOfFrames() const;
+    int numberOfCallsInFrame(int frameIdx) const;
+
+    void loadHelpFile();
+    void scanTrace();
+    void parseTrace();
+
+    int callInFrame(int callIdx) const;
+    bool callContains(Trace::Call *call,
+                      const QString &str,
+                      Qt::CaseSensitivity sensitivity);
+     QVector<ApiTraceCall*> fetchFrameContents(ApiTraceFrame *frame);
+     bool searchCallsBackwards(const QList<Trace::Call*> &calls,
+                               int frameIdx,
+                               const QString &str,
+                               Qt::CaseSensitivity sensitivity);
+
+private:
+    Trace::Parser m_parser;
+    QString m_fileName;
+    ApiTrace::FrameMarker m_frameMarker;
+
+    typedef QMap<int, FrameBookmark> FrameBookmarks;
+    FrameBookmarks m_frameBookmarks;
+    QList<ApiTraceFrame*> m_createdFrames;
+
+    QHash<QString, QUrl> m_helpHash;
+
+    QVector<ApiTraceCallSignature*> m_signatures;
+    QVector<ApiTraceEnumSignature*> m_enumSignatures;
+};
+
+#endif
index 3a7691b34c72d1e879ce616baebc6333afaffb70..f48c1aa9189c8b04c23a92448afc5b51ddd9d216 100644 (file)
@@ -55,6 +55,12 @@ File::~File()
     close();
 }
 
+
+void File::setCurrentOffset(const File::Offset &offset)
+{
+    assert(0);
+}
+
 bool File::isZLibCompressed(const std::string &filename)
 {
     std::fstream stream(filename.c_str(),
@@ -86,6 +92,12 @@ bool File::isSnappyCompressed(const std::string &filename)
     return (byte1 == SNAPPY_BYTE1 && byte2 == SNAPPY_BYTE2);
 }
 
+typedef struct gz_stream {
+    z_stream stream;
+    int      z_err;   /* error code for last stream operation */
+    int      z_eof;   /* set if end of input file */
+    FILE     *file;   /* .gz file */
+} gz_dummy_stream;
 
 ZLibFile::ZLibFile(const std::string &filename,
                    File::Mode mode)
@@ -102,6 +114,19 @@ bool ZLibFile::rawOpen(const std::string &filename, File::Mode mode)
 {
     m_gzFile = gzopen(filename.c_str(),
                       (mode == File::Write) ? "wb" : "rb");
+
+    if (mode == File::Read && m_gzFile) {
+        //XXX: unfortunately zlib doesn't support
+        //     SEEK_END or we could've done:
+        //m_endOffset = gzseek(m_gzFile, 0, SEEK_END);
+        //gzrewind(m_gzFile);
+        gz_dummy_stream *stream = (gz_dummy_stream *)m_gzFile;
+        long loc = ftell(stream->file);
+        fseek(stream->file,0,SEEK_END);
+        m_endOffset = ftell(stream->file);
+        fseek(stream->file, loc, SEEK_SET);
+    }
+
     return m_gzFile != NULL;
 }
 
@@ -132,3 +157,24 @@ void ZLibFile::rawFlush()
 {
     gzflush(m_gzFile, Z_SYNC_FLUSH);
 }
+
+File::Offset ZLibFile::currentOffset()
+{
+    return File::Offset(gztell(m_gzFile));
+}
+
+bool ZLibFile::supportsOffsets() const
+{
+    return false;
+}
+
+bool ZLibFile::rawSkip(size_t)
+{
+    return false;
+}
+
+int ZLibFile::rawPercentRead()
+{
+    gz_dummy_stream *stream = (gz_dummy_stream *)m_gzFile;
+    return 100 * (ftell(stream->file) / m_endOffset);
+}
index 8277b48d4df9c11e39a4b197e5764c396febf484..4b1b70ddad6f6bd886ac7aa51c28f5604b281faf 100644 (file)
@@ -29,6 +29,7 @@
 
 #include <string>
 #include <fstream>
+#include <stdint.h>
 
 namespace Trace {
 
@@ -38,6 +39,15 @@ public:
         Read,
         Write
     };
+    struct Offset {
+        Offset(uint64_t _chunk = 0, uint32_t _offsetInChunk = 0)
+            : chunk(_chunk),
+              offsetInChunk(_offsetInChunk)
+        {}
+        uint64_t chunk;
+        uint32_t offsetInChunk;
+    };
+
 public:
     static bool isZLibCompressed(const std::string &filename);
     static bool isSnappyCompressed(const std::string &filename);
@@ -55,7 +65,12 @@ public:
     void close();
     void flush(void);
     int getc();
+    bool skip(size_t length);
+    int percentRead();
 
+    virtual bool supportsOffsets() const = 0;
+    virtual File::Offset currentOffset() = 0;
+    virtual void setCurrentOffset(const File::Offset &offset);
 protected:
     virtual bool rawOpen(const std::string &filename, File::Mode mode) = 0;
     virtual bool rawWrite(const void *buffer, size_t length) = 0;
@@ -63,6 +78,8 @@ protected:
     virtual int rawGetc() = 0;
     virtual void rawClose() = 0;
     virtual void rawFlush() = 0;
+    virtual bool rawSkip(size_t length) = 0;
+    virtual int rawPercentRead() = 0;
 
 protected:
     File::Mode m_mode;
@@ -106,6 +123,14 @@ inline bool File::read(void *buffer, size_t length)
     return rawRead(buffer, length);
 }
 
+inline int File::percentRead()
+{
+    if (!m_isOpened || m_mode != File::Read) {
+        return 0;
+    }
+    return rawPercentRead();
+}
+
 inline void File::close()
 {
     if (m_isOpened) {
@@ -129,12 +154,23 @@ inline int File::getc()
     return rawGetc();
 }
 
+inline bool File::skip(size_t length)
+{
+    if (!m_isOpened || m_mode != File::Read) {
+        return false;
+    }
+    return rawSkip(length);
+}
+
 class ZLibFile : public File {
 public:
     ZLibFile(const std::string &filename = std::string(),
              File::Mode mode = File::Read);
     virtual ~ZLibFile();
 
+
+    virtual bool supportsOffsets() const;
+    virtual File::Offset currentOffset();
 protected:
     virtual bool rawOpen(const std::string &filename, File::Mode mode);
     virtual bool rawWrite(const void *buffer, size_t length);
@@ -142,10 +178,47 @@ protected:
     virtual int rawGetc();
     virtual void rawClose();
     virtual void rawFlush();
+    virtual bool rawSkip(size_t length);
+    virtual int  rawPercentRead();
 private:
     void *m_gzFile;
+    double m_endOffset;
 };
 
+inline bool
+operator<(const File::Offset &one, const File::Offset &two)
+{
+    return one.chunk < two.chunk ||
+            (one.chunk == two.chunk && one.offsetInChunk < two.offsetInChunk);
+}
+
+inline bool
+operator==(const File::Offset &one, const File::Offset &two)
+{
+    return one.chunk == two.chunk &&
+            one.offsetInChunk == two.offsetInChunk;
+}
+
+inline bool
+operator>=(const File::Offset &one, const File::Offset &two)
+{
+    return one.chunk > two.chunk ||
+            (one.chunk == two.chunk && one.offsetInChunk >= two.offsetInChunk);
+}
+
+inline bool
+operator>(const File::Offset &one, const File::Offset &two)
+{
+    return two < one;
+}
+
+inline bool
+operator<=(const File::Offset &one, const File::Offset &two)
+{
+    return two >= one;
+}
+
+
 }
 
 #endif
diff --git a/trace_loader.cpp b/trace_loader.cpp
new file mode 100644 (file)
index 0000000..c14d815
--- /dev/null
@@ -0,0 +1,139 @@
+#include "trace_loader.hpp"
+
+
+using namespace Trace;
+
+Loader::Loader()
+    : m_frameMarker(FrameMarker_SwapBuffers)
+{
+}
+
+Loader::~Loader()
+{
+    close();
+}
+
+Loader::FrameMarker Loader::frameMarker() const
+{
+    return m_frameMarker;
+}
+
+void Loader::setFrameMarker(Loader::FrameMarker marker)
+{
+    m_frameMarker = marker;
+}
+
+int Loader::numberOfFrames() const
+{
+    return m_frameBookmarks.size();
+}
+
+int Loader::numberOfCallsInFrame(int frameIdx) const
+{
+    if (frameIdx > m_frameBookmarks.size()) {
+        return 0;
+    }
+    FrameBookmarks::const_iterator itr =
+        m_frameBookmarks.find(frameIdx);
+    return itr->second.numberOfCalls;
+}
+
+bool Loader::open(const char *filename)
+{
+    if (!m_parser.open(filename)) {
+        std::cerr << "error: failed to open " << filename << "\n";
+        return false;
+    }
+    if (!m_parser.supportsOffsets()) {
+        std::cerr << "error: " <<filename<< " doesn't support seeking "
+                  << "\n";
+        return false;
+    }
+
+    Trace::Call *call;
+    ParseBookmark startBookmark;
+    int numOfFrames = 0;
+    int numOfCalls = 0;
+    int lastPercentReport = 0;
+
+    m_parser.getBookmark(startBookmark);
+
+    while ((call = m_parser.scan_call())) {
+        ++numOfCalls;
+
+        if (isCallAFrameMarker(call)) {
+            FrameBookmark frameBookmark(startBookmark);
+            frameBookmark.numberOfCalls = numOfCalls;
+
+            m_frameBookmarks[numOfFrames] = frameBookmark;
+            ++numOfFrames;
+
+            if (m_parser.percentRead() - lastPercentReport >= 5) {
+                std::cerr << "\tPercent scanned = "
+                          << m_parser.percentRead()
+                          << "..."<<std::endl;
+                lastPercentReport = m_parser.percentRead();
+            }
+            
+            m_parser.getBookmark(startBookmark);
+            numOfCalls = 0;
+        }
+        //call->dump(std::cout, color);
+        delete call;
+    }
+    return true;
+}
+
+void Loader::close()
+{
+    m_parser.close();
+}
+
+bool Loader::isCallAFrameMarker(const Trace::Call *call) const
+{
+    std::string name = call->name();
+
+    switch (m_frameMarker) {
+    case FrameMarker_SwapBuffers:
+        return  name.find("SwapBuffers") != std::string::npos ||
+                name == "CGLFlushDrawable" ||
+                name == "glFrameTerminatorGREMEDY";
+        break;
+    case FrameMarker_Flush:
+        return name == "glFlush";
+        break;
+    case FrameMarker_Finish:
+        return name == "glFinish";
+        break;
+    case FrameMarker_Clear:
+        return name == "glClear";
+        break;
+    }
+    return false;
+}
+
+std::vector<Trace::Call *> Loader::frame(int idx)
+{
+    int numOfCalls = numberOfCallsInFrame(idx);
+    if (numOfCalls) {
+        const FrameBookmark &frameBookmark = m_frameBookmarks[idx];
+        std::vector<Trace::Call*> calls(numOfCalls);
+        m_parser.setBookmark(frameBookmark.start);
+
+        Trace::Call *call;
+        int parsedCalls = 0;
+        while ((call = m_parser.parse_call())) {
+
+            calls[parsedCalls] = call;
+            ++parsedCalls;
+
+            if (isCallAFrameMarker(call)) {
+                break;
+            }
+
+        }
+        assert(parsedCalls == numOfCalls);
+        return calls;
+    }
+    return std::vector<Trace::Call*>();
+}
diff --git a/trace_loader.hpp b/trace_loader.hpp
new file mode 100644 (file)
index 0000000..9f74a9b
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef TRACE_LOADER_HPP
+#define TRACE_LOADER_HPP
+
+#include "trace_file.hpp"
+#include "trace_parser.hpp"
+
+#include <string>
+#include <map>
+#include <queue>
+#include <vector>
+
+namespace Trace  {
+
+class Frame;
+
+class Loader
+{
+public:
+    enum FrameMarker {
+        FrameMarker_SwapBuffers,
+        FrameMarker_Flush,
+        FrameMarker_Finish,
+        FrameMarker_Clear
+    };
+public:
+    Loader();
+    ~Loader();
+
+    Loader::FrameMarker frameMarker() const;
+    void setFrameMarker(Loader::FrameMarker marker);
+
+    int numberOfFrames() const;
+    int numberOfCallsInFrame(int frameIdx) const;
+
+    bool open(const char *filename);
+    void close();
+
+    std::vector<Trace::Call*> frame(int idx);
+
+private:
+    struct FrameBookmark {
+        FrameBookmark()
+            : numberOfCalls(0)
+        {}
+        FrameBookmark(const ParseBookmark &s)
+            : start(s),
+              numberOfCalls(0)
+        {}
+
+        ParseBookmark start;
+        int numberOfCalls;
+    };
+    bool isCallAFrameMarker(const Trace::Call *call) const;
+
+private:
+    Trace::Parser m_parser;
+    FrameMarker m_frameMarker;
+
+    typedef std::map<int, FrameBookmark> FrameBookmarks;
+    FrameBookmarks m_frameBookmarks;
+};
+
+}
+
+#endif // TRACE_LOADER_HPP
index 25dc4bb487138a2e38a33f277940c733bab5ce7e..306b9e7a64da745384f06ee050eeddc97e9a1b4b 100644 (file)
@@ -42,6 +42,11 @@ Call::~Call() {
 }
 
 
+String::~String() {
+    delete [] value;
+}
+
+
 Struct::~Struct() {
     for (std::vector<Value *>::iterator it = members.begin(); it != members.end(); ++it) {
         delete *it;
index a2847547421f8972666681d0784882192e2fb628..a74508efc5e0a0fc2411f9fe19b3e1aea070beda 100644 (file)
@@ -190,6 +190,7 @@ class String : public Value
 {
 public:
     String(const char * _value) : value(_value) {}
+    ~String();
 
     bool toBool(void) const;
     const char *toString(void) const;
index 44d1786ac207fe2fa576a88c9cfae90339013fdc..d7b20d2c450faaab61cc986c6c046b72df5a4caa 100644 (file)
@@ -98,22 +98,83 @@ void Parser::close(void) {
     }
 
     deleteAll(calls);
-    deleteAll(functions);
-    deleteAll(structs);
-    deleteAll(enums);
-    deleteAll(bitmasks);
+
+    // Delete all signature data.  Signatures are mere structures which don't
+    // own their own memory, so we need to destroy all data we created here.
+
+    for (FunctionMap::iterator it = functions.begin(); it != functions.end(); ++it) {
+        FunctionSigState *sig = *it;
+        if (sig) {
+            delete [] sig->name;
+            for (unsigned arg = 0; arg < sig->num_args; ++arg) {
+                delete [] sig->arg_names[arg];
+            }
+            delete [] sig->arg_names;
+            delete sig;
+        }
+    }
+    functions.clear();
+
+    for (StructMap::iterator it = structs.begin(); it != structs.end(); ++it) {
+        StructSigState *sig = *it;
+        if (sig) {
+            delete [] sig->name;
+            for (unsigned member = 0; member < sig->num_members; ++member) {
+                delete [] sig->member_names[member];
+            }
+            delete [] sig->member_names;
+            delete sig;
+        }
+    }
+    structs.clear();
+
+    for (EnumMap::iterator it = enums.begin(); it != enums.end(); ++it) {
+        EnumSigState *sig = *it;
+        if (sig) {
+            delete [] sig->name;
+            delete sig;
+        }
+    }
+    enums.clear();
+    
+    for (BitmaskMap::iterator it = bitmasks.begin(); it != bitmasks.end(); ++it) {
+        BitmaskSigState *sig = *it;
+        if (sig) {
+            for (unsigned flag = 0; flag < sig->num_flags; ++flag) {
+                delete [] sig->flags[flag].name;
+            }
+            delete [] sig->flags;
+            delete sig;
+        }
+    }
+    bitmasks.clear();
 }
 
 
-Call *Parser::parse_call(void) {
+void Parser::getBookmark(ParseBookmark &bookmark) {
+    bookmark.offset = file->currentOffset();
+    bookmark.next_call_no = next_call_no;
+}
+
+
+void Parser::setBookmark(const ParseBookmark &bookmark) {
+    file->setCurrentOffset(bookmark.offset);
+    next_call_no = bookmark.next_call_no;
+    
+    // Simply ignore all pending calls
+    deleteAll(calls);
+}
+
+
+Call *Parser::parse_call(Mode mode) {
     do {
         int c = read_byte();
         switch(c) {
         case Trace::EVENT_ENTER:
-            parse_enter();
+            parse_enter(mode);
             break;
         case Trace::EVENT_LEAVE:
-            return parse_leave();
+            return parse_leave(mode);
         default:
             std::cerr << "error: unknown event " << c << "\n";
             exit(1);
@@ -142,12 +203,14 @@ T *lookup(std::vector<T *> &map, size_t index) {
 }
 
 
-void Parser::parse_enter(void) {
+FunctionSig *Parser::parse_function_sig(void) {
     size_t id = read_uint();
 
-    FunctionSig *sig = lookup(functions, id);
+    FunctionSigState *sig = lookup(functions, id);
+
     if (!sig) {
-        sig = new FunctionSig;
+        /* parse the signature */
+        sig = new FunctionSigState;
         sig->id = id;
         sig->name = read_string();
         sig->num_args = read_uint();
@@ -156,14 +219,123 @@ void Parser::parse_enter(void) {
             arg_names[i] = read_string();
         }
         sig->arg_names = arg_names;
+        sig->offset = file->currentOffset();
         functions[id] = sig;
+    } else if (file->currentOffset() < sig->offset) {
+        /* skip over the signature */
+        skip_string(); /* name */
+        int num_args = read_uint();
+        for (unsigned i = 0; i < num_args; ++i) {
+             skip_string(); /*arg_name*/
+        }
+    }
+
+    assert(sig);
+    return sig;
+}
+
+
+StructSig *Parser::parse_struct_sig() {
+    size_t id = read_uint();
+
+    StructSigState *sig = lookup(structs, id);
+
+    if (!sig) {
+        /* parse the signature */
+        sig = new StructSigState;
+        sig->id = id;
+        sig->name = read_string();
+        sig->num_members = read_uint();
+        const char **member_names = new const char *[sig->num_members];
+        for (unsigned i = 0; i < sig->num_members; ++i) {
+            member_names[i] = read_string();
+        }
+        sig->member_names = member_names;
+        sig->offset = file->currentOffset();
+        structs[id] = sig;
+    } else if (file->currentOffset() < sig->offset) {
+        /* skip over the signature */
+        skip_string(); /* name */
+        unsigned num_members = read_uint();
+        for (unsigned i = 0; i < num_members; ++i) {
+            skip_string(); /* member_name */
+        }
     }
+
     assert(sig);
+    return sig;
+}
+
+
+EnumSig *Parser::parse_enum_sig() {
+    size_t id = read_uint();
+
+    EnumSigState *sig = lookup(enums, id);
+
+    if (!sig) {
+        /* parse the signature */
+        sig = new EnumSigState;
+        sig->id = id;
+        sig->name = read_string();
+        Value *value = parse_value();
+        sig->value = value->toSInt();
+        delete value;
+        sig->offset = file->currentOffset();
+        enums[id] = sig;
+    } else if (file->currentOffset() < sig->offset) {
+        /* skip over the signature */
+        skip_string(); /*name*/
+        scan_value();
+    }
+
+    assert(sig);
+    return sig;
+}
+
+
+BitmaskSig *Parser::parse_bitmask_sig() {
+    size_t id = read_uint();
+
+    BitmaskSigState *sig = lookup(bitmasks, id);
+
+    if (!sig) {
+        /* parse the signature */
+        sig = new BitmaskSigState;
+        sig->id = id;
+        sig->num_flags = read_uint();
+        BitmaskFlag *flags = new BitmaskFlag[sig->num_flags];
+        for (BitmaskFlag *it = flags; it != flags + sig->num_flags; ++it) {
+            it->name = read_string();
+            it->value = read_uint();
+            if (it->value == 0 && it != flags) {
+                std::cerr << "warning: bitmask " << it->name << " is zero but is not first flag\n";
+            }
+        }
+        sig->flags = flags;
+        sig->offset = file->currentOffset();
+        bitmasks[id] = sig;
+    } else if (file->currentOffset() < sig->offset) {
+        /* skip over the signature */
+        int num_flags = read_uint();
+        for (int i = 0; i < num_flags; ++i) {
+            skip_string(); /*name */
+            skip_uint(); /* value */
+        }
+    }
+
+    assert(sig);
+    return sig;
+}
+
+
+void Parser::parse_enter(Mode mode) {
+    FunctionSig *sig = parse_function_sig();
 
     Call *call = new Call(sig);
+
     call->no = next_call_no++;
 
-    if (parse_call_details(call)) {
+    if (parse_call_details(call, mode)) {
         calls.push_back(call);
     } else {
         delete call;
@@ -171,7 +343,7 @@ void Parser::parse_enter(void) {
 }
 
 
-Call *Parser::parse_leave(void) {
+Call *Parser::parse_leave(Mode mode) {
     unsigned call_no = read_uint();
     Call *call = NULL;
     for (CallList::iterator it = calls.begin(); it != calls.end(); ++it) {
@@ -185,7 +357,7 @@ Call *Parser::parse_leave(void) {
         return NULL;
     }
 
-    if (parse_call_details(call)) {
+    if (parse_call_details(call, mode)) {
         return call;
     } else {
         delete call;
@@ -194,17 +366,17 @@ Call *Parser::parse_leave(void) {
 }
 
 
-bool Parser::parse_call_details(Call *call) {
+bool Parser::parse_call_details(Call *call, Mode mode) {
     do {
         int c = read_byte();
         switch(c) {
         case Trace::CALL_END:
             return true;
         case Trace::CALL_ARG:
-            parse_arg(call);
+            parse_arg(call, mode);
             break;
         case Trace::CALL_RET:
-            call->ret = parse_value();
+            call->ret = parse_value(mode);
             break;
         default:
             std::cerr << "error: ("<<call->name()<< ") unknown call detail "
@@ -217,13 +389,15 @@ bool Parser::parse_call_details(Call *call) {
 }
 
 
-void Parser::parse_arg(Call *call) {
+void Parser::parse_arg(Call *call, Mode mode) {
     unsigned index = read_uint();
-    Value *value = parse_value();
-    if (index >= call->args.size()) {
-        call->args.resize(index + 1);
+    Value *value = parse_value(mode);
+    if (value) {
+        if (index >= call->args.size()) {
+            call->args.resize(index + 1);
+        }
+        call->args[index] = value;
     }
-    call->args[index] = value;
 }
 
 
@@ -290,16 +464,75 @@ Value *Parser::parse_value(void) {
 }
 
 
+void Parser::scan_value(void) {
+    int c = read_byte();
+    switch(c) {
+    case Trace::TYPE_NULL:
+    case Trace::TYPE_FALSE:
+    case Trace::TYPE_TRUE:
+        break;
+    case Trace::TYPE_SINT:
+        scan_sint();
+        break;
+    case Trace::TYPE_UINT:
+        scan_uint();
+        break;
+    case Trace::TYPE_FLOAT:
+        scan_float();
+        break;
+    case Trace::TYPE_DOUBLE:
+        scan_double();
+        break;
+    case Trace::TYPE_STRING:
+        scan_string();
+        break;
+    case Trace::TYPE_ENUM:
+        scan_enum();
+        break;
+    case Trace::TYPE_BITMASK:
+        scan_bitmask();
+        break;
+    case Trace::TYPE_ARRAY:
+        scan_array();
+        break;
+    case Trace::TYPE_STRUCT:
+        scan_struct();
+        break;
+    case Trace::TYPE_BLOB:
+        scan_blob();
+        break;
+    case Trace::TYPE_OPAQUE:
+        scan_opaque();
+        break;
+    default:
+        std::cerr << "error: unknown type " << c << "\n";
+        exit(1);
+    case -1:
+        break;
+    }
+}
+
+
 Value *Parser::parse_sint() {
     return new SInt(-(signed long long)read_uint());
 }
 
 
+void Parser::scan_sint() {
+    skip_uint();
+}
+
+
 Value *Parser::parse_uint() {
     return new UInt(read_uint());
 }
 
 
+void Parser::scan_uint() {
+    skip_uint();
+}
+
+
 Value *Parser::parse_float() {
     float value;
     file->read(&value, sizeof value);
@@ -307,6 +540,11 @@ Value *Parser::parse_float() {
 }
 
 
+void Parser::scan_float() {
+    file->skip(sizeof(float));
+}
+
+
 Value *Parser::parse_double() {
     double value;
     file->read(&value, sizeof value);
@@ -314,47 +552,34 @@ Value *Parser::parse_double() {
 }
 
 
+void Parser::scan_double() {
+    file->skip(sizeof(double));
+}
+
+
 Value *Parser::parse_string() {
     return new String(read_string());
 }
 
 
+void Parser::scan_string() {
+    skip_string();
+}
+
+
 Value *Parser::parse_enum() {
-    size_t id = read_uint();
-    EnumSig *sig = lookup(enums, id);
-    if (!sig) {
-        sig = new EnumSig;
-        sig->id = id;
-        sig->name = read_string();
-        Value *value = parse_value();
-        sig->value = value->toSInt();
-        delete value;
-        enums[id] = sig;
-    }
-    assert(sig);
+    EnumSig *sig = parse_enum_sig();
     return new Enum(sig);
 }
 
 
+void Parser::scan_enum() {
+    parse_enum_sig();
+}
+
+
 Value *Parser::parse_bitmask() {
-    size_t id = read_uint();
-    BitmaskSig *sig = lookup(bitmasks, id);
-    if (!sig) {
-        sig = new BitmaskSig;
-        sig->id = id;
-        sig->num_flags = read_uint();
-        BitmaskFlag *flags = new BitmaskFlag[sig->num_flags];
-        for (BitmaskFlag *it = flags; it != flags + sig->num_flags; ++it) {
-            it->name = read_string();
-            it->value = read_uint();
-            if (it->value == 0 && it != flags) {
-                std::cerr << "warning: bitmask " << it->name << " is zero but is not first flag\n";
-            }
-        }
-        sig->flags = flags;
-        bitmasks[id] = sig;
-    }
-    assert(sig);
+    BitmaskSig *sig = parse_bitmask_sig();
 
     unsigned long long value = read_uint();
 
@@ -362,6 +587,12 @@ Value *Parser::parse_bitmask() {
 }
 
 
+void Parser::scan_bitmask() {
+    parse_bitmask_sig();
+    skip_uint(); /* value */
+}
+
+
 Value *Parser::parse_array(void) {
     size_t len = read_uint();
     Array *array = new Array(len);
@@ -372,6 +603,14 @@ Value *Parser::parse_array(void) {
 }
 
 
+void Parser::scan_array(void) {
+    size_t len = read_uint();
+    for (size_t i = 0; i < len; ++i) {
+        scan_value();
+    }
+}
+
+
 Value *Parser::parse_blob(void) {
     size_t size = read_uint();
     Blob *blob = new Blob(size);
@@ -382,24 +621,16 @@ Value *Parser::parse_blob(void) {
 }
 
 
-Value *Parser::parse_struct() {
-    size_t id = read_uint();
-
-    StructSig *sig = lookup(structs, id);
-    if (!sig) {
-        sig = new StructSig;
-        sig->id = id;
-        sig->name = read_string();
-        sig->num_members = read_uint();
-        const char **member_names = new const char *[sig->num_members];
-        for (unsigned i = 0; i < sig->num_members; ++i) {
-            member_names[i] = read_string();
-        }
-        sig->member_names = member_names;
-        structs[id] = sig;
+void Parser::scan_blob(void) {
+    size_t size = read_uint();
+    if (size) {
+        file->skip(size);
     }
-    assert(sig);
+}
 
+
+Value *Parser::parse_struct() {
+    StructSig *sig = parse_struct_sig();
     Struct *value = new Struct(sig);
 
     for (size_t i = 0; i < sig->num_members; ++i) {
@@ -410,6 +641,14 @@ Value *Parser::parse_struct() {
 }
 
 
+void Parser::scan_struct() {
+    StructSig *sig = parse_struct_sig();
+    for (size_t i = 0; i < sig->num_members; ++i) {
+        scan_value();
+    }
+}
+
+
 Value *Parser::parse_opaque() {
     unsigned long long addr;
     addr = read_uint();
@@ -417,6 +656,11 @@ Value *Parser::parse_opaque() {
 }
 
 
+void Parser::scan_opaque() {
+    skip_uint();
+}
+
+
 const char * Parser::read_string(void) {
     size_t len = read_uint();
     char * value = new char[len + 1];
@@ -431,6 +675,12 @@ const char * Parser::read_string(void) {
 }
 
 
+void Parser::skip_string(void) {
+    size_t len = read_uint();
+    file->skip(len);
+}
+
+
 unsigned long long Parser::read_uint(void) {
     unsigned long long value = 0;
     int c;
@@ -450,6 +700,17 @@ unsigned long long Parser::read_uint(void) {
 }
 
 
+void Parser::skip_uint(void) {
+    int c;
+    do {
+        c = file->getc();
+        if (c == -1) {
+            break;
+        }
+    } while(c & 0x80);
+}
+
+
 inline int Parser::read_byte(void) {
     int c = file->getc();
 #if TRACE_VERBOSE
@@ -462,4 +723,9 @@ inline int Parser::read_byte(void) {
 }
 
 
+inline void Parser::skip_byte(void) {
+    file->skip(1);
+}
+
+
 } /* namespace Trace */
index 4fff9ad1157129e0da8458f8cca87e68687197ec..3aaa6d3a93f16cb71c85116185cee0d4d9cba14e 100644 (file)
 #include <iostream>
 #include <list>
 
+#include "trace_file.hpp"
 #include "trace_format.hpp"
 #include "trace_model.hpp"
 
 
 namespace Trace {
 
-class File;
+
+struct ParseBookmark
+{
+    File::Offset offset;
+    unsigned next_call_no;
+};
+
 
 class Parser
 {
 protected:
     File *file;
 
+    enum Mode {
+        FULL = 0,
+        SCAN,
+        SKIP
+    };
+
     typedef std::list<Call *> CallList;
     CallList calls;
 
-    typedef std::vector<FunctionSig *> FunctionMap;
-    FunctionMap functions;
+    // Helper template that extends a base signature structure, with additional
+    // parsing information.
+    template< class T >
+    struct SigState : public T {
+        // Offset in the file of where signature was defined.  It is used when
+        // reparsing to determine whether the signature definition is to be
+        // expected next or not.
+        File::Offset offset;
+    };
+
+    typedef SigState<FunctionSig> FunctionSigState;
+    typedef SigState<StructSig> StructSigState;
+    typedef SigState<EnumSig> EnumSigState;
+    typedef SigState<BitmaskSig> BitmaskSigState;
+
+    typedef std::vector<FunctionSigState *> FunctionMap;
+    typedef std::vector<StructSigState *> StructMap;
+    typedef std::vector<EnumSigState *> EnumMap;
+    typedef std::vector<BitmaskSigState *> BitmaskMap;
 
-    typedef std::vector<StructSig *> StructMap;
+    FunctionMap functions;
     StructMap structs;
-
-    typedef std::vector<EnumSig *> EnumMap;
     EnumMap enums;
-
-    typedef std::vector<BitmaskSig *> BitmaskMap;
     BitmaskMap bitmasks;
 
     unsigned next_call_no;
@@ -71,46 +97,98 @@ public:
 
     void close(void);
 
-    Call *parse_call(void);
+    Call *parse_call(void) {
+        return parse_call(FULL);
+    }
+
+    bool supportsOffsets() const
+    {
+        return file->supportsOffsets();
+    }
+
+    void getBookmark(ParseBookmark &bookmark);
+
+    void setBookmark(const ParseBookmark &bookmark);
+
+    int percentRead()
+    {
+        return file->percentRead();
+    }
+
+    Call *scan_call() {
+        return parse_call(SCAN);
+    }
 
 protected:
-    void parse_enter(void);
+    Call *parse_call(Mode mode);
+
+    FunctionSig *parse_function_sig(void);
+    StructSig *parse_struct_sig();
+    EnumSig *parse_enum_sig();
+    BitmaskSig *parse_bitmask_sig();
+    
+    Call *parse_Call(Mode mode);
+
+    void parse_enter(Mode mode);
 
-    Call *parse_leave(void);
+    Call *parse_leave(Mode mode);
 
-    bool parse_call_details(Call *call);
+    bool parse_call_details(Call *call, Mode mode);
 
-    void parse_arg(Call *call);
+    void parse_arg(Call *call, Mode mode);
 
     Value *parse_value(void);
+    void scan_value(void);
+    inline Value *parse_value(Mode mode) {
+        if (mode == FULL) {
+            return parse_value();
+        } else {
+            scan_value();
+            return NULL;
+        }
+    }
 
     Value *parse_sint();
+    void scan_sint();
 
     Value *parse_uint();
+    void scan_uint();
 
     Value *parse_float();
+    void scan_float();
 
     Value *parse_double();
+    void scan_double();
 
     Value *parse_string();
+    void scan_string();
 
     Value *parse_enum();
+    void scan_enum();
 
     Value *parse_bitmask();
+    void scan_bitmask();
 
     Value *parse_array(void);
+    void scan_array(void);
 
     Value *parse_blob(void);
+    void scan_blob(void);
 
     Value *parse_struct();
+    void scan_struct();
 
     Value *parse_opaque();
+    void scan_opaque();
 
     const char * read_string(void);
+    void skip_string(void);
 
     unsigned long long read_uint(void);
+    void skip_uint(void);
 
     inline int read_byte(void);
+    inline void skip_byte(void);
 };
 
 
index 6a60441aaada5015c8a41a8c35eb92fa50f4a9d3..4dbe42dc364264a8db0e717a1af17681b9af271e 100644 (file)
@@ -28,6 +28,8 @@
 
 #include <snappy.h>
 
+#include <iostream>
+
 #include <assert.h>
 #include <string.h>
 
@@ -91,6 +93,10 @@ bool SnappyFile::rawOpen(const std::string &filename, File::Mode mode)
 
     //read in the initial buffer if we're reading
     if (m_stream.is_open() && mode == File::Read) {
+        m_stream.seekg(0, std::ios::end);
+        m_endPos = m_stream.tellg();
+        m_stream.seekg(0, std::ios::beg);
+
         // read the snappy file identifier
         unsigned char byte1, byte2;
         m_stream >> byte1;
@@ -209,9 +215,10 @@ void SnappyFile::flushWriteCache()
     assert(m_cachePtr == m_cache);
 }
 
-void SnappyFile::flushReadCache()
+void SnappyFile::flushReadCache(size_t skipLength)
 {
     //assert(m_cachePtr == m_cache + m_cacheSize);
+    m_currentOffset.chunk = m_stream.tellg();
     size_t compressedLength;
     compressedLength = readCompressedLength();
 
@@ -220,8 +227,10 @@ void SnappyFile::flushReadCache()
         ::snappy::GetUncompressedLength(m_compressedCache, compressedLength,
                                         &m_cacheSize);
         createCache(m_cacheSize);
-        ::snappy::RawUncompress(m_compressedCache, compressedLength,
-                                m_cache);
+        if (skipLength < m_cacheSize) {
+            ::snappy::RawUncompress(m_compressedCache, compressedLength,
+                                    m_cache);
+        }
     } else {
         createCache(0);
     }
@@ -271,3 +280,59 @@ size_t SnappyFile::readCompressedLength()
     }
     return length;
 }
+
+bool SnappyFile::supportsOffsets() const
+{
+    return true;
+}
+
+File::Offset SnappyFile::currentOffset()
+{
+    m_currentOffset.offsetInChunk = m_cachePtr - m_cache;
+    return m_currentOffset;
+}
+
+void SnappyFile::setCurrentOffset(const File::Offset &offset)
+{
+    // to remove eof bit
+    m_stream.clear();
+    // seek to the start of a chunk
+    m_stream.seekg(offset.chunk, std::ios::beg);
+    // load the chunk
+    flushReadCache();
+    assert(m_cacheSize >= offset.offsetInChunk);
+    // seek within our cache to the correct location within the chunk
+    m_cachePtr = m_cache + offset.offsetInChunk;
+
+}
+
+bool SnappyFile::rawSkip(size_t length)
+{
+    if (endOfData()) {
+        return false;
+    }
+
+    if (freeCacheSize() >= length) {
+        m_cachePtr += length;
+    } else {
+        size_t sizeToRead = length;
+        while (sizeToRead) {
+            size_t chunkSize = std::min(freeCacheSize(), sizeToRead);
+            m_cachePtr += chunkSize;
+            sizeToRead -= chunkSize;
+            if (sizeToRead > 0) {
+                flushReadCache(sizeToRead);
+            }
+            if (!m_cacheSize) {
+                break;
+            }
+        }
+    }
+
+    return true;
+}
+
+int SnappyFile::rawPercentRead()
+{
+    return 100 * (double(m_stream.tellg()) / double(m_endPos));
+}
index b807bfbdee51e693171830535e1a6efdb2fa6ea4..33159ec73cc81eb4413158de707940a2c64c5211 100644 (file)
@@ -52,6 +52,9 @@ public:
                File::Mode mode = File::Read);
     virtual ~SnappyFile();
 
+    virtual bool supportsOffsets() const;
+    virtual File::Offset currentOffset();
+    virtual void setCurrentOffset(const File::Offset &offset);
 protected:
     virtual bool rawOpen(const std::string &filename, File::Mode mode);
     virtual bool rawWrite(const void *buffer, size_t length);
@@ -59,6 +62,8 @@ protected:
     virtual int rawGetc();
     virtual void rawClose();
     virtual void rawFlush();
+    virtual bool rawSkip(size_t length);
+    virtual int rawPercentRead();
 
 private:
     inline size_t usedCacheSize() const
@@ -80,7 +85,7 @@ private:
         return m_stream.eof() && freeCacheSize() == 0;
     }
     void flushWriteCache();
-    void flushReadCache();
+    void flushReadCache(size_t skipLength = 0);
     void createCache(size_t size);
     void writeCompressedLength(size_t length);
     size_t readCompressedLength();
@@ -91,6 +96,9 @@ private:
     size_t m_cacheSize;
 
     char *m_compressedCache;
+
+    File::Offset m_currentOffset;
+    std::streampos m_endPos;
 };
 
 }