From: Zack Rusin Date: Wed, 21 Sep 2011 04:30:58 +0000 (-0400) Subject: Merge branch 'on-demand-loading' X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=1c35c914cf934c957664d9692a099634aacc4e44;hp=5b92de64206c0a905a9758494b0fc2d72335266a;p=apitrace Merge branch 'on-demand-loading' Fixes #36 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 09bd2c4..9c91c90 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/gui/CMakeLists.txt b/gui/CMakeLists.txt index 6973450..777bc92 100644 --- a/gui/CMakeLists.txt +++ b/gui/CMakeLists.txt @@ -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 ) diff --git a/gui/apitrace.cpp b/gui/apitrace.cpp index 51c4755..bc86209 100644 --- a/gui/apitrace.cpp +++ b/gui/apitrace.cpp @@ -1,32 +1,75 @@ #include "apitrace.h" -#include "loaderthread.h" +#include "traceloader.h" #include "saverthread.h" +#include #include +#include ApiTrace::ApiTrace() : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers), m_needsSaving(false) { - m_loader = new LoaderThread(this); - connect(m_loader, SIGNAL(parsedFrames(const QList)), + 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)), this, SLOT(addFrames(const QList))); - connect(m_loader, SIGNAL(started()), + connect(m_loader, + SIGNAL(frameContentsLoaded(ApiTraceFrame*,QVector,quint64)), + this, + SLOT(loaderFrameLoaded(ApiTraceFrame*,QVector,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 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 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 &frames) { - QVector calls; int currentFrames = m_frames.count(); int numNewFrames = frames.count(); @@ -154,57 +168,20 @@ void ApiTrace::addFrames(const QList &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 &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 >::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 = "< #include -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 calls() const; - ApiTraceCall *callAt(int idx) const; ApiTraceCall *callWithIndex(int idx) const; - int numCalls() const; QList 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 &frames); void slotSaved(); + void finishedParsing(); + void loaderFrameLoaded(ApiTraceFrame *frame, + const QVector &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 m_editedCalls; @@ -101,8 +139,7 @@ private: bool m_needsSaving; QSet m_errors; - QVector m_signatures; - QVector m_enumSignatures; + QList< QPair > m_queuedErrors; }; #endif diff --git a/gui/apitracecall.cpp b/gui/apitracecall.cpp index bd30aaa..67fc955 100644 --- a/gui/apitracecall.cpp +++ b/gui/apitracecall.cpp @@ -1,6 +1,7 @@ #include "apitracecall.h" #include "apitrace.h" +#include "traceloader.h" #include "trace_model.hpp" #include @@ -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 & 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( + "Frame %1" + "   " + " " + "(%2 calls)") + .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( - "" - "Frame %1" + "%1" "" "    (%2MB)") - .arg(number) + .arg(richText) .arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2); - } else { - richText = - QObject::tr( - "Frame %1") - .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::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 &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; + } } diff --git a/gui/apitracecall.h b/gui/apitracecall.h index 33648f2..12b0216 100644 --- a/gui/apitracecall.h +++ b/gui/apitracecall.h @@ -12,12 +12,13 @@ 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 & textures() const; const QList & framebuffers() const; + ApiFramebuffer colorBuffer() const; private: QVariantMap m_parameters; QMap 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 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 calls() const; void setCalls(const QVector &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 m_calls; + bool m_loaded; + unsigned m_callsToLoad; + unsigned m_lastCallIndex; }; Q_DECLARE_METATYPE(ApiTraceFrame*); diff --git a/gui/apitracefilter.cpp b/gui/apitracefilter.cpp index 2bf7bf8..cfa930d 100644 --- a/gui/apitracefilter.cpp +++ b/gui/apitracefilter.cpp @@ -89,12 +89,6 @@ void ApiTraceFilter::setFilterOptions(ApiTraceFilter::FilterOptions opts) } } -QModelIndex ApiTraceFilter::callIndex(int callIdx) const -{ - ApiTraceModel *model = static_cast(sourceModel()); - QModelIndex index = model->callIndex(callIdx); - return mapFromSource(index); -} QModelIndex ApiTraceFilter::indexForCall(ApiTraceCall *call) const { diff --git a/gui/apitracefilter.h b/gui/apitracefilter.h index 217938c..30c92a1 100644 --- a/gui/apitracefilter.h +++ b/gui/apitracefilter.h @@ -27,7 +27,6 @@ public: void setFilterRegexp(const QRegExp ®exp); QRegExp filterRegexp() const; - QModelIndex callIndex(int callNum) const; QModelIndex indexForCall(ApiTraceCall *call) const; protected: bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; diff --git a/gui/apitracemodel.cpp b/gui/apitracemodel.cpp index 5860cf9..7303ae1 100644 --- a/gui/apitracemodel.cpp +++ b/gui/apitracemodel.cpp @@ -1,9 +1,10 @@ #include "apitracemodel.h" #include "apitracecall.h" -#include "loaderthread.h" +#include "traceloader.h" #include "trace_parser.hpp" +#include #include #include #include @@ -52,22 +53,53 @@ QVariant ApiTraceModel::data(const QModelIndex &index, int role) const .arg(call->name()) .arg(stateText); } else { + const char *htmlTempl = + "
\n" + "
\n" + "%1" + "\n" + "Frame %2\n" + "
\n" + "
%3 calls%4
\n" + "
\n"; + + ApiTraceFrame *frame = static_cast(itm); - QString text = QObject::tr("%1) Frame ") - .arg(frame->number); - int binaryDataSize = frame->binaryDataSize() / 1024; - if (!frame->hasState()) - return QObject::tr( - "%1 (binary data size = %2kB)") - .arg(text) - .arg(binaryDataSize); - else - return QObject::tr( - "%1 (binary data size = %2kB)" - "
%3") - .arg(text) - .arg(binaryDataSize) - .arg(stateText); + QString thumbStr, sizeStr; + + if (frame->hasState()) { + static const char *imgTempl = + "\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(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(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 = "<loaded(); + qDebug()<<"\tframe idx = "<number; + qDebug()<<"\tis empty = "<isEmpty(); + qDebug()<<"\tnum children = "<numChildren(); + qDebug()<<"\tindex is "< #include +#include #include 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 m_loadingFrames; }; #endif diff --git a/gui/main.cpp b/gui/main.cpp index 3f8cfa6..6043b75 100644 --- a/gui/main.cpp +++ b/gui/main.cpp @@ -1,5 +1,6 @@ #include "mainwindow.h" +#include "apitrace.h" #include "apitracecall.h" #include @@ -7,13 +8,20 @@ #include Q_DECLARE_METATYPE(QList); +Q_DECLARE_METATYPE(QVector); +Q_DECLARE_METATYPE(Qt::CaseSensitivity); +Q_DECLARE_METATYPE(ApiTrace::SearchResult); + int main(int argc, char **argv) { QApplication app(argc, argv); qRegisterMetaType >(); + qRegisterMetaType >(); qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); MainWindow window; window.show(); diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 591c557..8d7af8a 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -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 : " <arguments()[call->binaryDataIndex()].toByteArray(); m_vdataInterpreter->setData(data); - QVector args = call->arguments(); + QVector 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(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 &items) +variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap, + QList &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 &lst, const QVector &defaultLst, +variantListToItems(const QVector &lst, + const QVector &defaultLst, QList &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(); 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(); 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&)), - this, SLOT(slotRetraceErrors(const QList&))); + connect(m_retracer, SIGNAL(retraceErrors(const QList&)), + this, SLOT(slotRetraceErrors(const QList&))); 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(); - ApiTraceCall *call = 0; + ApiTraceCall *call = currentCall(); + ApiTraceFrame *frame = currentFrame(); - if (event->type() == ApiTraceCall::Call) - call = static_cast(event); - else { - Q_ASSERT(event->type() == ApiTraceCall::Frame); - ApiTraceFrame *frame = static_cast(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 &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(); - ApiTraceCall *call = 0; + ApiTraceCall *call = currentCall(); + ApiTraceFrame *frame = currentFrame(); - if (event->type() == ApiTraceCall::Call) - call = static_cast(event); - else { - Q_ASSERT(event->type() == ApiTraceCall::Frame); - ApiTraceFrame *frame = static_cast(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 &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(); - 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::const_iterator itr; - QVector 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::const_iterator itr; - QVector 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 &errors) +void MainWindow::slotRetraceErrors(const QList &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 &errors) void MainWindow::slotErrorSelected(QTreeWidgetItem *current) { if (current) { - ApiTraceCall *call = - current->data(0, Qt::UserRole).value(); - 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(); 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(); + Q_ASSERT(event); + if (!event) { + return 0; + } + + ApiTraceFrame *frame = 0; + if (event->type() == ApiTraceCall::Frame) { + frame = static_cast(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(); + Q_ASSERT(event); + if (!event) { + return 0; + } + + ApiTraceCall *call = 0; + if (event->type() == ApiTraceCall::Call) { + call = static_cast(event); + } + + return call; + +} + +void MainWindow::slotFoundFrameStart(ApiTraceFrame *frame) +{ + Q_ASSERT(frame->isLoaded()); + if (!frame || frame->isEmpty()) { + return; + } + + QVector::const_iterator itr; + QVector 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::const_iterator itr; + QVector 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" diff --git a/gui/mainwindow.h b/gui/mainwindow.h index f3e3181..0ae8ab7 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -3,6 +3,8 @@ #include "ui_mainwindow.h" +#include "apitrace.h" + #include #include @@ -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 &errors); + void slotRetraceErrors(const QList &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; diff --git a/gui/retracer.cpp b/gui/retracer.cpp index 500d859..251028c 100644 --- a/gui/retracer.cpp +++ b/gui/retracer.cpp @@ -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&)), - this, SIGNAL(retraceErrors(const QList&))); + connect(retrace, SIGNAL(retraceErrors(const QList&)), + this, SIGNAL(retraceErrors(const QList&))); retrace->start(); @@ -163,11 +163,11 @@ void RetraceProcess::replayFinished() } QStringList errorLines = errStr.split('\n'); - QList errors; + QList 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); +Q_DECLARE_METATYPE(QList); RetraceProcess::RetraceProcess(QObject *parent) : QObject(parent) { m_process = new QProcess(this); m_jsonParser = new QJson::Parser(); - qRegisterMetaType >(); + qRegisterMetaType >(); connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(replayFinished())); diff --git a/gui/retracer.h b/gui/retracer.h index 70170f5..4a43c26 100644 --- a/gui/retracer.h +++ b/gui/retracer.h @@ -1,6 +1,8 @@ #ifndef RETRACER_H #define RETRACER_H +#include "apitracecall.h" + #include #include @@ -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 &errors); + void retraceErrors(const QList &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 &errors); + void retraceErrors(const QList &errors); protected: virtual void run(); diff --git a/gui/saverthread.cpp b/gui/saverthread.cpp index 5837c99..d5f4d6b 100644 --- a/gui/saverthread.cpp +++ b/gui/saverthread.cpp @@ -1,6 +1,8 @@ #include "saverthread.h" #include "trace_writer.hpp" +#include "trace_model.hpp" +#include "trace_parser.hpp" #include #include @@ -8,7 +10,7 @@ #include - +#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(); + QVector 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 &calls) +void SaverThread::saveFile(const QString &writeFileName, + const QString &readFileName, + const QSet &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 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 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 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(); diff --git a/gui/saverthread.h b/gui/saverthread.h index 3da502c..e8c6889 100644 --- a/gui/saverthread.h +++ b/gui/saverthread.h @@ -16,8 +16,9 @@ public: SaverThread(QObject *parent=0); public slots: - void saveFile(const QString &fileName, - const QVector &calls); + void saveFile(const QString &saveFileName, + const QString &readFileName, + const QSet &editedCalls); signals: void traceSaved(); @@ -26,8 +27,9 @@ protected: virtual void run(); private: - QString m_fileName; - QVector m_calls; + QString m_readFileName; + QString m_writeFileName; + QSet m_editedCalls; }; diff --git a/gui/traceloader.cpp b/gui/traceloader.cpp new file mode 100644 index 0000000..d970c16 --- /dev/null +++ b/gui/traceloader.cpp @@ -0,0 +1,497 @@ +#include "traceloader.h" + +#include "apitrace.h" +#include +#include + +#define FRAMES_TO_CACHE 100 + +static ApiTraceCall * +apiCallFromTraceCall(const Trace::Call *call, + const QHash &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 "< 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 = "< 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 frames; + ApiTraceFrame *currentFrame = 0; + int frameCount = 0; + QVector 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 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 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 &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 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 +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 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(); +} + +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 calls = fetchFrameContents(frame); + QVector::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 index 0000000..4b88ec6 --- /dev/null +++ b/gui/traceloader.h @@ -0,0 +1,101 @@ +#ifndef TRACELOADER_H +#define TRACELOADER_H + + +#include "apitrace.h" +#include "trace_file.hpp" +#include "trace_parser.hpp" + +#include +#include +#include + +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 &frames); + void frameContentsLoaded(ApiTraceFrame *frame, + const QVector &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 fetchFrameContents(ApiTraceFrame *frame); + bool searchCallsBackwards(const QList &calls, + int frameIdx, + const QString &str, + Qt::CaseSensitivity sensitivity); + +private: + Trace::Parser m_parser; + QString m_fileName; + ApiTrace::FrameMarker m_frameMarker; + + typedef QMap FrameBookmarks; + FrameBookmarks m_frameBookmarks; + QList m_createdFrames; + + QHash m_helpHash; + + QVector m_signatures; + QVector m_enumSignatures; +}; + +#endif diff --git a/trace_file.cpp b/trace_file.cpp index 3a7691b..f48c1aa 100644 --- a/trace_file.cpp +++ b/trace_file.cpp @@ -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); +} diff --git a/trace_file.hpp b/trace_file.hpp index 8277b48..4b1b70d 100644 --- a/trace_file.hpp +++ b/trace_file.hpp @@ -29,6 +29,7 @@ #include #include +#include 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 index 0000000..c14d815 --- /dev/null +++ b/trace_loader.cpp @@ -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: " <= 5) { + std::cerr << "\tPercent scanned = " + << m_parser.percentRead() + << "..."<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 Loader::frame(int idx) +{ + int numOfCalls = numberOfCallsInFrame(idx); + if (numOfCalls) { + const FrameBookmark &frameBookmark = m_frameBookmarks[idx]; + std::vector 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(); +} diff --git a/trace_loader.hpp b/trace_loader.hpp new file mode 100644 index 0000000..9f74a9b --- /dev/null +++ b/trace_loader.hpp @@ -0,0 +1,65 @@ +#ifndef TRACE_LOADER_HPP +#define TRACE_LOADER_HPP + +#include "trace_file.hpp" +#include "trace_parser.hpp" + +#include +#include +#include +#include + +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 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 FrameBookmarks; + FrameBookmarks m_frameBookmarks; +}; + +} + +#endif // TRACE_LOADER_HPP diff --git a/trace_model.cpp b/trace_model.cpp index 25dc4bb..306b9e7 100644 --- a/trace_model.cpp +++ b/trace_model.cpp @@ -42,6 +42,11 @@ Call::~Call() { } +String::~String() { + delete [] value; +} + + Struct::~Struct() { for (std::vector::iterator it = members.begin(); it != members.end(); ++it) { delete *it; diff --git a/trace_model.hpp b/trace_model.hpp index a284754..a74508e 100644 --- a/trace_model.hpp +++ b/trace_model.hpp @@ -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; diff --git a/trace_parser.cpp b/trace_parser.cpp index 44d1786..d7b20d2 100644 --- a/trace_parser.cpp +++ b/trace_parser.cpp @@ -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 &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: ("<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 */ diff --git a/trace_parser.hpp b/trace_parser.hpp index 4fff9ad..3aaa6d3 100644 --- a/trace_parser.hpp +++ b/trace_parser.hpp @@ -30,32 +30,58 @@ #include #include +#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 CallList; CallList calls; - typedef std::vector 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 FunctionSigState; + typedef SigState StructSigState; + typedef SigState EnumSigState; + typedef SigState BitmaskSigState; + + typedef std::vector FunctionMap; + typedef std::vector StructMap; + typedef std::vector EnumMap; + typedef std::vector BitmaskMap; - typedef std::vector StructMap; + FunctionMap functions; StructMap structs; - - typedef std::vector EnumMap; EnumMap enums; - - typedef std::vector 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); }; diff --git a/trace_snappyfile.cpp b/trace_snappyfile.cpp index 6a60441..4dbe42d 100644 --- a/trace_snappyfile.cpp +++ b/trace_snappyfile.cpp @@ -28,6 +28,8 @@ #include +#include + #include #include @@ -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)); +} diff --git a/trace_snappyfile.hpp b/trace_snappyfile.hpp index b807bfb..33159ec 100644 --- a/trace_snappyfile.hpp +++ b/trace_snappyfile.hpp @@ -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; }; }