trace_writer.cpp
trace_local_writer.cpp
trace_model_writer.cpp
+ trace_loader.cpp
image.cpp
image_bmp.cpp
image_pnm.cpp
add_executable (tracedump tracedump.cpp)
install (TARGETS tracedump RUNTIME DESTINATION bin)
-
##############################################################################
# API tracers
glsledit.cpp
imageviewer.cpp
jumpwidget.cpp
- loaderthread.cpp
mainwindow.cpp
main.cpp
retracer.cpp
settingsdialog.cpp
shaderssourcewidget.cpp
tracedialog.cpp
+ traceloader.cpp
traceprocess.cpp
vertexdatainterpreter.cpp
)
#include "apitrace.h"
-#include "loaderthread.h"
+#include "traceloader.h"
#include "saverthread.h"
+#include <QDebug>
#include <QDir>
+#include <QThread>
ApiTrace::ApiTrace()
: m_frameMarker(ApiTrace::FrameMarker_SwapBuffers),
m_needsSaving(false)
{
- m_loader = new LoaderThread(this);
- connect(m_loader, SIGNAL(parsedFrames(const QList<ApiTraceFrame*>)),
+ m_loader = new TraceLoader();
+
+ connect(this, SIGNAL(loadTrace(QString)),
+ m_loader, SLOT(loadTrace(QString)));
+ connect(this, SIGNAL(requestFrame(ApiTraceFrame*)),
+ m_loader, SLOT(loadFrame(ApiTraceFrame*)));
+ connect(m_loader, SIGNAL(framesLoaded(const QList<ApiTraceFrame*>)),
this, SLOT(addFrames(const QList<ApiTraceFrame*>)));
- connect(m_loader, SIGNAL(started()),
+ connect(m_loader,
+ SIGNAL(frameContentsLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)),
+ this,
+ SLOT(loaderFrameLoaded(ApiTraceFrame*,QVector<ApiTraceCall*>,quint64)));
+ connect(m_loader, SIGNAL(finishedParsing()),
+ this, SLOT(finishedParsing()));
+ connect(this, SIGNAL(loaderSearchNext(int,QString,Qt::CaseSensitivity)),
+ m_loader, SLOT(searchNext(int,QString,Qt::CaseSensitivity)));
+ connect(this, SIGNAL(loaderSearchPrev(int,QString,Qt::CaseSensitivity)),
+ m_loader, SLOT(searchPrev(int,QString,Qt::CaseSensitivity)));
+ connect(m_loader,
+ SIGNAL(searchResult(ApiTrace::SearchResult,ApiTraceCall*)),
+ this,
+ SLOT(loaderSearchResult(ApiTrace::SearchResult,ApiTraceCall*)));
+ connect(this, SIGNAL(loaderFindFrameStart(ApiTraceFrame*)),
+ m_loader, SLOT(findFrameStart(ApiTraceFrame*)));
+ connect(this, SIGNAL(loaderFindFrameEnd(ApiTraceFrame*)),
+ m_loader, SLOT(findFrameEnd(ApiTraceFrame*)));
+ connect(m_loader, SIGNAL(foundFrameStart(ApiTraceFrame*)),
+ this, SIGNAL(foundFrameStart(ApiTraceFrame*)));
+ connect(m_loader, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
+ this, SIGNAL(foundFrameEnd(ApiTraceFrame*)));
+ connect(this, SIGNAL(loaderFindCallIndex(int)),
+ m_loader, SLOT(findCallIndex(int)));
+ connect(m_loader, SIGNAL(foundCallIndex(ApiTraceCall*)),
+ this, SIGNAL(foundCallIndex(ApiTraceCall*)));
+
+
+ connect(m_loader, SIGNAL(startedParsing()),
this, SIGNAL(startedLoadingTrace()));
- connect(m_loader, SIGNAL(finished()),
+ connect(m_loader, SIGNAL(parsed(int)),
+ this, SIGNAL(loaded(int)));
+ connect(m_loader, SIGNAL(finishedParsing()),
this, SIGNAL(finishedLoadingTrace()));
+
m_saver = new SaverThread(this);
connect(m_saver, SIGNAL(traceSaved()),
this, SLOT(slotSaved()));
connect(m_saver, SIGNAL(traceSaved()),
this, SIGNAL(saved()));
+
+ m_loaderThread = new QThread();
+ m_loader->moveToThread(m_loaderThread);
+ m_loaderThread->start();
}
ApiTrace::~ApiTrace()
{
- qDeleteAll(m_calls);
+ m_loaderThread->quit();
+ m_loaderThread->deleteLater();
qDeleteAll(m_frames);
delete m_loader;
delete m_saver;
bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
ApiTrace::FrameMarker marker)
{
- if (!call)
+ if (!call) {
return false;
+ }
switch (marker) {
case FrameMarker_SwapBuffers:
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;
}
return m_frameMarker;
}
-QVector<ApiTraceCall*> ApiTrace::calls() const
-{
- return m_calls;
-}
-
-ApiTraceCall * ApiTrace::callAt(int idx) const
-{
- return m_calls.value(idx);
-}
-
-int ApiTrace::numCalls() const
-{
- return m_calls.count();
-}
-
QList<ApiTraceFrame*> ApiTrace::frames() const
{
return m_frames;
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)
if (m_fileName != name) {
m_fileName = name;
- if (m_loader->isRunning()) {
- m_loader->terminate();
- m_loader->wait();
- }
m_frames.clear();
- m_calls.clear();
m_errors.clear();
m_editedCalls.clear();
m_needsSaving = false;
emit invalidated();
- m_loader->loadFile(m_fileName);
- }
-}
-
-void ApiTrace::setFrameMarker(FrameMarker marker)
-{
- if (m_frameMarker != marker) {
- emit framesInvalidated();
-
- qDeleteAll(m_frames);
- m_frames.clear();
- detectFrames();
+ emit loadTrace(m_fileName);
}
}
void ApiTrace::addFrames(const QList<ApiTraceFrame*> &frames)
{
- QVector<ApiTraceCall*> calls;
int currentFrames = m_frames.count();
int numNewFrames = frames.count();
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;
}
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)
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()
return m_saver->isRunning();
}
-void ApiTrace::callError(ApiTraceCall *call)
+bool ApiTrace::hasErrors() const
{
- Q_ASSERT(call);
+ return !m_errors.isEmpty();
+}
- if (call->hasError())
- m_errors.insert(call);
- else
- m_errors.remove(call);
+void ApiTrace::loadFrame(ApiTraceFrame *frame)
+{
+ Q_ASSERT(!frame->isLoaded());
+ emit requestFrame(frame);
+}
- emit changed(call);
+void ApiTrace::finishedParsing()
+{
+ ApiTraceFrame *firstFrame = m_frames[0];
+ if (firstFrame && !firstFrame->isLoaded()) {
+ loadFrame(firstFrame);
+ }
}
-bool ApiTrace::hasErrors() const
+void ApiTrace::loaderFrameLoaded(ApiTraceFrame *frame,
+ const QVector<ApiTraceCall*> &calls,
+ quint64 binaryDataSize)
+{
+ Q_ASSERT(frame->numChildrenToLoad() == calls.size());
+ emit beginLoadingFrame(frame, calls.size());
+ frame->setCalls(calls, binaryDataSize);
+ emit endLoadingFrame(frame);
+
+ if (!m_queuedErrors.isEmpty()) {
+ QList< QPair<ApiTraceFrame*, ApiTraceError> >::iterator itr;
+ for (itr = m_queuedErrors.begin(); itr != m_queuedErrors.end();
+ ++itr) {
+ const ApiTraceError &error = (*itr).second;
+ if ((*itr).first == frame) {
+ ApiTraceCall *call = frame->callWithIndex(error.callIndex);
+
+ if (!call) {
+ continue;
+ }
+
+ call->setError(error.message);
+ m_queuedErrors.erase(itr);
+
+ if (call->hasError()) {
+ m_errors.insert(call);
+ } else {
+ m_errors.remove(call);
+ }
+ emit changed(call);
+ }
+ }
+ }
+}
+
+void ApiTrace::findNext(ApiTraceFrame *frame,
+ ApiTraceCall *from,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity)
{
- return !m_errors.isEmpty();
+ ApiTraceCall *foundCall = 0;
+ int frameIdx = m_frames.indexOf(frame);
+
+ if (frame->isLoaded()) {
+ foundCall = frame->findNextCall(from, str, sensitivity);
+ if (foundCall) {
+ emit findResult(SearchResult_Found, foundCall);
+ return;
+ }
+
+ //if the frame is loaded we already searched it above
+ // so skip it
+ frameIdx += 1;
+ }
+
+ for (int i = frameIdx; i < m_frames.count(); ++i) {
+ ApiTraceFrame *frame = m_frames[i];
+ if (!frame->isLoaded()) {
+ emit loaderSearchNext(i, str, sensitivity);
+ return;
+ } else {
+ ApiTraceCall *call = frame->findNextCall(0, str, sensitivity);
+ if (call) {
+ emit findResult(SearchResult_Found, call);
+ return;
+ }
+ }
+ }
+ emit findResult(SearchResult_Wrapped, 0);
}
-ApiTraceCallSignature * ApiTrace::signature(unsigned id)
+void ApiTrace::findPrev(ApiTraceFrame *frame,
+ ApiTraceCall *from,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity)
{
- if (id >= m_signatures.count()) {
- m_signatures.resize(id + 1);
- return NULL;
- } else {
- return m_signatures[id];
+ ApiTraceCall *foundCall = 0;
+ int frameIdx = m_frames.indexOf(frame);
+
+ if (frame->isLoaded()) {
+ foundCall = frame->findPrevCall(from, str, sensitivity);
+ if (foundCall) {
+ emit findResult(SearchResult_Found, foundCall);
+ return;
+ }
+
+ //if the frame is loaded we already searched it above
+ // so skip it
+ frameIdx -= 1;
+ }
+
+ for (int i = frameIdx; i >= 0; --i) {
+ ApiTraceFrame *frame = m_frames[i];
+ if (!frame->isLoaded()) {
+ emit loaderSearchPrev(i, str, sensitivity);
+ return;
+ } else {
+ ApiTraceCall *call = frame->findPrevCall(0, str, sensitivity);
+ if (call) {
+ emit findResult(SearchResult_Found, call);
+ return;
+ }
+ }
}
+ emit findResult(SearchResult_Wrapped, 0);
+}
+
+void ApiTrace::loaderSearchResult(ApiTrace::SearchResult result,
+ ApiTraceCall *call)
+{
+ //qDebug()<<"Search result = "<<result
+ // <<", call is = "<<call;
+ emit findResult(result, call);
}
-void ApiTrace::addSignature(unsigned id, ApiTraceCallSignature *signature)
+void ApiTrace::findFrameStart(ApiTraceFrame *frame)
{
- m_signatures[id] = signature;
+ if (frame->isLoaded()) {
+ emit foundFrameStart(frame);
+ } else {
+ emit loaderFindFrameStart(frame);
+ }
}
-ApiTraceEnumSignature * ApiTrace::enumSignature(unsigned id)
+void ApiTrace::findFrameEnd(ApiTraceFrame *frame)
{
- if (id >= m_enumSignatures.count()) {
- m_enumSignatures.resize(id + 1);
- return NULL;
+ if (frame->isLoaded()) {
+ emit foundFrameEnd(frame);
} else {
- return m_enumSignatures[id];
+ emit loaderFindFrameEnd(frame);
}
}
-void ApiTrace::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature)
+void ApiTrace::findCallIndex(int index)
{
- m_enumSignatures[id] = signature;
+ int frameIdx = callInFrame(index);
+ ApiTraceFrame *frame = 0;
+
+ if (frameIdx < 0) {
+ emit foundCallIndex(0);
+ return;
+ }
+
+ frame = m_frames[frameIdx];
+
+ if (frame) {
+ if (frame->isLoaded()) {
+ ApiTraceCall *call = frame->callWithIndex(index);
+ emit foundCallIndex(call);
+ } else {
+ emit loaderFindCallIndex(index);
+ }
+ }
+}
+
+int ApiTrace::callInFrame(int callIdx) const
+{
+ unsigned numCalls = 0;
+
+ for (int frameIdx = 0; frameIdx <= m_frames.size(); ++frameIdx) {
+ const ApiTraceFrame *frame = m_frames[frameIdx];
+ unsigned numCallsInFrame = frame->isLoaded()
+ ? frame->numChildren()
+ : frame->numChildrenToLoad();
+ unsigned firstCall = numCalls;
+ unsigned endCall = numCalls + numCallsInFrame;
+ if (firstCall <= callIdx && endCall > callIdx) {
+ return frameIdx;
+ }
+ numCalls = endCall;
+ }
+
+ return -1;
+}
+
+void ApiTrace::setCallError(const ApiTraceError &error)
+{
+ int frameIdx = callInFrame(error.callIndex);
+ ApiTraceFrame *frame = 0;
+
+ if (frameIdx < 0) {
+ return;
+ }
+ frame = m_frames[frameIdx];
+
+ if (frame->isLoaded()) {
+ ApiTraceCall *call = frame->callWithIndex(error.callIndex);
+ call->setError(error.message);
+ if (call->hasError()) {
+ m_errors.insert(call);
+ } else {
+ m_errors.remove(call);
+ }
+ emit changed(call);
+ } else {
+ emit requestFrame(frame);
+ m_queuedErrors.append(qMakePair(frame, error));
+ }
}
#include "apitrace.moc"
#include <QObject>
#include <QSet>
-class LoaderThread;
+class TraceLoader;
class SaverThread;
+class QThread;
class ApiTrace : public QObject
{
FrameMarker_Finish,
FrameMarker_Clear
};
+ enum SearchResult {
+ SearchResult_NotFound,
+ SearchResult_Found,
+ SearchResult_Wrapped
+ };
+
static bool isCallAFrameMarker(const ApiTraceCall *call,
FrameMarker marker);
public:
ApiTraceState defaultState() const;
- ApiTraceCallSignature *signature(unsigned id);
- void addSignature(unsigned id, ApiTraceCallSignature *signature);
-
- ApiTraceEnumSignature *enumSignature(unsigned id);
- void addEnumSignature(unsigned id, ApiTraceEnumSignature *signature);
-
-
- QVector<ApiTraceCall*> calls() const;
- ApiTraceCall *callAt(int idx) const;
ApiTraceCall *callWithIndex(int idx) const;
- int numCalls() const;
QList<ApiTraceFrame*> frames() const;
ApiTraceFrame *frameAt(int idx) const;
public slots:
void setFileName(const QString &name);
- void setFrameMarker(FrameMarker marker);
void save();
+ void loadFrame(ApiTraceFrame *frame);
+ void findNext(ApiTraceFrame *frame,
+ ApiTraceCall *call,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+ void findPrev(ApiTraceFrame *frame,
+ ApiTraceCall *call,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+ void findFrameStart(ApiTraceFrame *frame);
+ void findFrameEnd(ApiTraceFrame *frame);
+ void findCallIndex(int index);
+ void setCallError(const ApiTraceError &error);
+
signals:
+ void loadTrace(const QString &name);
+ void requestFrame(ApiTraceFrame *frame);
void startedLoadingTrace();
+ void loaded(int percent);
void finishedLoadingTrace();
void invalidated();
void framesInvalidated();
void changed(ApiTraceCall *call);
void startedSaving();
void saved();
+ void findResult(ApiTrace::SearchResult result,
+ ApiTraceCall *call);
void beginAddingFrames(int oldCount, int numAdded);
void endAddingFrames();
- void callsAdded(int oldCount, int numAdded);
+ void beginLoadingFrame(ApiTraceFrame *frame, int numAdded);
+ void endLoadingFrame(ApiTraceFrame *frame);
+ void foundFrameStart(ApiTraceFrame *frame);
+ void foundFrameEnd(ApiTraceFrame *frame);
+ void foundCallIndex(ApiTraceCall *call);
+
+signals:
+ void loaderSearchNext(int startFrame,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+ void loaderSearchPrev(int startFrame,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+ void loaderFindFrameStart(ApiTraceFrame *frame);
+ void loaderFindFrameEnd(ApiTraceFrame *frame);
+ void loaderFindCallIndex(int index);
private slots:
void addFrames(const QList<ApiTraceFrame*> &frames);
void slotSaved();
+ void finishedParsing();
+ void loaderFrameLoaded(ApiTraceFrame *frame,
+ const QVector<ApiTraceCall*> &calls,
+ quint64 binaryDataSize);
+ void loaderSearchResult(ApiTrace::SearchResult result,
+ ApiTraceCall *call);
+
private:
- void detectFrames();
+ int callInFrame(int callIdx) const;
private:
QString m_fileName;
QString m_tempFileName;
FrameMarker m_frameMarker;
- LoaderThread *m_loader;
+ TraceLoader *m_loader;
+ QThread *m_loaderThread;
SaverThread *m_saver;
QSet<ApiTraceCall*> m_editedCalls;
bool m_needsSaving;
QSet<ApiTraceCall*> m_errors;
- QVector<ApiTraceCallSignature*> m_signatures;
- QVector<ApiTraceEnumSignature*> m_enumSignatures;
+ QList< QPair<ApiTraceFrame*, ApiTraceError> > m_queuedErrors;
};
#endif
#include "apitracecall.h"
#include "apitrace.h"
+#include "traceloader.h"
#include "trace_model.hpp"
#include <QDebug>
{
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);
}
}
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),
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);
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) {
void ApiTraceCall::setError(const QString &msg)
{
if (m_error != msg) {
- ApiTrace *trace = parentTrace();
m_error = msg;
m_richText = QString();
- if (trace)
- trace->callError(this);
}
}
return 0;
}
+bool ApiTraceCall::contains(const QString &str,
+ Qt::CaseSensitivity sensitivity) const
+{
+ QString txt = searchText();
+ return txt.contains(str, sensitivity);
+}
+
+
ApiTraceFrame::ApiTraceFrame(ApiTrace *parentTrace)
: ApiTraceEvent(ApiTraceEvent::Frame),
m_parentTrace(parentTrace),
- m_binaryDataSize(0)
+ m_binaryDataSize(0),
+ m_loaded(false),
+ m_callsToLoad(0),
+ m_lastCallIndex(0)
{
}
+ApiTraceFrame::~ApiTraceFrame()
+{
+ qDeleteAll(m_calls);
+}
+
QStaticText ApiTraceFrame::staticText() const
{
if (m_staticText && !m_staticText->text().isEmpty())
return *m_staticText;
- QString richText;
+ QString richText = QObject::tr(
+ "<span style=\"font-weight:bold\">Frame %1</span>"
+ " "
+ "<span style=\"font-style:italic;font-size:small;font-weight:lighter;\"> "
+ "(%2 calls)</span>")
+ .arg(number)
+ .arg(m_loaded ? m_calls.count() : m_callsToLoad);
//mark the frame if it uploads more than a meg a frame
if (m_binaryDataSize > (1024*1024)) {
richText =
QObject::tr(
- "<span style=\"font-weight:bold;\">"
- "Frame %1</span>"
+ "%1"
"<span style=\"font-style:italic;\">"
" (%2MB)</span>")
- .arg(number)
+ .arg(richText)
.arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2);
- } else {
- richText =
- QObject::tr(
- "<span style=\"font-weight:bold\">Frame %1</span>")
- .arg(number);
}
if (!m_staticText)
return m_calls.value(idx);
}
+
+ApiTraceCall * ApiTraceFrame::callWithIndex(int index) const
+{
+ QVector<ApiTraceCall*>::const_iterator itr;
+ for (itr = m_calls.constBegin(); itr != m_calls.constEnd(); ++itr) {
+ if ((*itr)->index() == index) {
+ return *itr;
+ }
+ }
+ return 0;
+}
+
int ApiTraceFrame::callIndex(ApiTraceCall *call) const
{
return m_calls.indexOf(call);
bool ApiTraceFrame::isEmpty() const
{
- return m_calls.isEmpty();
+ if (m_loaded) {
+ return m_calls.isEmpty();
+ } else {
+ return m_callsToLoad == 0;
+ }
}
int ApiTraceFrame::binaryDataSize() const
{
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;
+ }
}
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);
return m_variant;
}
private:
- ApiTrace *m_trace;
+ TraceLoader *m_loader;
QVariant m_variant;
};
+
+struct ApiTraceError
+{
+ int callIndex;
+ QString type;
+ QString message;
+};
+
class ApiTraceEnumSignature
{
public:
const QList<ApiTexture> & textures() const;
const QList<ApiFramebuffer> & framebuffers() const;
+ ApiFramebuffer colorBuffer() const;
private:
QVariantMap m_parameters;
QMap<QString, QString> m_shaderSources;
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;
QVector<QVariant> editedValues() const;
void revert();
+ bool contains(const QString &str,
+ Qt::CaseSensitivity sensitivity) const;
+
ApiTrace *parentTrace() const;
QString toHtml() const;
class ApiTraceFrame : public ApiTraceEvent
{
public:
- ApiTraceFrame(ApiTrace *parent);
+ ApiTraceFrame(ApiTrace *parent=0);
+ ~ApiTraceFrame();
int number;
bool isEmpty() const;
+ void setParentTrace(ApiTrace *parent);
ApiTrace *parentTrace() const;
+ void setNumChildren(int num);
int numChildren() const;
+ int numChildrenToLoad() const;
QStaticText staticText() const;
int callIndex(ApiTraceCall *call) const;
ApiTraceCall *call(int idx) const;
+ ApiTraceCall *callWithIndex(int index) const;
void addCall(ApiTraceCall *call);
QVector<ApiTraceCall*> calls() const;
void setCalls(const QVector<ApiTraceCall*> &calls,
quint64 binaryDataSize);
+ ApiTraceCall *findNextCall(ApiTraceCall *from,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity) const;
+
+ ApiTraceCall *findPrevCall(ApiTraceCall *from,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity) const;
+
int binaryDataSize() const;
+
+ bool isLoaded() const;
+ void setLoaded(bool l);
+
+ void setLastCallIndex(unsigned index);
+ unsigned lastCallIndex() const;
private:
ApiTrace *m_parentTrace;
quint64 m_binaryDataSize;
QVector<ApiTraceCall*> m_calls;
+ bool m_loaded;
+ unsigned m_callsToLoad;
+ unsigned m_lastCallIndex;
};
Q_DECLARE_METATYPE(ApiTraceFrame*);
}
}
-QModelIndex ApiTraceFilter::callIndex(int callIdx) const
-{
- ApiTraceModel *model = static_cast<ApiTraceModel *>(sourceModel());
- QModelIndex index = model->callIndex(callIdx);
- return mapFromSource(index);
-}
QModelIndex ApiTraceFilter::indexForCall(ApiTraceCall *call) const
{
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;
#include "apitracemodel.h"
#include "apitracecall.h"
-#include "loaderthread.h"
+#include "traceloader.h"
#include "trace_parser.hpp"
+#include <QBuffer>
#include <QDebug>
#include <QImage>
#include <QVariant>
.arg(call->name())
.arg(stateText);
} else {
+ const char *htmlTempl =
+ "<div>\n"
+ "<div>\n"
+ "%1"
+ "<span style=\"font-weight:bold; font-size:large; vertical-align:center; padding-bottom: 30px \">\n"
+ "Frame %2</span>\n"
+ "</div>\n"
+ "<div >%3 calls%4</div>\n"
+ "</div>\n";
+
+
ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(itm);
- QString text = QObject::tr("%1) Frame ")
- .arg(frame->number);
- int binaryDataSize = frame->binaryDataSize() / 1024;
- if (!frame->hasState())
- return QObject::tr(
- "<b>%1 </b>(binary data size = %2kB)")
- .arg(text)
- .arg(binaryDataSize);
- else
- return QObject::tr(
- "<b>%1 (binary data size = %2kB)</b>"
- "<br/>%3")
- .arg(text)
- .arg(binaryDataSize)
- .arg(stateText);
+ QString thumbStr, sizeStr;
+
+ if (frame->hasState()) {
+ static const char *imgTempl =
+ "<img style=\"float:left;\" "
+ "src=\"data:image/png;base64,%1\"/>\n";
+ static const char *sizeTempl =
+ ", %1kb";
+
+ ApiFramebuffer fbo = frame->state()->colorBuffer();
+ QImage thumb = fbo.thumb();
+ if (!thumb.isNull()) {
+ QByteArray ba;
+ QBuffer buffer(&ba);
+ buffer.open(QIODevice::WriteOnly);
+ thumb.save(&buffer, "PNG");
+ thumbStr = tr(imgTempl).arg(
+ QString(buffer.data().toBase64()));
+ }
+
+ int binaryDataSize = frame->binaryDataSize() / 1024;
+ if (binaryDataSize > 0) {
+ sizeStr = tr(sizeTempl).arg(binaryDataSize);
+ }
+ }
+
+ int numCalls = frame->isLoaded()
+ ? frame->numChildren()
+ : frame->numChildrenToLoad();
+
+ return tr(htmlTempl)
+ .arg(thumbStr)
+ .arg(frame->number)
+ .arg(numCalls)
+ .arg(sizeStr);
}
}
case ApiTraceModel::EventRole:
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
}
}
-QModelIndex ApiTraceModel::callIndex(int callNum) const
-{
- ApiTraceCall *call = m_trace->callWithIndex(callNum);
- return indexForCall(call);
-}
-
QModelIndex ApiTraceModel::indexForCall(ApiTraceCall *call) const
{
if (!call) {
endInsertRows();
}
+bool ApiTraceModel::canFetchMore(const QModelIndex &parent) const
+{
+ if (parent.isValid()) {
+ ApiTraceEvent *event = item(parent);
+ if (event && event->type() == ApiTraceEvent::Frame) {
+ ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+ return !frame->isLoaded() && !m_loadingFrames.contains(frame);
+ } else
+ return false;
+ } else {
+ return false;
+ }
+}
+
+void ApiTraceModel::fetchMore(const QModelIndex &parent)
+{
+ if (parent.isValid()) {
+ ApiTraceEvent *event = item(parent);
+ if (event && event->type() == ApiTraceEvent::Frame) {
+ ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
+ QModelIndex index = createIndex(frame->number, 0, frame);
+
+ Q_ASSERT(!frame->isLoaded());
+ m_loadingFrames.insert(frame);
+
+ m_trace->loadFrame(frame);
+ }
+ }
+}
+
+void ApiTraceModel::beginLoadingFrame(ApiTraceFrame *frame, int numAdded)
+{
+ QModelIndex index = createIndex(frame->number, 0, frame);
+ beginInsertRows(index, 0, numAdded - 1);
+}
+
+void ApiTraceModel::endLoadingFrame(ApiTraceFrame *frame)
+{
+ QModelIndex index = createIndex(frame->number, 0, frame);
+#if 0
+ qDebug()<<"Frame loaded = "<<frame->loaded();
+ qDebug()<<"\tframe idx = "<<frame->number;
+ qDebug()<<"\tis empty = "<<frame->isEmpty();
+ qDebug()<<"\tnum children = "<<frame->numChildren();
+ qDebug()<<"\tindex is "<<index;
+#endif
+
+ endInsertRows();
+
+ emit dataChanged(index, index);
+
+ m_loadingFrames.remove(frame);
+}
+
#include "apitracemodel.moc"
#include <QAbstractItemModel>
#include <QModelIndex>
+#include <QSet>
#include <QVariant>
class ApiTrace;
class ApiTraceCall;
class ApiTraceEvent;
+class ApiTraceFrame;
class ApiTraceModel : public QAbstractItemModel
{
const ApiTrace *apiTrace() const;
void stateSetOnEvent(ApiTraceEvent *event);
- QModelIndex callIndex(int callNum) const;
QModelIndex indexForCall(ApiTraceCall *call) const;
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:
void beginAddingFrames(int oldCount, int numAdded);
void endAddingFrames();
void callChanged(ApiTraceCall *call);
+ void beginLoadingFrame(ApiTraceFrame *frame, int numAdded);
+ void endLoadingFrame(ApiTraceFrame *frame);
private:
ApiTraceEvent *item(const QModelIndex &index) const;
private:
ApiTrace *m_trace;
+ QSet<ApiTraceFrame*> m_loadingFrames;
};
#endif
#include "mainwindow.h"
+#include "apitrace.h"
#include "apitracecall.h"
#include <QApplication>
#include <QVariant>
Q_DECLARE_METATYPE(QList<ApiTraceFrame*>);
+Q_DECLARE_METATYPE(QVector<ApiTraceCall*>);
+Q_DECLARE_METATYPE(Qt::CaseSensitivity);
+Q_DECLARE_METATYPE(ApiTrace::SearchResult);
+
int main(int argc, char **argv)
{
QApplication app(argc, argv);
qRegisterMetaType<QList<ApiTraceFrame*> >();
+ qRegisterMetaType<QVector<ApiTraceCall*> >();
qRegisterMetaType<ApiTraceState>();
+ qRegisterMetaType<Qt::CaseSensitivity>();
+ qRegisterMetaType<ApiTrace::SearchResult>();
MainWindow window;
window.show();
void MainWindow::createTrace()
{
- TraceDialog dialog;
-
if (!m_traceProcess->canTrace()) {
QMessageBox::warning(
this,
return;
}
+ TraceDialog dialog;
if (dialog.exec() == QDialog::Accepted) {
qDebug()<< "App : " <<dialog.applicationPath();
qDebug()<< " Arguments: "<<dialog.arguments();
void MainWindow::openTrace()
{
QString fileName =
- QFileDialog::getOpenFileName(
- this,
- tr("Open Trace"),
- QDir::homePath(),
- tr("Trace Files (*.trace)"));
+ QFileDialog::getOpenFileName(
+ this,
+ tr("Open Trace"),
+ QDir::homePath(),
+ tr("Trace Files (*.trace)"));
if (!fileName.isEmpty() && QFile::exists(fileName)) {
newTraceFile(fileName);
QByteArray data =
call->arguments()[call->binaryDataIndex()].toByteArray();
m_vdataInterpreter->setData(data);
- QVector<QVariant> args = call->arguments();
+ QVector<QVariant> args = call->arguments();
for (int i = 0; i < call->argNames().count(); ++i) {
QString name = call->argNames()[i];
if (name == QLatin1String("stride")) {
} 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);
+ }
}
}
}
} else {
if (event && event->type() == ApiTraceEvent::Frame) {
m_selectedEvent = static_cast<ApiTraceFrame*>(event);
- } else
+ } else {
m_selectedEvent = 0;
+ }
m_ui.detailsDock->hide();
m_ui.vertexDataDock->hide();
}
if (m_selectedEvent && m_selectedEvent->hasState()) {
fillStateForFrame();
- } else
+ } else {
m_ui.stateDock->hide();
+ }
}
void MainWindow::replayStart()
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);
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);
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;
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()
}
static QTreeWidgetItem *
-variantToItem(const QString &key, const QVariant &var, const QVariant &defaultVar);
+variantToItem(const QString &key, const QVariant &var,
+ const QVariant &defaultVar);
static void
-variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap, QList<QTreeWidgetItem *> &items)
+variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap,
+ QList<QTreeWidgetItem *> &items)
{
QVariantMap::const_iterator itr;
for (itr = map.constBegin(); itr != map.constEnd(); ++itr) {
}
static void
-variantListToItems(const QVector<QVariant> &lst, const QVector<QVariant> &defaultLst,
+variantListToItems(const QVector<QVariant> &lst,
+ const QVector<QVariant> &defaultLst,
QList<QTreeWidgetItem *> &items)
{
for (int i = 0; i < lst.count(); ++i) {
}
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;
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)
void MainWindow::fillStateForFrame()
{
- if (!m_selectedEvent || !m_selectedEvent->hasState())
+ if (!m_selectedEvent || !m_selectedEvent->hasState()) {
return;
+ }
if (m_nonDefaultsLookupEvent) {
m_ui.nonDefaultsCB->blockSignals(true);
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 =
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 =
{
QTreeWidget *tree = m_ui.surfacesTreeWidget;
QTreeWidgetItem *item = tree->itemAt(pos);
- if (!item)
+ if (!item) {
return;
+ }
QMenu menu(tr("Surfaces"), this);
QTreeWidgetItem *item =
m_ui.surfacesTreeWidget->currentItem();
- if (!item)
+ if (!item) {
return;
+ }
- QVariant var = item->data(0, Qt::UserRole);
- QImage img = var.value<QImage>();
ImageViewer *viewer = new ImageViewer(this);
QString title;
- if (currentCall()) {
+ if (selectedCall()) {
title = tr("QApiTrace - Surface at %1 (%2)")
- .arg(currentCall()->name())
- .arg(currentCall()->index());
+ .arg(selectedCall()->name())
+ .arg(selectedCall()->index());
} else {
title = tr("QApiTrace - Surface Viewer");
}
viewer->setWindowTitle(title);
+
viewer->setAttribute(Qt::WA_DeleteOnClose, true);
+
+ QVariant var = item->data(0, Qt::UserRole);
+ QImage img = var.value<QImage>();
viewer->setImage(img);
+
QRect screenRect = QApplication::desktop()->availableGeometry();
viewer->resize(qMin(int(0.75 * screenRect.width()), img.width()) + 40,
qMin(int(0.75 * screenRect.height()), img.height()) + 40);
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();
{
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()),
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&)));
this, SLOT(replayError(const QString&)));
connect(m_retracer, SIGNAL(foundState(ApiTraceState*)),
this, SLOT(replayStateFound(ApiTraceState*)));
- connect(m_retracer, SIGNAL(retraceErrors(const QList<RetraceError>&)),
- this, SLOT(slotRetraceErrors(const QList<RetraceError>&)));
+ connect(m_retracer, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
+ this, SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
m_vdataInterpreter, SLOT(interpretData()));
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)
void MainWindow::slotSearchNext(const QString &str,
Qt::CaseSensitivity sensitivity)
{
- QModelIndex index = m_ui.callView->currentIndex();
- ApiTraceEvent *event = 0;
-
-
- if (!index.isValid()) {
- index = m_proxyModel->index(0, 0, QModelIndex());
- if (!index.isValid()) {
- qDebug()<<"no currently valid index";
- m_searchWidget->setFound(false);
- return;
- }
- }
-
- event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
- ApiTraceCall *call = 0;
+ ApiTraceCall *call = currentCall();
+ ApiTraceFrame *frame = currentFrame();
- if (event->type() == ApiTraceCall::Call)
- call = static_cast<ApiTraceCall*>(event);
- else {
- Q_ASSERT(event->type() == ApiTraceCall::Frame);
- ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
- call = frame->call(0);
+ Q_ASSERT(call || frame);
+ if (!frame) {
+ frame = call->parentFrame();
}
+ Q_ASSERT(frame);
- if (!call) {
- m_searchWidget->setFound(false);
- return;
- }
- const QVector<ApiTraceCall*> &calls = m_trace->calls();
- int callNum = calls.indexOf(call);
-
- for (int i = callNum + 1; i < calls.count(); ++i) {
- ApiTraceCall *testCall = calls[i];
- QModelIndex index = m_proxyModel->indexForCall(testCall);
- /* if it's not valid it means that the proxy model has already
- * filtered it out */
- if (index.isValid()) {
- QString txt = testCall->searchText();
- if (txt.contains(str, sensitivity)) {
- m_ui.callView->setCurrentIndex(index);
- m_searchWidget->setFound(true);
- return;
- }
- }
- }
- m_searchWidget->setFound(false);
+ m_trace->findNext(frame, call, str, sensitivity);
}
void MainWindow::slotSearchPrev(const QString &str,
Qt::CaseSensitivity sensitivity)
{
- QModelIndex index = m_ui.callView->currentIndex();
- ApiTraceEvent *event = 0;
-
-
- if (!index.isValid()) {
- index = m_proxyModel->index(0, 0, QModelIndex());
- if (!index.isValid()) {
- qDebug()<<"no currently valid index";
- m_searchWidget->setFound(false);
- return;
- }
- }
-
- event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
- ApiTraceCall *call = 0;
+ ApiTraceCall *call = currentCall();
+ ApiTraceFrame *frame = currentFrame();
- if (event->type() == ApiTraceCall::Call)
- call = static_cast<ApiTraceCall*>(event);
- else {
- Q_ASSERT(event->type() == ApiTraceCall::Frame);
- ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(event);
- call = frame->call(0);
+ Q_ASSERT(call || frame);
+ if (!frame) {
+ frame = call->parentFrame();
}
+ Q_ASSERT(frame);
- if (!call) {
- m_searchWidget->setFound(false);
- return;
- }
- const QVector<ApiTraceCall*> &calls = m_trace->calls();
- int callNum = calls.indexOf(call);
-
- for (int i = callNum - 1; i >= 0; --i) {
- ApiTraceCall *testCall = calls[i];
- QModelIndex index = m_proxyModel->indexForCall(testCall);
- /* if it's not valid it means that the proxy model has already
- * filtered it out */
- if (index.isValid()) {
- QString txt = testCall->searchText();
- if (txt.contains(str, sensitivity)) {
- m_ui.callView->setCurrentIndex(index);
- m_searchWidget->setFound(true);
- return;
- }
- }
- }
- m_searchWidget->setFound(false);
+ m_trace->findPrev(frame, call, str, sensitivity);
}
void MainWindow::fillState(bool nonDefaults)
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;
}
void MainWindow::customContextMenuRequested(QPoint pos)
{
- QMenu menu;
QModelIndex index = m_ui.callView->indexAt(pos);
callItemSelected(index);
- if (!index.isValid())
+ if (!index.isValid()) {
return;
+ }
ApiTraceEvent *event =
index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
- if (!event)
+ if (!event) {
return;
+ }
+ QMenu menu;
menu.addAction(QIcon(":/resources/media-record.png"),
tr("Lookup state"), this, SLOT(lookupState()));
if (event->type() == ApiTraceEvent::Call) {
void MainWindow::slotGoFrameStart()
{
ApiTraceFrame *frame = currentFrame();
- if (!frame || frame->isEmpty()) {
- return;
- }
-
- QVector<ApiTraceCall*>::const_iterator itr;
- QVector<ApiTraceCall*> calls = frame->calls();
+ ApiTraceCall *call = currentCall();
- itr = calls.constBegin();
- while (itr != calls.constEnd()) {
- ApiTraceCall *call = *itr;
- QModelIndex idx = m_proxyModel->indexForCall(call);
- if (idx.isValid()) {
- m_ui.callView->setCurrentIndex(idx);
- break;
- }
- ++itr;
+ if (!frame && call) {
+ frame = call->parentFrame();
}
+
+ m_trace->findFrameStart(frame);
}
void MainWindow::slotGoFrameEnd()
{
ApiTraceFrame *frame = currentFrame();
- if (!frame || frame->isEmpty()) {
- return;
+ ApiTraceCall *call = currentCall();
+
+ if (!frame && call) {
+ frame = call->parentFrame();
}
- QVector<ApiTraceCall*>::const_iterator itr;
- QVector<ApiTraceCall*> calls = frame->calls();
- itr = calls.constEnd();
- do {
- --itr;
- ApiTraceCall *call = *itr;
- QModelIndex idx = m_proxyModel->indexForCall(call);
- if (idx.isValid()) {
- m_ui.callView->setCurrentIndex(idx);
- break;
- }
- } while (itr != calls.constBegin());
+ m_trace->findFrameEnd(frame);
}
-ApiTraceFrame * MainWindow::currentFrame() const
+ApiTraceFrame * MainWindow::selectedFrame() const
{
if (m_selectedEvent) {
if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
}
}
-void MainWindow::slotRetraceErrors(const QList<RetraceError> &errors)
+void MainWindow::slotRetraceErrors(const QList<ApiTraceError> &errors)
{
m_ui.errorsTreeWidget->clear();
- foreach(RetraceError error, errors) {
- ApiTraceCall *call = m_trace->callWithIndex(error.callIndex);
- if (!call)
- continue;
- call->setError(error.message);
+ foreach(ApiTraceError error, errors) {
+ m_trace->setCallError(error);
QTreeWidgetItem *item =
new QTreeWidgetItem(m_ui.errorsTreeWidget);
item->setData(0, Qt::DisplayRole, error.callIndex);
- item->setData(0, Qt::UserRole, QVariant::fromValue(call));
+ item->setData(0, Qt::UserRole, error.callIndex);
QString type = error.type;
type[0] = type[0].toUpper();
item->setData(1, Qt::DisplayRole, type);
void MainWindow::slotErrorSelected(QTreeWidgetItem *current)
{
if (current) {
- ApiTraceCall *call =
- current->data(0, Qt::UserRole).value<ApiTraceCall*>();
- Q_ASSERT(call);
- QModelIndex index = m_proxyModel->indexForCall(call);
- if (index.isValid()) {
- m_ui.callView->setCurrentIndex(index);
- } else {
- statusBar()->showMessage(tr("Call has been filtered out."));
- }
+ int callIndex =
+ current->data(0, Qt::UserRole).toInt();
+ m_trace->findCallIndex(callIndex);
}
}
-ApiTraceCall * MainWindow::currentCall() const
+ApiTraceCall * MainWindow::selectedCall() const
{
if (m_selectedEvent &&
m_selectedEvent->type() == ApiTraceEvent::Call) {
QTreeWidgetItem *item =
m_ui.surfacesTreeWidget->currentItem();
- if (!item || !m_trace)
+ if (!item || !m_trace) {
return;
+ }
QVariant var = item->data(0, Qt::UserRole);
QImage img = var.value<QImage>();
QString imageIndex;
- if (currentCall()) {
+ if (selectedCall()) {
imageIndex = tr("_call_%1")
- .arg(currentCall()->index());
- } else if (currentFrame()) {
- ApiTraceCall *firstCall = currentFrame()->call(0);
+ .arg(selectedCall()->index());
+ } else if (selectedFrame()) {
+ ApiTraceCall *firstCall = selectedFrame()->call(0);
if (firstCall) {
imageIndex = tr("_frame_%1")
.arg(firstCall->index());
statusBar()->showMessage( tr("Saved '%1'").arg(fileName), 5000);
}
+void MainWindow::loadProgess(int percent)
+{
+ m_progressBar->setValue(percent);
+}
+
+void MainWindow::slotSearchResult(ApiTrace::SearchResult result,
+ ApiTraceCall *call)
+{
+ switch (result) {
+ case ApiTrace::SearchResult_NotFound:
+ m_searchWidget->setFound(false);
+ break;
+ case ApiTrace::SearchResult_Found: {
+ QModelIndex index = m_proxyModel->indexForCall(call);
+ m_ui.callView->setCurrentIndex(index);
+ m_searchWidget->setFound(true);
+ }
+ break;
+ case ApiTrace::SearchResult_Wrapped:
+ m_searchWidget->setFound(false);
+ break;
+ }
+}
+
+ApiTraceFrame * MainWindow::currentFrame() const
+{
+ QModelIndex index = m_ui.callView->currentIndex();
+ ApiTraceEvent *event = 0;
+
+ if (!index.isValid()) {
+ index = m_proxyModel->index(0, 0, QModelIndex());
+ if (!index.isValid()) {
+ qDebug()<<"no currently valid index";
+ return 0;
+ }
+ }
+
+ event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+ Q_ASSERT(event);
+ if (!event) {
+ return 0;
+ }
+
+ ApiTraceFrame *frame = 0;
+ if (event->type() == ApiTraceCall::Frame) {
+ frame = static_cast<ApiTraceFrame*>(event);
+ }
+ return frame;
+}
+
+ApiTraceCall * MainWindow::currentCall() const
+{
+ QModelIndex index = m_ui.callView->currentIndex();
+ ApiTraceEvent *event = 0;
+
+ if (!index.isValid()) {
+ index = m_proxyModel->index(0, 0, QModelIndex());
+ if (!index.isValid()) {
+ qDebug()<<"no currently valid index";
+ return 0;
+ }
+ }
+
+ event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+ Q_ASSERT(event);
+ if (!event) {
+ return 0;
+ }
+
+ ApiTraceCall *call = 0;
+ if (event->type() == ApiTraceCall::Call) {
+ call = static_cast<ApiTraceCall*>(event);
+ }
+
+ return call;
+
+}
+
+void MainWindow::slotFoundFrameStart(ApiTraceFrame *frame)
+{
+ Q_ASSERT(frame->isLoaded());
+ if (!frame || frame->isEmpty()) {
+ return;
+ }
+
+ QVector<ApiTraceCall*>::const_iterator itr;
+ QVector<ApiTraceCall*> calls = frame->calls();
+
+ itr = calls.constBegin();
+ while (itr != calls.constEnd()) {
+ ApiTraceCall *call = *itr;
+ QModelIndex idx = m_proxyModel->indexForCall(call);
+ if (idx.isValid()) {
+ m_ui.callView->setCurrentIndex(idx);
+ break;
+ }
+ ++itr;
+ }
+}
+
+void MainWindow::slotFoundFrameEnd(ApiTraceFrame *frame)
+{
+ Q_ASSERT(frame->isLoaded());
+ if (!frame || frame->isEmpty()) {
+ return;
+ }
+ QVector<ApiTraceCall*>::const_iterator itr;
+ QVector<ApiTraceCall*> calls = frame->calls();
+
+ itr = calls.constEnd();
+ do {
+ --itr;
+ ApiTraceCall *call = *itr;
+ QModelIndex idx = m_proxyModel->indexForCall(call);
+ if (idx.isValid()) {
+ m_ui.callView->setCurrentIndex(idx);
+ break;
+ }
+ } while (itr != calls.constBegin());
+}
+
+void MainWindow::slotJumpToResult(ApiTraceCall *call)
+{
+ QModelIndex index = m_proxyModel->indexForCall(call);
+ if (index.isValid()) {
+ m_ui.callView->setCurrentIndex(index);
+ } else {
+ statusBar()->showMessage(tr("Call has been filtered out."));
+ }
+}
+
#include "mainwindow.moc"
#include "ui_mainwindow.h"
+#include "apitrace.h"
+
#include <QMainWindow>
#include <QProcess>
class QProgressBar;
class QTreeWidgetItem;
class QUrl;
-struct RetraceError;
+struct ApiTraceError;
class Retracer;
class SearchWidget;
class ShadersSourceWidget;
void replayStateFound(ApiTraceState *state);
void replayError(const QString &msg);
void startedLoadingTrace();
+ void loadProgess(int percent);
void finishedLoadingTrace();
void lookupState();
void showSettings();
void slotGoFrameStart();
void slotGoFrameEnd();
void slotTraceChanged(ApiTraceCall *call);
- void slotRetraceErrors(const QList<RetraceError> &errors);
+ void slotRetraceErrors(const QList<ApiTraceError> &errors);
void slotErrorSelected(QTreeWidgetItem *current);
+ void slotSearchResult(ApiTrace::SearchResult result,
+ ApiTraceCall *call);
+ void slotFoundFrameStart(ApiTraceFrame *frame);
+ void slotFoundFrameEnd(ApiTraceFrame *frame);
+ void slotJumpToResult(ApiTraceCall *call);
private:
void initObjects();
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;
this, SIGNAL(error(const QString&)));
connect(retrace, SIGNAL(foundState(ApiTraceState*)),
this, SIGNAL(foundState(ApiTraceState*)));
- connect(retrace, SIGNAL(retraceErrors(const QList<RetraceError>&)),
- this, SIGNAL(retraceErrors(const QList<RetraceError>&)));
+ connect(retrace, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
+ this, SIGNAL(retraceErrors(const QList<ApiTraceError>&)));
retrace->start();
}
QStringList errorLines = errStr.split('\n');
- QList<RetraceError> errors;
+ QList<ApiTraceError> errors;
QRegExp regexp("(^\\d+): +(\\b\\w+\\b): (.+$)");
foreach(QString line, errorLines) {
if (regexp.indexIn(line) != -1) {
- RetraceError error;
+ ApiTraceError error;
error.callIndex = regexp.cap(1).toInt();
error.type = regexp.cap(2);
error.message = regexp.cap(3);
tr("Couldn't execute the replay file '%1'").arg(m_fileName));
}
-Q_DECLARE_METATYPE(QList<RetraceError>);
+Q_DECLARE_METATYPE(QList<ApiTraceError>);
RetraceProcess::RetraceProcess(QObject *parent)
: QObject(parent)
{
m_process = new QProcess(this);
m_jsonParser = new QJson::Parser();
- qRegisterMetaType<QList<RetraceError> >();
+ qRegisterMetaType<QList<ApiTraceError> >();
connect(m_process, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(replayFinished()));
#ifndef RETRACER_H
#define RETRACER_H
+#include "apitracecall.h"
+
#include <QThread>
#include <QProcess>
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
void finished(const QString &output);
void error(const QString &msg);
void foundState(ApiTraceState *state);
- void retraceErrors(const QList<RetraceError> &errors);
+ void retraceErrors(const QList<ApiTraceError> &errors);
private slots:
void replayFinished();
void finished(const QString &output);
void foundState(ApiTraceState *state);
void error(const QString &msg);
- void retraceErrors(const QList<RetraceError> &errors);
+ void retraceErrors(const QList<ApiTraceError> &errors);
protected:
virtual void run();
#include "saverthread.h"
#include "trace_writer.hpp"
+#include "trace_model.hpp"
+#include "trace_parser.hpp"
#include <QFile>
#include <QHash>
#include <QDebug>
-
+#if 0
static Trace::FunctionSig *
createFunctionSig(ApiTraceCall *call, unsigned id)
{
}
}
}
+#endif
+
+class EditVisitor : public Trace::Visitor
+{
+public:
+ EditVisitor(const QVariant &variant)
+ : m_variant(variant),
+ m_editedValue(0)
+ {}
+ virtual void visit(Trace::Null *val)
+ {
+ m_editedValue = val;
+ }
+
+ virtual void visit(Trace::Bool *node)
+ {
+// Q_ASSERT(m_variant.userType() == QVariant::Bool);
+ bool var = m_variant.toBool();
+ m_editedValue = new Trace::Bool(var);
+ }
+
+ virtual void visit(Trace::SInt *node)
+ {
+// Q_ASSERT(m_variant.userType() == QVariant::Int);
+ m_editedValue = new Trace::SInt(m_variant.toInt());
+ }
+
+ virtual void visit(Trace::UInt *node)
+ {
+// Q_ASSERT(m_variant.userType() == QVariant::UInt);
+ m_editedValue = new Trace::SInt(m_variant.toUInt());
+ }
+
+ virtual void visit(Trace::Float *node)
+ {
+ m_editedValue = new Trace::Float(m_variant.toFloat());
+ }
+
+ virtual void visit(Trace::String *node)
+ {
+ QString str = m_variant.toString();
+ m_editedValue = new Trace::String(str.toLocal8Bit().constData());
+ }
+
+ virtual void visit(Trace::Enum *e)
+ {
+ m_editedValue = e;
+ }
+
+ virtual void visit(Trace::Bitmask *bitmask)
+ {
+ m_editedValue = bitmask;
+ }
+
+ virtual void visit(Trace::Struct *str)
+ {
+ m_editedValue = str;
+ }
+
+ virtual void visit(Trace::Array *array)
+ {
+ ApiArray apiArray = m_variant.value<ApiArray>();
+ QVector<QVariant> vals = apiArray.values();
+
+ Trace::Array *newArray = new Trace::Array(vals.count());
+ for (int i = 0; i < vals.count(); ++i) {
+ EditVisitor visitor(vals[i]);
+
+ array->values[i]->visit(visitor);
+ if (array->values[i] == visitor.value()) {
+ //non-editabled
+ delete newArray;
+ m_editedValue = array;
+ return;
+ }
+
+ newArray->values.push_back(visitor.value());
+ }
+ m_editedValue = newArray;
+ }
+
+ virtual void visit(Trace::Blob *blob)
+ {
+ m_editedValue = blob;
+ }
+
+ virtual void visit(Trace::Pointer *ptr)
+ {
+ m_editedValue = ptr;
+ }
+
+ Trace::Value *value() const
+ {
+ return m_editedValue;
+ }
+private:
+ QVariant m_variant;
+ Trace::Value *m_editedValue;
+};
+
+static void
+overwriteValue(Trace::Call *call, const QVariant &val, int index)
+{
+ EditVisitor visitor(val);
+ Trace::Value *origValue = call->args[index];
+ origValue->visit(visitor);
+
+ if (visitor.value() && origValue != visitor.value()) {
+ delete origValue;
+ call->args[index] = visitor.value();
+ }
+}
SaverThread::SaverThread(QObject *parent)
: QThread(parent)
{
}
-void SaverThread::saveFile(const QString &fileName,
- const QVector<ApiTraceCall*> &calls)
+void SaverThread::saveFile(const QString &writeFileName,
+ const QString &readFileName,
+ const QSet<ApiTraceCall*> &editedCalls)
{
- m_fileName = fileName;
- m_calls = calls;
+ m_writeFileName = writeFileName;
+ m_readFileName = readFileName;
+ m_editedCalls = editedCalls;
start();
}
void SaverThread::run()
{
- unsigned id = 0;
- qDebug() << "Saving : " << m_fileName;
+ qDebug() << "Saving " << m_readFileName
+ << ", to " << m_writeFileName;
+ QMap<int, ApiTraceCall*> callIndexMap;
+
+ foreach(ApiTraceCall *call, m_editedCalls) {
+ callIndexMap.insert(call->index(), call);
+ }
+
Trace::Writer writer;
- writer.open(m_fileName.toLocal8Bit());
- for (int i = 0; i < m_calls.count(); ++i) {
- ApiTraceCall *call = m_calls[i];
- Trace::FunctionSig *funcSig = createFunctionSig(call, ++id);
- unsigned callNo = writer.beginEnter(funcSig);
- {
- //args
- QVector<QVariant> vars = call->arguments();
- int index = 0;
- foreach(QVariant var, vars) {
- writer.beginArg(index++);
- writeValue(writer, var, ++id);
- writer.endArg();
- }
- }
- writer.endEnter();
- writer.beginLeave(callNo);
- {
- QVariant ret = call->returnValue();
- if (!ret.isNull()) {
- writer.beginReturn();
- writeValue(writer, ret, ++id);
- writer.endReturn();
+ writer.open(m_writeFileName.toLocal8Bit());
+
+ Trace::Parser parser;
+ parser.open(m_readFileName.toLocal8Bit());
+
+ Trace::Call *call;
+ while ((call = parser.parse_call())) {
+ if (callIndexMap.contains(call->no)) {
+ QVector<QVariant> values = callIndexMap[call->no]->editedValues();
+ for (int i = 0; i < values.count(); ++i) {
+ const QVariant &val = values[i];
+ overwriteValue(call, val, i);
}
+ writer.writeCall(call);
+ } else {
+ writer.writeCall(call);
}
- writer.endLeave();
-
- deleteFunctionSig(funcSig);
}
+
writer.close();
emit traceSaved();
SaverThread(QObject *parent=0);
public slots:
- void saveFile(const QString &fileName,
- const QVector<ApiTraceCall*> &calls);
+ void saveFile(const QString &saveFileName,
+ const QString &readFileName,
+ const QSet<ApiTraceCall*> &editedCalls);
signals:
void traceSaved();
virtual void run();
private:
- QString m_fileName;
- QVector<ApiTraceCall*> m_calls;
+ QString m_readFileName;
+ QString m_writeFileName;
+ QSet<ApiTraceCall*> m_editedCalls;
};
--- /dev/null
+#include "traceloader.h"
+
+#include "apitrace.h"
+#include <QDebug>
+#include <QFile>
+
+#define FRAMES_TO_CACHE 100
+
+static ApiTraceCall *
+apiCallFromTraceCall(const Trace::Call *call,
+ const QHash<QString, QUrl> &helpHash,
+ ApiTraceFrame *frame,
+ TraceLoader *loader)
+{
+ ApiTraceCall *apiCall = new ApiTraceCall(frame, loader, call);
+
+ apiCall->setHelpUrl(helpHash.value(apiCall->name()));
+
+ return apiCall;
+}
+
+TraceLoader::TraceLoader(QObject *parent)
+ : QObject(parent),
+ m_frameMarker(ApiTrace::FrameMarker_SwapBuffers)
+{
+}
+
+TraceLoader::~TraceLoader()
+{
+ m_parser.close();
+ qDeleteAll(m_signatures);
+ qDeleteAll(m_enumSignatures);
+}
+
+void TraceLoader::loadTrace(const QString &filename)
+{
+ if (m_helpHash.isEmpty()) {
+ loadHelpFile();
+ }
+
+ if (!m_parser.open(filename.toLatin1())) {
+ qDebug() << "error: failed to open " << filename;
+ return;
+ }
+ qDebug()<<"load trace with "<<filename;
+ emit startedParsing();
+
+ qDebug() <<"\t support offsets = "<<m_parser.supportsOffsets();
+ if (m_parser.supportsOffsets()) {
+ scanTrace();
+ } else {
+ //Load the entire file into memory
+ parseTrace();
+ }
+
+ emit finishedParsing();
+}
+
+void TraceLoader::loadFrame(ApiTraceFrame *currentFrame)
+{
+ fetchFrameContents(currentFrame);
+}
+
+void TraceLoader::setFrameMarker(ApiTrace::FrameMarker marker)
+{
+ m_frameMarker = marker;
+}
+
+bool TraceLoader::isCallAFrameMarker(const Trace::Call *call) const
+{
+ std::string name = call->name();
+
+ switch (m_frameMarker) {
+ case ApiTrace::FrameMarker_SwapBuffers:
+ return name.find("SwapBuffers") != std::string::npos ||
+ name == "CGLFlushDrawable" ||
+ name == "glFrameTerminatorGREMEDY";
+ break;
+ case ApiTrace::FrameMarker_Flush:
+ return name == "glFlush";
+ break;
+ case ApiTrace::FrameMarker_Finish:
+ return name == "glFinish";
+ break;
+ case ApiTrace::FrameMarker_Clear:
+ return name == "glClear";
+ break;
+ }
+ return false;
+}
+
+int TraceLoader::numberOfFrames() const
+{
+ return m_frameBookmarks.size();
+}
+
+int TraceLoader::numberOfCallsInFrame(int frameIdx) const
+{
+ if (frameIdx > m_frameBookmarks.size()) {
+ return 0;
+ }
+ FrameBookmarks::const_iterator itr =
+ m_frameBookmarks.find(frameIdx);
+ return itr->numberOfCalls;
+}
+
+void TraceLoader::loadHelpFile()
+{
+ QFile file(":/resources/glreference.tsv");
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ QString line;
+ while (!file.atEnd()) {
+ line = file.readLine();
+ QString function = line.section('\t', 0, 0).trimmed();
+ QUrl url = QUrl(line.section('\t', 1, 1).trimmed());
+ //qDebug()<<"function = "<<function<<", url = "<<url.toString();
+ m_helpHash.insert(function, url);
+ }
+ } else {
+ qWarning() << "Couldn't open reference file "
+ << file.fileName();
+ }
+ file.close();
+}
+
+void TraceLoader::scanTrace()
+{
+ QList<ApiTraceFrame*> frames;
+ ApiTraceFrame *currentFrame = 0;
+
+ Trace::Call *call;
+ Trace::ParseBookmark startBookmark;
+ int numOfFrames = 0;
+ int numOfCalls = 0;
+ int lastPercentReport = 0;
+
+ m_parser.getBookmark(startBookmark);
+
+ while ((call = m_parser.scan_call())) {
+ ++numOfCalls;
+
+ if (isCallAFrameMarker(call)) {
+ FrameBookmark frameBookmark(startBookmark);
+ frameBookmark.numberOfCalls = numOfCalls;
+
+ currentFrame = new ApiTraceFrame();
+ currentFrame->number = numOfFrames;
+ currentFrame->setNumChildren(numOfCalls);
+ currentFrame->setLastCallIndex(call->no);
+ frames.append(currentFrame);
+
+ m_createdFrames.append(currentFrame);
+ m_frameBookmarks[numOfFrames] = frameBookmark;
+ ++numOfFrames;
+
+ if (m_parser.percentRead() - lastPercentReport >= 5) {
+ emit parsed(m_parser.percentRead());
+ lastPercentReport = m_parser.percentRead();
+ }
+ m_parser.getBookmark(startBookmark);
+ numOfCalls = 0;
+ }
+ delete call;
+ }
+
+ if (numOfCalls) {
+ //Trace::File::Bookmark endBookmark = m_parser.currentBookmark();
+ FrameBookmark frameBookmark(startBookmark);
+ frameBookmark.numberOfCalls = numOfCalls;
+
+ currentFrame = new ApiTraceFrame();
+ currentFrame->number = numOfFrames;
+ currentFrame->setNumChildren(numOfCalls);
+ frames.append(currentFrame);
+
+ m_createdFrames.append(currentFrame);
+ m_frameBookmarks[numOfFrames] = frameBookmark;
+ ++numOfFrames;
+ }
+
+ emit parsed(100);
+
+ emit framesLoaded(frames);
+}
+
+void TraceLoader::parseTrace()
+{
+ QList<ApiTraceFrame*> frames;
+ ApiTraceFrame *currentFrame = 0;
+ int frameCount = 0;
+ QVector<ApiTraceCall*> calls;
+ quint64 binaryDataSize = 0;
+
+ int lastPercentReport = 0;
+
+ Trace::Call *call = m_parser.parse_call();
+ while (call) {
+ //std::cout << *call;
+ if (!currentFrame) {
+ currentFrame = new ApiTraceFrame();
+ currentFrame->number = frameCount;
+ ++frameCount;
+ }
+ ApiTraceCall *apiCall =
+ apiCallFromTraceCall(call, m_helpHash, currentFrame, this);
+ calls.append(apiCall);
+ if (apiCall->hasBinaryData()) {
+ QByteArray data =
+ apiCall->arguments()[apiCall->binaryDataIndex()].toByteArray();
+ binaryDataSize += data.size();
+ }
+ if (ApiTrace::isCallAFrameMarker(apiCall,
+ m_frameMarker)) {
+ calls.squeeze();
+ currentFrame->setCalls(calls, binaryDataSize);
+ calls.clear();
+ frames.append(currentFrame);
+ currentFrame = 0;
+ binaryDataSize = 0;
+ if (frames.count() >= FRAMES_TO_CACHE) {
+ emit framesLoaded(frames);
+ frames.clear();
+ }
+ if (m_parser.percentRead() - lastPercentReport >= 5) {
+ emit parsed(m_parser.percentRead());
+ lastPercentReport = m_parser.percentRead();
+ }
+ }
+ delete call;
+ call = m_parser.parse_call();
+ }
+
+ //last frames won't have markers
+ // it's just a bunch of Delete calls for every object
+ // after the last SwapBuffers
+ if (currentFrame) {
+ calls.squeeze();
+ currentFrame->setCalls(calls, binaryDataSize);
+ frames.append(currentFrame);
+ currentFrame = 0;
+ }
+ if (frames.count()) {
+ emit framesLoaded(frames);
+ }
+}
+
+
+ApiTraceCallSignature * TraceLoader::signature(unsigned id)
+{
+ if (id >= m_signatures.count()) {
+ m_signatures.resize(id + 1);
+ return NULL;
+ } else {
+ return m_signatures[id];
+ }
+}
+
+void TraceLoader::addSignature(unsigned id, ApiTraceCallSignature *signature)
+{
+ m_signatures[id] = signature;
+}
+
+ApiTraceEnumSignature * TraceLoader::enumSignature(unsigned id)
+{
+ if (id >= m_enumSignatures.count()) {
+ m_enumSignatures.resize(id + 1);
+ return NULL;
+ } else {
+ return m_enumSignatures[id];
+ }
+}
+
+void TraceLoader::addEnumSignature(unsigned id, ApiTraceEnumSignature *signature)
+{
+ m_enumSignatures[id] = signature;
+}
+
+void TraceLoader::searchNext(int startFrame,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity)
+{
+ Q_ASSERT(m_parser.supportsOffsets());
+ if (m_parser.supportsOffsets()) {
+ const FrameBookmark &frameBookmark = m_frameBookmarks[startFrame];
+ m_parser.setBookmark(frameBookmark.start);
+ Trace::Call *call = 0;
+ while ((call = m_parser.parse_call())) {
+
+ if (callContains(call, str, sensitivity)) {
+ unsigned frameIdx = callInFrame(call->no);
+ ApiTraceFrame *frame = m_createdFrames[frameIdx];
+ const QVector<ApiTraceCall*> calls =
+ fetchFrameContents(frame);
+ for (int i = 0; i < calls.count(); ++i) {
+ if (calls[i]->index() == call->no) {
+ emit searchResult(ApiTrace::SearchResult_Found, calls[i]);
+ break;
+ }
+ }
+ delete call;
+ return;
+ }
+
+ delete call;
+ }
+ }
+ emit searchResult(ApiTrace::SearchResult_NotFound, 0);
+}
+
+void TraceLoader::searchPrev(int startFrame,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity)
+{
+ Q_ASSERT(m_parser.supportsOffsets());
+ if (m_parser.supportsOffsets()) {
+ Trace::Call *call = 0;
+ QList<Trace::Call*> frameCalls;
+ int frameIdx = startFrame;
+
+ const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
+ int numCallsToParse = frameBookmark.numberOfCalls;
+ m_parser.setBookmark(frameBookmark.start);
+
+ while ((call = m_parser.parse_call())) {
+
+ frameCalls.append(call);
+ --numCallsToParse;
+
+ if (numCallsToParse == 0) {
+ bool foundCall = searchCallsBackwards(frameCalls,
+ frameIdx,
+ str, sensitivity);
+
+ qDeleteAll(frameCalls);
+ frameCalls.clear();
+ if (foundCall) {
+ return;
+ }
+
+ --frameIdx;
+
+ if (frameIdx >= 0) {
+ const FrameBookmark &frameBookmark =
+ m_frameBookmarks[frameIdx];
+ m_parser.setBookmark(frameBookmark.start);
+ numCallsToParse = frameBookmark.numberOfCalls;
+ }
+ }
+ }
+ }
+ emit searchResult(ApiTrace::SearchResult_NotFound, 0);
+}
+
+bool TraceLoader::searchCallsBackwards(const QList<Trace::Call*> &calls,
+ int frameIdx,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity)
+{
+ for (int i = calls.count() - 1; i >= 0; --i) {
+ Trace::Call *call = calls[i];
+ if (callContains(call, str, sensitivity)) {
+ ApiTraceFrame *frame = m_createdFrames[frameIdx];
+ const QVector<ApiTraceCall*> apiCalls =
+ fetchFrameContents(frame);
+ for (int i = 0; i < apiCalls.count(); ++i) {
+ if (apiCalls[i]->index() == call->no) {
+ emit searchResult(ApiTrace::SearchResult_Found, apiCalls[i]);
+ break;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+int TraceLoader::callInFrame(int callIdx) const
+{
+ unsigned numCalls = 0;
+
+ for (int frameIdx = 0; frameIdx <= m_frameBookmarks.size(); ++frameIdx) {
+ const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
+ unsigned firstCall = numCalls;
+ unsigned endCall = numCalls + frameBookmark.numberOfCalls;
+ if (firstCall <= callIdx && endCall > callIdx) {
+ return frameIdx;
+ }
+ numCalls = endCall;
+ }
+ Q_ASSERT(!"call not in the trace");
+ return 0;
+}
+
+bool TraceLoader::callContains(Trace::Call *call,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity)
+{
+ /*
+ * FIXME: do string comparison directly on Trace::Call
+ */
+ ApiTraceCall *apiCall = apiCallFromTraceCall(call, m_helpHash,
+ 0, this);
+ bool result = apiCall->contains(str, sensitivity);
+ delete apiCall;
+ return result;
+}
+
+QVector<ApiTraceCall*>
+TraceLoader::fetchFrameContents(ApiTraceFrame *currentFrame)
+{
+ Q_ASSERT(currentFrame);
+
+ if (currentFrame->isLoaded()) {
+ return currentFrame->calls();
+ }
+
+ if (m_parser.supportsOffsets()) {
+ unsigned frameIdx = currentFrame->number;
+ int numOfCalls = numberOfCallsInFrame(frameIdx);
+
+ if (numOfCalls) {
+ quint64 binaryDataSize = 0;
+ QVector<ApiTraceCall*> calls(numOfCalls);
+ const FrameBookmark &frameBookmark = m_frameBookmarks[frameIdx];
+
+ m_parser.setBookmark(frameBookmark.start);
+
+ Trace::Call *call;
+ int parsedCalls = 0;
+ while ((call = m_parser.parse_call())) {
+ ApiTraceCall *apiCall =
+ apiCallFromTraceCall(call, m_helpHash,
+ currentFrame, this);
+ calls[parsedCalls] = apiCall;
+ Q_ASSERT(calls[parsedCalls]);
+ if (apiCall->hasBinaryData()) {
+ QByteArray data =
+ apiCall->arguments()[
+ apiCall->binaryDataIndex()].toByteArray();
+ binaryDataSize += data.size();
+ }
+
+ ++parsedCalls;
+
+ delete call;
+
+ if (ApiTrace::isCallAFrameMarker(apiCall, m_frameMarker)) {
+ break;
+ }
+
+ }
+ assert(parsedCalls == numOfCalls);
+ Q_ASSERT(parsedCalls == calls.size());
+ calls.squeeze();
+
+ Q_ASSERT(parsedCalls == currentFrame->numChildrenToLoad());
+ emit frameContentsLoaded(currentFrame,
+ calls, binaryDataSize);
+ return calls;
+ }
+ }
+ return QVector<ApiTraceCall*>();
+}
+
+void TraceLoader::findFrameStart(ApiTraceFrame *frame)
+{
+ if (!frame->isLoaded()) {
+ loadFrame(frame);
+ }
+ emit foundFrameStart(frame);
+}
+
+void TraceLoader::findFrameEnd(ApiTraceFrame *frame)
+{
+ if (!frame->isLoaded()) {
+ loadFrame(frame);
+ }
+ emit foundFrameEnd(frame);
+}
+
+void TraceLoader::findCallIndex(int index)
+{
+ int frameIdx = callInFrame(index);
+ ApiTraceFrame *frame = m_createdFrames[frameIdx];
+ QVector<ApiTraceCall*> calls = fetchFrameContents(frame);
+ QVector<ApiTraceCall*>::const_iterator itr;
+ ApiTraceCall *call = 0;
+ for (itr = calls.constBegin(); itr != calls.constEnd(); ++itr) {
+ if ((*itr)->index() == index) {
+ call = *itr;
+ }
+ }
+ Q_ASSERT(call);
+ emit foundCallIndex(call);
+}
+
+#include "traceloader.moc"
--- /dev/null
+#ifndef TRACELOADER_H
+#define TRACELOADER_H
+
+
+#include "apitrace.h"
+#include "trace_file.hpp"
+#include "trace_parser.hpp"
+
+#include <QObject>
+#include <QList>
+#include <QMap>
+
+class TraceLoader : public QObject
+{
+ Q_OBJECT
+public:
+ TraceLoader(QObject *parent=0);
+ ~TraceLoader();
+
+
+ ApiTraceCallSignature *signature(unsigned id);
+ void addSignature(unsigned id, ApiTraceCallSignature *signature);
+
+ ApiTraceEnumSignature *enumSignature(unsigned id);
+ void addEnumSignature(unsigned id, ApiTraceEnumSignature *signature);
+
+public slots:
+ void loadTrace(const QString &filename);
+ void loadFrame(ApiTraceFrame *frame);
+ void setFrameMarker(ApiTrace::FrameMarker marker);
+ void searchNext(int startFrame,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+ void searchPrev(int startFrame,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+ void findFrameStart(ApiTraceFrame *frame);
+ void findFrameEnd(ApiTraceFrame *frame);
+ void findCallIndex(int index);
+
+signals:
+ void startedParsing();
+ void parsed(int percent);
+ void finishedParsing();
+
+ void framesLoaded(const QList<ApiTraceFrame*> &frames);
+ void frameContentsLoaded(ApiTraceFrame *frame,
+ const QVector<ApiTraceCall*> &calls,
+ quint64 binaryDataSize);
+
+ void searchResult(ApiTrace::SearchResult result, ApiTraceCall *call);
+ void foundFrameStart(ApiTraceFrame *frame);
+ void foundFrameEnd(ApiTraceFrame *frame);
+ void foundCallIndex(ApiTraceCall *call);
+private:
+ struct FrameBookmark {
+ FrameBookmark()
+ : numberOfCalls(0)
+ {}
+ FrameBookmark(const Trace::ParseBookmark &s)
+ : start(s),
+ numberOfCalls(0)
+ {}
+
+ Trace::ParseBookmark start;
+ int numberOfCalls;
+ };
+ bool isCallAFrameMarker(const Trace::Call *call) const;
+ int numberOfFrames() const;
+ int numberOfCallsInFrame(int frameIdx) const;
+
+ void loadHelpFile();
+ void scanTrace();
+ void parseTrace();
+
+ int callInFrame(int callIdx) const;
+ bool callContains(Trace::Call *call,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+ QVector<ApiTraceCall*> fetchFrameContents(ApiTraceFrame *frame);
+ bool searchCallsBackwards(const QList<Trace::Call*> &calls,
+ int frameIdx,
+ const QString &str,
+ Qt::CaseSensitivity sensitivity);
+
+private:
+ Trace::Parser m_parser;
+ QString m_fileName;
+ ApiTrace::FrameMarker m_frameMarker;
+
+ typedef QMap<int, FrameBookmark> FrameBookmarks;
+ FrameBookmarks m_frameBookmarks;
+ QList<ApiTraceFrame*> m_createdFrames;
+
+ QHash<QString, QUrl> m_helpHash;
+
+ QVector<ApiTraceCallSignature*> m_signatures;
+ QVector<ApiTraceEnumSignature*> m_enumSignatures;
+};
+
+#endif
close();
}
+
+void File::setCurrentOffset(const File::Offset &offset)
+{
+ assert(0);
+}
+
bool File::isZLibCompressed(const std::string &filename)
{
std::fstream stream(filename.c_str(),
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)
{
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;
}
{
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);
+}
#include <string>
#include <fstream>
+#include <stdint.h>
namespace Trace {
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);
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;
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;
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) {
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);
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
--- /dev/null
+#include "trace_loader.hpp"
+
+
+using namespace Trace;
+
+Loader::Loader()
+ : m_frameMarker(FrameMarker_SwapBuffers)
+{
+}
+
+Loader::~Loader()
+{
+ close();
+}
+
+Loader::FrameMarker Loader::frameMarker() const
+{
+ return m_frameMarker;
+}
+
+void Loader::setFrameMarker(Loader::FrameMarker marker)
+{
+ m_frameMarker = marker;
+}
+
+int Loader::numberOfFrames() const
+{
+ return m_frameBookmarks.size();
+}
+
+int Loader::numberOfCallsInFrame(int frameIdx) const
+{
+ if (frameIdx > m_frameBookmarks.size()) {
+ return 0;
+ }
+ FrameBookmarks::const_iterator itr =
+ m_frameBookmarks.find(frameIdx);
+ return itr->second.numberOfCalls;
+}
+
+bool Loader::open(const char *filename)
+{
+ if (!m_parser.open(filename)) {
+ std::cerr << "error: failed to open " << filename << "\n";
+ return false;
+ }
+ if (!m_parser.supportsOffsets()) {
+ std::cerr << "error: " <<filename<< " doesn't support seeking "
+ << "\n";
+ return false;
+ }
+
+ Trace::Call *call;
+ ParseBookmark startBookmark;
+ int numOfFrames = 0;
+ int numOfCalls = 0;
+ int lastPercentReport = 0;
+
+ m_parser.getBookmark(startBookmark);
+
+ while ((call = m_parser.scan_call())) {
+ ++numOfCalls;
+
+ if (isCallAFrameMarker(call)) {
+ FrameBookmark frameBookmark(startBookmark);
+ frameBookmark.numberOfCalls = numOfCalls;
+
+ m_frameBookmarks[numOfFrames] = frameBookmark;
+ ++numOfFrames;
+
+ if (m_parser.percentRead() - lastPercentReport >= 5) {
+ std::cerr << "\tPercent scanned = "
+ << m_parser.percentRead()
+ << "..."<<std::endl;
+ lastPercentReport = m_parser.percentRead();
+ }
+
+ m_parser.getBookmark(startBookmark);
+ numOfCalls = 0;
+ }
+ //call->dump(std::cout, color);
+ delete call;
+ }
+ return true;
+}
+
+void Loader::close()
+{
+ m_parser.close();
+}
+
+bool Loader::isCallAFrameMarker(const Trace::Call *call) const
+{
+ std::string name = call->name();
+
+ switch (m_frameMarker) {
+ case FrameMarker_SwapBuffers:
+ return name.find("SwapBuffers") != std::string::npos ||
+ name == "CGLFlushDrawable" ||
+ name == "glFrameTerminatorGREMEDY";
+ break;
+ case FrameMarker_Flush:
+ return name == "glFlush";
+ break;
+ case FrameMarker_Finish:
+ return name == "glFinish";
+ break;
+ case FrameMarker_Clear:
+ return name == "glClear";
+ break;
+ }
+ return false;
+}
+
+std::vector<Trace::Call *> Loader::frame(int idx)
+{
+ int numOfCalls = numberOfCallsInFrame(idx);
+ if (numOfCalls) {
+ const FrameBookmark &frameBookmark = m_frameBookmarks[idx];
+ std::vector<Trace::Call*> calls(numOfCalls);
+ m_parser.setBookmark(frameBookmark.start);
+
+ Trace::Call *call;
+ int parsedCalls = 0;
+ while ((call = m_parser.parse_call())) {
+
+ calls[parsedCalls] = call;
+ ++parsedCalls;
+
+ if (isCallAFrameMarker(call)) {
+ break;
+ }
+
+ }
+ assert(parsedCalls == numOfCalls);
+ return calls;
+ }
+ return std::vector<Trace::Call*>();
+}
--- /dev/null
+#ifndef TRACE_LOADER_HPP
+#define TRACE_LOADER_HPP
+
+#include "trace_file.hpp"
+#include "trace_parser.hpp"
+
+#include <string>
+#include <map>
+#include <queue>
+#include <vector>
+
+namespace Trace {
+
+class Frame;
+
+class Loader
+{
+public:
+ enum FrameMarker {
+ FrameMarker_SwapBuffers,
+ FrameMarker_Flush,
+ FrameMarker_Finish,
+ FrameMarker_Clear
+ };
+public:
+ Loader();
+ ~Loader();
+
+ Loader::FrameMarker frameMarker() const;
+ void setFrameMarker(Loader::FrameMarker marker);
+
+ int numberOfFrames() const;
+ int numberOfCallsInFrame(int frameIdx) const;
+
+ bool open(const char *filename);
+ void close();
+
+ std::vector<Trace::Call*> frame(int idx);
+
+private:
+ struct FrameBookmark {
+ FrameBookmark()
+ : numberOfCalls(0)
+ {}
+ FrameBookmark(const ParseBookmark &s)
+ : start(s),
+ numberOfCalls(0)
+ {}
+
+ ParseBookmark start;
+ int numberOfCalls;
+ };
+ bool isCallAFrameMarker(const Trace::Call *call) const;
+
+private:
+ Trace::Parser m_parser;
+ FrameMarker m_frameMarker;
+
+ typedef std::map<int, FrameBookmark> FrameBookmarks;
+ FrameBookmarks m_frameBookmarks;
+};
+
+}
+
+#endif // TRACE_LOADER_HPP
}
+String::~String() {
+ delete [] value;
+}
+
+
Struct::~Struct() {
for (std::vector<Value *>::iterator it = members.begin(); it != members.end(); ++it) {
delete *it;
{
public:
String(const char * _value) : value(_value) {}
+ ~String();
bool toBool(void) const;
const char *toString(void) const;
}
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);
}
-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();
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;
}
-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) {
return NULL;
}
- if (parse_call_details(call)) {
+ if (parse_call_details(call, mode)) {
return call;
} else {
delete call;
}
-bool Parser::parse_call_details(Call *call) {
+bool Parser::parse_call_details(Call *call, Mode mode) {
do {
int c = read_byte();
switch(c) {
case Trace::CALL_END:
return true;
case Trace::CALL_ARG:
- parse_arg(call);
+ parse_arg(call, mode);
break;
case Trace::CALL_RET:
- call->ret = parse_value();
+ call->ret = parse_value(mode);
break;
default:
std::cerr << "error: ("<<call->name()<< ") unknown call detail "
}
-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;
}
}
+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);
}
+void Parser::scan_float() {
+ file->skip(sizeof(float));
+}
+
+
Value *Parser::parse_double() {
double value;
file->read(&value, sizeof value);
}
+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();
}
+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);
}
+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);
}
-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) {
}
+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();
}
+void Parser::scan_opaque() {
+ skip_uint();
+}
+
+
const char * Parser::read_string(void) {
size_t len = read_uint();
char * value = new char[len + 1];
}
+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;
}
+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
}
+inline void Parser::skip_byte(void) {
+ file->skip(1);
+}
+
+
} /* namespace Trace */
#include <iostream>
#include <list>
+#include "trace_file.hpp"
#include "trace_format.hpp"
#include "trace_model.hpp"
namespace Trace {
-class File;
+
+struct ParseBookmark
+{
+ File::Offset offset;
+ unsigned next_call_no;
+};
+
class Parser
{
protected:
File *file;
+ enum Mode {
+ FULL = 0,
+ SCAN,
+ SKIP
+ };
+
typedef std::list<Call *> CallList;
CallList calls;
- typedef std::vector<FunctionSig *> FunctionMap;
- FunctionMap functions;
+ // Helper template that extends a base signature structure, with additional
+ // parsing information.
+ template< class T >
+ struct SigState : public T {
+ // Offset in the file of where signature was defined. It is used when
+ // reparsing to determine whether the signature definition is to be
+ // expected next or not.
+ File::Offset offset;
+ };
+
+ typedef SigState<FunctionSig> FunctionSigState;
+ typedef SigState<StructSig> StructSigState;
+ typedef SigState<EnumSig> EnumSigState;
+ typedef SigState<BitmaskSig> BitmaskSigState;
+
+ typedef std::vector<FunctionSigState *> FunctionMap;
+ typedef std::vector<StructSigState *> StructMap;
+ typedef std::vector<EnumSigState *> EnumMap;
+ typedef std::vector<BitmaskSigState *> BitmaskMap;
- typedef std::vector<StructSig *> StructMap;
+ FunctionMap functions;
StructMap structs;
-
- typedef std::vector<EnumSig *> EnumMap;
EnumMap enums;
-
- typedef std::vector<BitmaskSig *> BitmaskMap;
BitmaskMap bitmasks;
unsigned next_call_no;
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);
};
#include <snappy.h>
+#include <iostream>
+
#include <assert.h>
#include <string.h>
//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;
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();
::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);
}
}
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));
+}
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);
virtual int rawGetc();
virtual void rawClose();
virtual void rawFlush();
+ virtual bool rawSkip(size_t length);
+ virtual int rawPercentRead();
private:
inline size_t usedCacheSize() const
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();
size_t m_cacheSize;
char *m_compressedCache;
+
+ File::Offset m_currentOffset;
+ std::streampos m_endPos;
};
}