set (glws glws_glx.cpp)
endif (WIN32)
-add_library (trace trace_model.cpp trace_parser.cpp ${os})
+add_library (trace trace_model.cpp trace_parser.cpp trace_write.cpp ${os})
add_executable (tracedump tracedump.cpp)
target_link_libraries (tracedump trace)
apitracecall.cpp
apitracefilter.cpp
apitracemodel.cpp
+ argumentseditor.cpp
glsledit.cpp
imageviewer.cpp
jumpwidget.cpp
mainwindow.cpp
main.cpp
retracer.cpp
+ saverthread.cpp
searchwidget.cpp
settingsdialog.cpp
shaderssourcewidget.cpp
qt4_add_resources(qapitrace_SRCS qapitrace.qrc)
set(qapitrace_UIS
+ ui/argumentseditor.ui
ui/imageviewer.ui
ui/jumpwidget.ui
ui/mainwindow.ui
#add_app_icon(qapitrace_SRCS ../icons/hi*-qapitrace.png)
link_directories(${LINK_DIRECTORIES} ${QJSON_LIBRARY_DIRS})
-include_directories(${QT_INCLUDES} ${QJSON_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/..)
+include_directories(${QT_INCLUDES} ${QJSON_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/..)
add_definitions(-DBUILD_DIR="${CMAKE_CURRENT_BINARY_DIR}/..")
ApiCallDelegate::ApiCallDelegate(QWidget *parent)
: QStyledItemDelegate(parent),
- m_stateEmblem(":/resources/dialog-information.png")
+ m_stateEmblem(":/resources/dialog-information.png"),
+ m_editEmblem(":/resources/document-edit.png")
{
}
QStyle *style = QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &option, painter, 0);
if (!event->state().isEmpty()) {
- QPixmap px = m_stateEmblem.pixmap(option.rect.height(), option.rect.height());
+ QPixmap px = m_stateEmblem.pixmap(option.rect.height(),
+ option.rect.height());
painter->drawPixmap(option.rect.topLeft(), px);
offset = QPoint(option.rect.height() + 5, 0);
}
+ if (event->type() == ApiTraceEvent::Call) {
+ ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
+ if (call->edited()) {
+ QPixmap px = m_editEmblem.pixmap(option.rect.height(),
+ option.rect.height());
+ painter->drawPixmap(option.rect.topLeft() + offset, px);
+ offset += QPoint(option.rect.height() + 5, 0);
+ }
+ }
+
painter->drawStaticText(option.rect.topLeft() + offset, text);
} else {
QStyledItemDelegate::paint(painter, option, index);
private:
QIcon m_stateEmblem;
+ QIcon m_editEmblem;
};
#endif
#include "apitrace.h"
#include "loaderthread.h"
+#include "saverthread.h"
+
+#include <QDir>
ApiTrace::ApiTrace()
- : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers)
+ : m_frameMarker(ApiTrace::FrameMarker_SwapBuffers),
+ m_needsSaving(false)
{
m_loader = new LoaderThread(this);
connect(m_loader, SIGNAL(parsedFrames(const QList<ApiTraceFrame*>)),
this, SIGNAL(startedLoadingTrace()));
connect(m_loader, SIGNAL(finished()),
this, SIGNAL(finishedLoadingTrace()));
+
+ m_saver = new SaverThread(this);
+ connect(m_saver, SIGNAL(traceSaved()),
+ this, SLOT(slotSaved()));
+ connect(m_saver, SIGNAL(traceSaved()),
+ this, SIGNAL(saved()));
}
ApiTrace::~ApiTrace()
qDeleteAll(m_calls);
qDeleteAll(m_frames);
delete m_loader;
+ delete m_saver;
}
bool ApiTrace::isCallAFrameMarker(const ApiTraceCall *call,
switch (marker) {
case FrameMarker_SwapBuffers:
- return call->name.contains(QLatin1String("SwapBuffers"));
+ return call->name().contains(QLatin1String("SwapBuffers"));
case FrameMarker_Flush:
- return call->name == QLatin1String("glFlush");
+ return call->name() == QLatin1String("glFlush");
case FrameMarker_Finish:
- return call->name == QLatin1String("glFinish");
+ return call->name() == QLatin1String("glFinish");
case FrameMarker_Clear:
- return call->name == QLatin1String("glClear");
+ return call->name() == QLatin1String("glClear");
}
Q_ASSERT(!"unknown frame marker");
QString ApiTrace::fileName() const
{
+ if (edited())
+ return m_tempFileName;
+
return m_fileName;
}
int currentCalls = m_calls.count();
int numNewCalls = 0;
foreach(ApiTraceFrame *frame, frames) {
+ frame->setParentTrace(this);
numNewCalls += frame->calls.count();
m_calls += frame->calls;
}
foreach(ApiTraceCall *apiCall, m_calls) {
if (!currentFrame) {
currentFrame = new ApiTraceFrame();
+ currentFrame->setParentTrace(this);
currentFrame->number = m_frames.count();
}
- apiCall->parentFrame = currentFrame;
+ apiCall->setParentFrame(currentFrame);
currentFrame->calls.append(apiCall);
if (ApiTrace::isCallAFrameMarker(apiCall,
m_frameMarker)) {
{
for (int i = 0; i < m_calls.count(); ++i) {
ApiTraceCall *call = m_calls[i];
- if (call->index == idx)
+ if (call->index() == idx)
return call;
}
return NULL;
return frame->state();
}
+void ApiTrace::callEdited(ApiTraceCall *call)
+{
+ if (!m_editedCalls.contains(call)) {
+ //lets generate a temp filename
+ QString tempPath = QDir::tempPath();
+ m_tempFileName = QString::fromLatin1("%1/%2.edited")
+ .arg(tempPath)
+ .arg(m_fileName);
+ }
+ m_editedCalls.insert(call);
+ m_needsSaving = true;
+
+ emit changed(call);
+}
+
+void ApiTrace::callReverted(ApiTraceCall *call)
+{
+ m_editedCalls.remove(call);
+
+ if (m_editedCalls.isEmpty()) {
+ m_needsSaving = false;
+ }
+ emit changed(call);
+}
+
+bool ApiTrace::edited() const
+{
+ return !m_editedCalls.isEmpty();
+}
+
+bool ApiTrace::needsSaving() const
+{
+ return m_needsSaving;
+}
+
+void ApiTrace::save()
+{
+ QFileInfo fi(m_tempFileName);
+ QDir dir;
+ emit startedSaving();
+ dir.mkpath(fi.absolutePath());
+ m_saver->saveFile(m_tempFileName, m_calls);
+}
+
+void ApiTrace::slotSaved()
+{
+ m_needsSaving = false;
+}
+
+bool ApiTrace::isSaving() const
+{
+ return m_saver->isRunning();
+}
+
#include "apitrace.moc"
#include "apitracecall.h"
#include <QObject>
+#include <QSet>
class LoaderThread;
+class SaverThread;
class ApiTrace : public QObject
{
int numFrames() const;
int numCallsInFrame(int idx) const;
+ void callEdited(ApiTraceCall *call);
+ void callReverted(ApiTraceCall *call);
+
+ bool edited() const;
+ bool needsSaving() const;
+
+ bool isSaving() const;
+
public slots:
void setFileName(const QString &name);
void setFrameMarker(FrameMarker marker);
+ void save();
signals:
void startedLoadingTrace();
void finishedLoadingTrace();
void invalidated();
void framesInvalidated();
+ void changed(ApiTraceCall *call);
+ void startedSaving();
+ void saved();
void framesAdded(int oldCount, int numAdded);
void callsAdded(int oldCount, int numAdded);
private slots:
void addFrames(const QList<ApiTraceFrame*> &frames);
+ void slotSaved();
private:
void detectFrames();
private:
QString m_fileName;
+ QString m_tempFileName;
QList<ApiTraceFrame*> m_frames;
QList<ApiTraceCall*> m_calls;
FrameMarker m_frameMarker;
LoaderThread *m_loader;
+ SaverThread *m_saver;
+
+ QSet<ApiTraceCall*> m_editedCalls;
+
+ bool m_needsSaving;
};
#endif
#include "apitracecall.h"
+#include "apitrace.h"
#include "trace_model.hpp"
#include <QDebug>
#include <QObject>
+#define QT_USE_FAST_OPERATOR_PLUS
+#include <QStringBuilder>
-ApiPointer::ApiPointer(int val)
+ApiPointer::ApiPointer(unsigned long long val)
: m_value(val)
{
}
if (variant.canConvert<ApiArray>()) {
return variant.value<ApiArray>().toString();
}
+ if (variant.canConvert<ApiEnum>()) {
+ return variant.value<ApiEnum>().toString();
+ }
return QString();
}
str += QLatin1String("{");
for (unsigned i = 0; i < m_members.count(); ++i) {
- str += m_sig.memberNames[i];
- str += QLatin1String(" = ");
- str += apiVariantToString(m_members[i]);
+ str += m_sig.memberNames[i] %
+ QLatin1Literal(" = ") %
+ apiVariantToString(m_members[i]);
if (i < m_members.count() - 1)
str += QLatin1String(", ");
}
void VariantVisitor::visit(Trace::Null *)
{
- m_variant = QVariant(QLatin1String("NULL"));
+ m_variant = QVariant::fromValue(ApiPointer(0));
}
void VariantVisitor::visit(Trace::Bool *node)
void VariantVisitor::visit(Trace::Enum *e)
{
- m_variant = QVariant(QString::fromStdString(e->sig->first));
+ VariantVisitor vis;
+ e->sig->second->visit(vis);
+
+ QVariant val = vis.variant();
+
+ m_variant = QVariant::fromValue(
+ ApiEnum(QString::fromStdString(e->sig->first), val));
}
void VariantVisitor::visit(Trace::Bitmask *bitmask)
void VariantVisitor::visit(Trace::Blob *blob)
{
+ //XXX
+ //FIXME: this is a nasty hack. Trace::Blob's can't
+ // delete the contents in the destructor because
+ // the data is being used by other calls. We piggy back
+ // on that assumption and don't deep copy the data. If
+ // Blob's will start deleting the data we will need to
+ // start deep copying it or switch to using something like
+ // Boost's shared_ptr or Qt's QSharedPointer to handle it
QByteArray barray = QByteArray::fromRawData(blob->buf, blob->size);
m_variant = QVariant(barray);
}
init(arr);
}
+ApiArray::ApiArray(const QList<QVariant> &vals)
+ : m_array(vals)
+{
+}
+
QString ApiArray::toString() const
{
QString str;
QStaticText ApiTraceCall::staticText() const
{
- if (!m_staticText.text().isEmpty())
- return m_staticText;
+ if (m_staticText && !m_staticText->text().isEmpty())
+ return *m_staticText;
+
+ QVariantList argValues = arguments();
- QString richText = QString::fromLatin1("<span style=\"font-weight:bold\">%1</span>(").arg(name);
- for (int i = 0; i < argNames.count(); ++i) {
+ QString richText = QString::fromLatin1(
+ "<span style=\"font-weight:bold\">%1</span>(").arg(m_name);
+ for (int i = 0; i < m_argNames.count(); ++i) {
richText += QLatin1String("<span style=\"color:#0000ff\">");
QString argText = apiVariantToString(argValues[i]);
richText += argText;
}
richText += QLatin1String("</span>");
- if (i < argNames.count() - 1)
- richText += QString::fromLatin1(", ");
+ if (i < m_argNames.count() - 1)
+ richText += QLatin1String(", ");
}
richText += QLatin1String(")");
- if (returnValue.isValid()) {
- richText += QLatin1String(" = ");
- richText += QLatin1String("<span style=\"color:#0000ff\">");
- richText += apiVariantToString(returnValue);
- richText += QLatin1String("</span>");
+ if (m_returnValue.isValid()) {
+ richText +=
+ QLatin1Literal(" = ") %
+ QLatin1Literal("<span style=\"color:#0000ff\">") %
+ apiVariantToString(m_returnValue) %
+ QLatin1Literal("</span>");
}
- m_staticText.setText(richText);
+ if (!m_staticText)
+ m_staticText = new QStaticText(richText);
+ else
+ m_staticText->setText(richText);
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
- m_staticText.setTextOption(opt);
- m_staticText.prepare();
+ m_staticText->setTextOption(opt);
+ m_staticText->prepare();
- return m_staticText;
+ return *m_staticText;
}
QString ApiTraceCall::toHtml() const
if (!m_richText.isEmpty())
return m_richText;
- if (helpUrl.isEmpty()) {
+ if (m_helpUrl.isEmpty()) {
m_richText = QString::fromLatin1(
"%1) <span style=\"font-weight:bold\">%2</span>(")
- .arg(index)
- .arg(name);
+ .arg(m_index)
+ .arg(m_name);
} else {
m_richText = QString::fromLatin1(
"%1) <span style=\"font-weight:bold\"><a href=\"%2\">%3</a></span>(")
- .arg(index)
- .arg(helpUrl.toString())
- .arg(name);
+ .arg(m_index)
+ .arg(m_helpUrl.toString())
+ .arg(m_name);
}
- for (int i = 0; i < argNames.count(); ++i) {
- m_richText += argNames[i];
- m_richText += QString::fromLatin1(" = ");
- m_richText += QLatin1String("<span style=\"color:#0000ff\">");
- m_richText += apiVariantToString(argValues[i]);
- m_richText += QLatin1String("</span>");
- if (i < argNames.count() - 1)
- m_richText += QString::fromLatin1(", ");
+ QVariantList argValues = arguments();
+ for (int i = 0; i < m_argNames.count(); ++i) {
+ m_richText += m_argNames[i] +
+ QLatin1Literal(" = ") +
+ QLatin1Literal("<span style=\"color:#0000ff\">") +
+ apiVariantToString(argValues[i]) +
+ QLatin1Literal("</span>");
+ if (i < m_argNames.count() - 1)
+ m_richText += QLatin1String(", ");
}
m_richText += QLatin1String(")");
- if (returnValue.isValid()) {
- m_richText += QLatin1String(" = ");
- m_richText += QLatin1String("<span style=\"color:#0000ff\">");
- m_richText += apiVariantToString(returnValue);
- m_richText += QLatin1String("</span>");
+ if (m_returnValue.isValid()) {
+ m_richText +=
+ QLatin1String(" = ") +
+ QLatin1String("<span style=\"color:#0000ff\">") +
+ apiVariantToString(m_returnValue) +
+ QLatin1String("</span>");
}
+ m_richText.squeeze();
return m_richText;
}
if (!m_filterText.isEmpty())
return m_filterText;
- m_filterText = name;
- m_filterText += QLatin1String("(");
- for (int i = 0; i < argNames.count(); ++i) {
- m_filterText += argNames[i];
- m_filterText += QString::fromLatin1(" = ");
-
+ QVariantList argValues = arguments();
+ m_filterText = m_name + QLatin1Literal("(");
+ for (int i = 0; i < m_argNames.count(); ++i) {
+ m_filterText += m_argNames[i] +
+ QLatin1Literal(" = ") +
+ apiVariantToString(argValues[i]);
if (argValues[i].type() == QVariant::ByteArray) {
m_hasBinaryData = true;
m_binaryDataIndex = i;
}
- m_filterText += apiVariantToString(argValues[i]);
- if (i < argNames.count() - 1)
- m_filterText += QString::fromLatin1(", ");
+ if (i < m_argNames.count() - 1)
+ m_filterText += QLatin1String(", ");
}
m_filterText += QLatin1String(")");
- if (returnValue.isValid()) {
- m_filterText += QLatin1String(" = ");
- m_filterText += apiVariantToString(returnValue);
+ if (m_returnValue.isValid()) {
+ m_filterText += QLatin1Literal(" = ") +
+ apiVariantToString(m_returnValue);
}
+ m_filterText.squeeze();
return m_filterText;
}
QStaticText ApiTraceFrame::staticText() const
{
- if (!m_staticText.text().isEmpty())
- return m_staticText;
+ if (m_staticText && !m_staticText->text().isEmpty())
+ return *m_staticText;
QString richText =
QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
- m_staticText.setText(richText);
+ if (!m_staticText)
+ m_staticText = new QStaticText(richText);
+
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
- m_staticText.setTextOption(opt);
- m_staticText.prepare();
+ m_staticText->setTextOption(opt);
+ m_staticText->prepare();
- return m_staticText;
+ return *m_staticText;
}
int ApiTraceCall::numChildren() const
}
ApiTraceFrame::ApiTraceFrame()
- : ApiTraceEvent(ApiTraceEvent::Frame)
+ : ApiTraceEvent(ApiTraceEvent::Frame),
+ m_parentTrace(0)
{
}
}
ApiTraceEvent::ApiTraceEvent()
- : m_type(ApiTraceEvent::None)
+ : m_type(ApiTraceEvent::None),
+ m_staticText(0)
{
}
ApiTraceEvent::ApiTraceEvent(Type t)
- : m_type(t)
+ : m_type(t),
+ m_staticText(0)
{
}
QVariantMap::const_iterator itr;
- for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd(); ++itr) {
+ for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
+ ++itr) {
QString type = itr.key();
QString source = itr.value().toString();
m_shaderSources[type] = source;
return m_framebuffers;
}
+QList<QVariant> ApiArray::values() const
+{
+ return m_array;
+}
+
+int ApiTraceCall::index() const
+{
+ return m_index;
+}
+
+QString ApiTraceCall::name() const
+{
+ return m_name;
+}
+
+QStringList ApiTraceCall::argNames() const
+{
+ return m_argNames;
+}
+
+QVariantList ApiTraceCall::arguments() const
+{
+ if (m_editedValues.isEmpty())
+ return m_argValues;
+ else
+ return m_editedValues;
+}
+
+QVariant ApiTraceCall::returnValue() const
+{
+ return m_returnValue;
+}
+
+QUrl ApiTraceCall::helpUrl() const
+{
+ return m_helpUrl;
+}
+
+ApiTraceCall::ApiTraceCall(const Trace::Call *call)
+ : ApiTraceEvent(ApiTraceEvent::Call),
+ m_hasBinaryData(false),
+ m_binaryDataIndex(0)
+{
+ m_name = QString::fromStdString(call->sig->name);
+ m_index = call->no;
+
+ QString argumentsText;
+ for (int i = 0; i < call->sig->arg_names.size(); ++i) {
+ m_argNames +=
+ QString::fromStdString(call->sig->arg_names[i]);
+ }
+ if (call->ret) {
+ VariantVisitor retVisitor;
+ call->ret->visit(retVisitor);
+ m_returnValue = retVisitor.variant();
+ }
+ for (int i = 0; i < call->args.size(); ++i) {
+ VariantVisitor argVisitor;
+ call->args[i]->visit(argVisitor);
+ m_argValues += argVisitor.variant();
+ }
+}
+
+void ApiTraceCall::setHelpUrl(const QUrl &url)
+{
+ m_helpUrl = url;
+}
+
+void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
+{
+ m_parentFrame = frame;
+}
+
+ApiTraceFrame * ApiTraceCall::parentFrame()const
+{
+ return m_parentFrame;
+}
+
+ApiTraceEvent::~ApiTraceEvent()
+{
+ delete m_staticText;
+}
+
+void ApiTraceCall::revert()
+{
+ setEditedValues(QVariantList());
+}
+
+ApiTrace * ApiTraceFrame::parentTrace() const
+{
+ return m_parentTrace;
+}
+
+void ApiTraceFrame::setParentTrace(ApiTrace *trace)
+{
+ m_parentTrace = trace;
+}
+
+QVariantList ApiTraceCall::originalValues() const
+{
+ return m_argValues;
+}
+
+void ApiTraceCall::setEditedValues(const QVariantList &lst)
+{
+ ApiTrace *trace = 0;
+ if (m_parentFrame)
+ trace = m_parentFrame->parentTrace();
+
+ m_editedValues = lst;
+ //lets regenerate data
+ m_richText = QString();
+ m_filterText = QString();
+ delete m_staticText;
+ m_staticText = 0;
+
+ if (trace) {
+ if (!lst.isEmpty()) {
+ trace->callEdited(this);
+ } else {
+ trace->callReverted(this);
+ }
+ }
+}
+
+QVariantList ApiTraceCall::editedValues() const
+{
+ return m_editedValues;
+}
+
+bool ApiTraceCall::edited() const
+{
+ return !m_editedValues.isEmpty();
+}
+
+ApiEnum::ApiEnum(const QString &name, const QVariant &val)
+ : m_name(name),
+ m_value(val)
+{
+}
+
+QString ApiEnum::toString() const
+{
+ return m_name;
+}
+
+QVariant ApiEnum::value() const
+{
+ return m_value;
+}
+
+QString ApiEnum::name() const
+{
+ return m_name;
+}
+
+unsigned long long ApiBitmask::value() const
+{
+ return m_value;
+}
+
+ApiBitmask::Signature ApiBitmask::signature() const
+{
+ return m_sig;
+}
+
+ApiStruct::Signature ApiStruct::signature() const
+{
+ return m_sig;
+}
+
+QList<QVariant> ApiStruct::values() const
+{
+ return m_members;
+}
+
+unsigned long long ApiPointer::value() const
+{
+ return m_value;
+}
#include "trace_model.hpp"
+class ApiTrace;
+
class VariantVisitor : public Trace::Visitor
{
public:
QVariant m_variant;
};
+class ApiEnum
+{
+public:
+ ApiEnum(const QString &name = QString(), const QVariant &val=QVariant());
+
+ QString toString() const;
+
+ QVariant value() const;
+ QString name() const;
+private:
+ QString m_name;
+ QVariant m_value;
+};
+Q_DECLARE_METATYPE(ApiEnum);
class ApiPointer
{
public:
- ApiPointer(int val=0);
+ ApiPointer(unsigned long long val=0);
QString toString() const;
+
+ unsigned long long value() const;
+
private:
- int m_value;
+ unsigned long long m_value;
};
Q_DECLARE_METATYPE(ApiPointer);
QString toString() const;
+ unsigned long long value() const;
+ Signature signature() const;
+
private:
void init(const Trace::Bitmask *bitmask);
private:
ApiStruct(const Trace::Struct *s = 0);
QString toString() const;
+ Signature signature() const;
+ QList<QVariant> values() const;
private:
void init(const Trace::Struct *bitmask);
{
public:
ApiArray(const Trace::Array *arr = 0);
+ ApiArray(const QList<QVariant> &vals);
QString toString() const;
+ QList<QVariant> values() const;
private:
void init(const Trace::Array *arr);
private:
public:
ApiTraceEvent();
ApiTraceEvent(Type t);
+ virtual ~ApiTraceEvent();
Type type() const { return m_type; }
protected:
Type m_type;
ApiTraceState m_state;
+
+ mutable QStaticText *m_staticText;
};
Q_DECLARE_METATYPE(ApiTraceEvent*);
{
public:
ApiTraceCall();
+ ApiTraceCall(const Trace::Call *tcall);
~ApiTraceCall();
- int index;
- QString name;
- QStringList argNames;
- QVariantList argValues;
- QVariant returnValue;
- ApiTraceFrame *parentFrame;
- QUrl helpUrl;
+ int index() const;
+ QString name() const;
+ QStringList argNames() const;
+ QVariantList arguments() const;
+ QVariant returnValue() const;
+ QUrl helpUrl() const;
+ void setHelpUrl(const QUrl &url);
+ ApiTraceFrame *parentFrame()const;
+ void setParentFrame(ApiTraceFrame *frame);
+
+ QVariantList originalValues() const;
+
+ bool edited() const;
+ void setEditedValues(const QVariantList &lst);
+ QVariantList editedValues() const;
+ void revert();
QString toHtml() const;
QString filterText() const;
bool hasBinaryData() const;
int binaryDataIndex() const;
private:
+ int m_index;
+ QString m_name;
+ QStringList m_argNames;
+ QVariantList m_argValues;
+ QVariant m_returnValue;
+ ApiTraceFrame *m_parentFrame;
+ QUrl m_helpUrl;
+
+ QVariantList m_editedValues;
+
mutable QString m_richText;
mutable QString m_filterText;
- mutable QStaticText m_staticText;
mutable bool m_hasBinaryData;
mutable int m_binaryDataIndex;
};
int number;
QList<ApiTraceCall*> calls;
+ ApiTrace *parentTrace() const;
+ void setParentTrace(ApiTrace *trace);
+
int numChildren() const;
QStaticText staticText() const;
private:
- mutable QStaticText m_staticText;
+ ApiTrace *m_parentTrace;
};
Q_DECLARE_METATYPE(ApiTraceFrame*);
}
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
- QString function = call->name;
+ QString function = call->name();
if (!m_regexp.isEmpty() && m_regexp.isValid()) {
return function.contains(m_regexp);
ApiTraceCall *call = static_cast<ApiTraceCall*>(itm);
if (call->state().isEmpty())
return QString::fromLatin1("%1) <b>%2</b>")
- .arg(call->index)
- .arg(call->name);
+ .arg(call->index())
+ .arg(call->name());
else
return QString::fromLatin1("%1) <b>%2</b><br/>%3")
- .arg(call->index)
- .arg(call->name)
+ .arg(call->index())
+ .arg(call->name())
.arg(stateText);
} else {
ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(itm);
ApiTraceEvent *event = item(index);
if (event && event->type() == ApiTraceEvent::Call) {
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
- Q_ASSERT(call->parentFrame);
- return createIndex(call->parentFrame->number,
- 0, call->parentFrame);
+ Q_ASSERT(call->parentFrame());
+ return createIndex(call->parentFrame()->number,
+ 0, call->parentFrame());
}
return QModelIndex();
}
this, SLOT(invalidateFrames()));
connect(m_trace, SIGNAL(framesAdded(int, int)),
this, SLOT(appendFrames(int, int)));
+ connect(m_trace, SIGNAL(changed(ApiTraceCall*)),
+ this, SLOT(callChanged(ApiTraceCall*)));
}
const ApiTrace * ApiTraceModel::apiTrace() const
if (event->type() == ApiTraceEvent::Call) {
ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
- ApiTraceFrame *frame = call->parentFrame;
+ ApiTraceFrame *frame = call->parentFrame();
int row = frame->calls.indexOf(call);
QModelIndex index = createIndex(row, 0, call);
emit dataChanged(index, index);
return QModelIndex();
}
- ApiTraceFrame *frame = call->parentFrame;
+ ApiTraceFrame *frame = call->parentFrame();
Q_ASSERT(frame);
int row = frame->calls.indexOf(call);
if (row < 0) {
- qDebug() << "Couldn't find call num "<<call->index<<" inside parent!";
+ qDebug() << "Couldn't find call num "<<call->index()<<" inside parent!";
return QModelIndex();
}
return createIndex(row, 0, call);
}
+void ApiTraceModel::callChanged(ApiTraceCall *call)
+{
+ ApiTrace *trace = call->parentFrame()->parentTrace();
+
+ Q_ASSERT(trace);
+ trace->save();
+
+#if 0
+ qDebug()<<"Call changed = "<<call->edited();
+ qDebug()<<"\ttrace edited = "<<trace->edited();
+ qDebug()<<"\ttrace file = "<<trace->fileName();
+ qDebug()<<"\ttrace needs saving = "<<trace->needsSaving();
+#endif
+
+ ApiTraceFrame *frame = call->parentFrame();
+ int row = frame->calls.indexOf(call);
+ QModelIndex index = createIndex(row, 0, call);
+ emit dataChanged(index, index);
+}
+
#include "apitracemodel.moc"
private slots:
void invalidateFrames();
void appendFrames(int oldCount, int numAdded);
+ void callChanged(ApiTraceCall *call);
private:
ApiTraceEvent *item(const QModelIndex &index) const;
--- /dev/null
+#include "argumentseditor.h"
+
+#include "apitracecall.h"
+
+#include <QDebug>
+#include <QDoubleSpinBox>
+#include <QItemEditorFactory>
+#include <QSpinBox>
+
+#include <limits.h>
+#include <float.h>
+
+
+static bool
+isVariantEditable(const QVariant &var)
+{
+ switch (var.userType()) {
+ case QVariant::Bool:
+ case QVariant::Int:
+ case QVariant::UInt:
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::Double:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool
+isVariantStringArray(const QVariant &var)
+{
+ if (var.isNull() || var.userType() != QMetaType::type("ApiArray"))
+ return false;
+
+ ApiArray array = var.value<ApiArray>();
+ QList<QVariant> origValues = array.values();
+ if (origValues.isEmpty() ||
+ origValues.first().userType() != QVariant::String)
+ return false;
+
+ return true;
+}
+
+ArgumentsItemEditorFactory::ArgumentsItemEditorFactory()
+ : QItemEditorFactory()
+{
+}
+
+QWidget * ArgumentsItemEditorFactory::createEditor(QVariant::Type type,
+ QWidget *parent) const
+{
+ switch (type) {
+ case QVariant::Bool: {
+ BooleanComboBox *cb = new BooleanComboBox(parent);
+ cb->setFrame(false);
+ return cb;
+ }
+ case QVariant::UInt: {
+ QSpinBox *sb = new QSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMaximum(INT_MAX);
+ return sb; }
+ case QVariant::Int: {
+ QSpinBox *sb = new QSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMinimum(INT_MIN);
+ sb->setMaximum(INT_MAX);
+ return sb;
+ }
+ case QVariant::ULongLong: {
+ QSpinBox *sb = new QSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMaximum(INT_MAX);
+ return sb; }
+ case QVariant::LongLong: {
+ QSpinBox *sb = new QSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMinimum(INT_MIN);
+ sb->setMaximum(INT_MAX);
+ return sb;
+ }
+ case QVariant::Pixmap:
+ return new QLabel(parent);
+ case QVariant::Double: {
+ QDoubleSpinBox *sb = new QDoubleSpinBox(parent);
+ sb->setFrame(false);
+ sb->setMinimum(-DBL_MAX);
+ sb->setMaximum(DBL_MAX);
+ sb->setDecimals(8);
+ return sb;
+ }
+ default:
+ break;
+ }
+ return 0;
+}
+
+QByteArray
+ArgumentsItemEditorFactory::valuePropertyName(QVariant::Type type) const
+{
+ switch (type) {
+ case QVariant::Bool:
+ return "currentIndex";
+ case QVariant::UInt:
+ case QVariant::Int:
+ case QVariant::LongLong:
+ case QVariant::ULongLong:
+ case QVariant::Double:
+ return "value";
+#if 0
+ case QVariant::String:
+#endif
+ default:
+ return "text";
+ }
+}
+
+BooleanComboBox::BooleanComboBox(QWidget *parent)
+ : QComboBox(parent)
+{
+ addItem(tr("False"));
+ addItem(tr("True"));
+}
+
+void BooleanComboBox::setValue(bool value)
+{
+ setCurrentIndex(value ? 1 : 0);
+}
+
+bool BooleanComboBox::value() const
+{
+ return (currentIndex() == 1);
+}
+
+ArgumentsEditor::ArgumentsEditor(QWidget *parent)
+ : QDialog(parent),
+ m_model(new QStandardItemModel()),
+ m_call(0)
+{
+ init();
+}
+
+ArgumentsEditor::~ArgumentsEditor()
+{
+}
+
+void ArgumentsEditor::setCall(ApiTraceCall *call)
+{
+ m_call = call;
+ setupCall();
+}
+
+ApiTraceCall * ArgumentsEditor::call() const
+{
+ return m_call;
+}
+
+void ArgumentsEditor::init()
+{
+ m_ui.setupUi(this);
+
+ connect(m_ui.selectStringCB, SIGNAL(currentIndexChanged(int)),
+ SLOT(currentSourceChanged(int)));
+ connect(m_ui.glslEdit, SIGNAL(textChanged()),
+ SLOT(sourceChanged()));
+ connect(m_ui.revertButton, SIGNAL(clicked()),
+ SLOT(revert()));
+
+ m_ui.argsTree->setModel(m_model);
+ QItemEditorFactory *factory =
+ new ArgumentsItemEditorFactory();
+
+ QItemEditorFactory::setDefaultFactory(factory);
+}
+
+void ArgumentsEditor::setupCall()
+{
+ m_model->clear();
+
+ QStringList headers;
+ headers.append(tr("Argument"));
+ headers.append(tr("Value"));
+ m_model->setColumnCount(2);
+ m_model->setHorizontalHeaderLabels(headers);
+ m_ui.argsTabWidget->removeTab(
+ m_ui.argsTabWidget->indexOf(m_ui.shaderTab));
+
+ if (!m_call)
+ return;
+
+ m_ui.callLabel->setText(m_call->name());
+ QStandardItem *rootItem = m_model->invisibleRootItem();
+ for (int i = 0; i < m_call->argNames().count(); ++i) {
+ QString argName = m_call->argNames()[i];
+ QVariant val = m_call->arguments()[i];
+ QStandardItem *nameItem = new QStandardItem(argName);
+ nameItem->setFlags(nameItem->flags() ^ Qt::ItemIsEditable);
+ QList<QStandardItem*> topRow;
+ topRow.append(nameItem);
+
+ if (val.canConvert<ApiArray>()) {
+ ApiArray array = val.value<ApiArray>();
+ QList<QVariant> vals = array.values();
+
+ QVariant firstVal = vals.value(0);
+ if (firstVal.userType() == QVariant::String) {
+ m_ui.argsTabWidget->addTab(
+ m_ui.shaderTab, argName);
+ setupShaderEditor(vals);
+ delete nameItem;
+ continue;
+ } else if (isVariantEditable(firstVal)) {
+ for (int i = 0; i < vals.count(); ++i) {
+ QList<QStandardItem*> row;
+
+ QStandardItem *idx = new QStandardItem();
+ idx->setFlags(idx->flags() ^ Qt::ItemIsEditable);
+ idx->setText(tr("%1)").arg(i));
+
+ QStandardItem *col = new QStandardItem();
+ col->setFlags(col->flags() | Qt::ItemIsEditable);
+ col->setData(vals[i], Qt::DisplayRole);
+ row.append(idx);
+ row.append(col);
+ nameItem->appendRow(row);
+ }
+ } else {
+ qDebug()<<"\tUnsupported array = "<<firstVal;
+ delete nameItem;
+ continue;
+ }
+ } else if (val.canConvert<ApiPointer>()) {
+ ApiPointer ptr = val.value<ApiPointer>();
+ QStandardItem *item = new QStandardItem();
+ item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+ item->setText(ptr.toString());
+ QIcon icon(":/resources/emblem-locked.png");
+ item->setIcon(icon);
+ item->setToolTip(tr("Argument is read-only"));
+ topRow.append(item);
+ } else if (val.canConvert<ApiEnum>()) {
+ ApiEnum en = val.value<ApiEnum>();
+ QStandardItem *item = new QStandardItem();
+ item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+ item->setText(en.toString());
+ QIcon icon(":/resources/emblem-locked.png");
+ item->setIcon(icon);
+ item->setToolTip(tr("Argument is read-only"));
+ topRow.append(item);
+ } else if (val.canConvert<ApiBitmask>()) {
+ ApiBitmask mask = val.value<ApiBitmask>();
+ QStandardItem *item = new QStandardItem();
+ item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+ item->setText(mask.toString());
+ QIcon icon(":/resources/emblem-locked.png");
+ item->setIcon(icon);
+ item->setToolTip(tr("Argument is read-only"));
+ topRow.append(item);
+ } else if (val.canConvert<ApiStruct>()) {
+ ApiStruct str = val.value<ApiStruct>();
+ QStandardItem *item = new QStandardItem();
+ item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+ item->setText(str.toString());
+ QIcon icon(":/resources/emblem-locked.png");
+ item->setIcon(icon);
+ item->setToolTip(tr("Argument is read-only"));
+ topRow.append(item);
+ } else if (val.userType() == QVariant::ByteArray) {
+ QByteArray ba = val.value<QByteArray>();
+ QStandardItem *item = new QStandardItem();
+ item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+ item->setText(
+ tr("<binary data, size = %1 bytes>").arg(ba.size()));
+ QIcon icon(":/resources/emblem-locked.png");
+ item->setIcon(icon);
+ item->setToolTip(tr("Argument is read-only"));
+ topRow.append(item);
+ } else {
+ QStandardItem *item
+ = new QStandardItem();
+
+ if (isVariantEditable(val)) {
+ item->setFlags(item->flags() | Qt::ItemIsEditable);
+ } else {
+ QIcon icon(":/resources/emblem-locked.png");
+ item->setIcon(icon);
+ item->setFlags(item->flags() ^ Qt::ItemIsEditable);
+ item->setToolTip(tr("Argument is read-only"));
+ }
+ item->setData(val, Qt::DisplayRole);
+ topRow.append(item);
+ }
+ rootItem->appendRow(topRow);
+ }
+}
+
+void ArgumentsEditor::setupShaderEditor(const QList<QVariant> &sources)
+{
+ m_ui.selectStringCB->clear();
+ m_ui.glslEdit->clear();
+ for (int i = 0; i < sources.count(); ++i) {
+ m_ui.selectStringCB->addItem(
+ tr("Shader string: %1").arg(i),
+ sources[i]);
+ }
+ m_ui.selectStringCB->setCurrentIndex(0);
+}
+
+void ArgumentsEditor::currentSourceChanged(int idx)
+{
+ QVariant source = m_ui.selectStringCB->itemData(idx);
+ QString str = source.toString();
+ m_ui.glslEdit->setPlainText(source.toString());
+ m_ui.lengthLabel->setText(
+ tr("%1").arg(str.length()));
+}
+
+void ArgumentsEditor::sourceChanged()
+{
+ QString str = m_ui.glslEdit->toPlainText();
+ m_ui.lengthLabel->setText(
+ tr("%1").arg(str.length()));
+
+ m_ui.selectStringCB->setItemData(
+ m_ui.selectStringCB->currentIndex(),
+ str);
+}
+
+void ArgumentsEditor::accept()
+{
+ QStringList argNames = m_call->argNames();
+ QList<QVariant> originalValues = m_call->arguments();
+ QList<QVariant> newValues;
+ bool changed = false;
+ for (int i = 0; i < argNames.count(); ++i) {
+ bool valChanged = false;
+ QString argName = argNames[i];
+ QVariant argValue = originalValues[i];
+ QVariant editorValue = valueForName(argName, argValue, &valChanged);
+ newValues.append(editorValue);
+#if 0
+ qDebug()<<"Arg = "<<argName;
+ qDebug()<<"\toriginal = "<<argValue;
+ qDebug()<<"\teditor = "<<editorValue;
+ qDebug()<<"\tchanged = "<<valChanged;
+#endif
+ if (valChanged)
+ changed = true;
+ }
+ if (changed)
+ m_call->setEditedValues(newValues);
+ QDialog::accept();
+}
+
+QVariant ArgumentsEditor::valueForName(const QString &name,
+ const QVariant &originalValue,
+ bool *changed) const
+{
+ QVariant val;
+
+ *changed = false;
+
+ //Handle string arrays specially
+ if (isVariantStringArray(originalValue)) {
+ ApiArray array = originalValue.value<ApiArray>();
+ return arrayFromEditor(array, changed);
+ }
+
+ for (int topRow = 0; topRow < m_model->rowCount(); ++topRow) {
+ QModelIndex nameIdx = m_model->index(topRow, 0, QModelIndex());
+ QString argName = nameIdx.data().toString();
+ /* we display shaders in a separate widget so
+ * the ordering might be different */
+ if (argName == name) {
+ if (originalValue.userType() == QMetaType::type("ApiArray")) {
+ ApiArray array = originalValue.value<ApiArray>();
+ val = arrayFromIndex(nameIdx, array, changed);
+ } else {
+ QModelIndex valIdx = m_model->index(topRow, 1, QModelIndex());
+ val = valIdx.data();
+ if (val != originalValue)
+ *changed = true;
+ }
+ }
+ }
+ return val;
+}
+
+QVariant ArgumentsEditor::arrayFromIndex(const QModelIndex &parentIndex,
+ const ApiArray &origArray,
+ bool *changed) const
+{
+ QList<QVariant> origValues = origArray.values();
+
+ *changed = false;
+
+ if (origValues.isEmpty())
+ return QVariant::fromValue(ApiArray());
+
+ QList<QVariant> lst;
+ for (int i = 0; i < origValues.count(); ++i) {
+ QModelIndex valIdx = m_model->index(i, 1, parentIndex);
+ QVariant var = valIdx.data();
+ QVariant origValue = origValues[i];
+ if (var != origValue)
+ *changed = true;
+ //qDebug()<<"\t\tarray "<<i<<") "<<var;
+ lst.append(var);
+ }
+ return QVariant::fromValue(ApiArray(lst));
+}
+
+QVariant ArgumentsEditor::arrayFromEditor(const ApiArray &origArray,
+ bool *changed) const
+{
+ QList<QVariant> vals;
+ QList<QVariant> origValues = origArray.values();
+
+ Q_ASSERT(isVariantStringArray(QVariant::fromValue(origArray)));
+ *changed = false;
+ //shaders
+ for (int i = 0; i < m_ui.selectStringCB->count(); ++i) {
+ QVariant val = m_ui.selectStringCB->itemData(i);
+ QVariant origValue = origValues[i];
+ if (origValue != val)
+ *changed = true;
+ vals.append(val);
+ }
+ return QVariant::fromValue(ApiArray(vals));
+}
+
+void ArgumentsEditor::revert()
+{
+ m_call->revert();
+ setupCall();
+}
+
+#include "argumentseditor.moc"
--- /dev/null
+#ifndef ARGUMENTSEDITOR_H
+#define ARGUMENTSEDITOR_H
+
+#include "apitracecall.h"
+#include "ui_argumentseditor.h"
+
+#include <QComboBox>
+#include <QDialog>
+#include <QItemEditorFactory>
+#include <QStandardItemModel>
+
+class ApiTraceCall;
+
+class BooleanComboBox : public QComboBox
+{
+ Q_OBJECT
+ Q_PROPERTY(bool value READ value WRITE setValue USER true)
+public:
+ BooleanComboBox(QWidget *parent);
+ void setValue(bool);
+ bool value() const;
+};
+
+class ArgumentsItemEditorFactory : public QItemEditorFactory
+{
+public:
+ ArgumentsItemEditorFactory();
+ QWidget *createEditor(QVariant::Type type, QWidget *parent) const;
+ QByteArray valuePropertyName(QVariant::Type) const;
+};
+
+class ArgumentsEditor : public QDialog
+{
+ Q_OBJECT
+public:
+ ArgumentsEditor(QWidget *parent=0);
+ ~ArgumentsEditor();
+
+
+ virtual void accept();
+
+ void setCall(ApiTraceCall *call);
+ ApiTraceCall *call() const;
+
+private slots:
+ void currentSourceChanged(int idx);
+ void sourceChanged();
+ void revert();
+private:
+ void init();
+ void setupCall();
+ void setupShaderEditor(const QList<QVariant> &sources);
+ QVariant valueForName(const QString &name,
+ const QVariant &orignalValue,
+ bool *changed) const;
+ QVariant arrayFromIndex(const QModelIndex &index,
+ const ApiArray &array,
+ bool *changed) const;
+ QVariant arrayFromEditor(const ApiArray &origArray,
+ bool *changed) const;
+private:
+ Ui_ArgumentsEditor m_ui;
+ QStandardItemModel *m_model;
+
+ ApiTraceCall *m_call;
+};
+
+#endif
apiCallFromTraceCall(const Trace::Call *call,
const QHash<QString, QUrl> &helpHash)
{
- ApiTraceCall *apiCall = new ApiTraceCall();
- apiCall->name = QString::fromStdString(call->sig->name);
- apiCall->index = call->no;
-
- QString argumentsText;
- for (int i = 0; i < call->sig->arg_names.size(); ++i) {
- apiCall->argNames +=
- QString::fromStdString(call->sig->arg_names[i]);
- }
- if (call->ret) {
- VariantVisitor retVisitor;
- call->ret->visit(retVisitor);
- apiCall->returnValue = retVisitor.variant();
- }
- for (int i = 0; i < call->args.size(); ++i) {
- VariantVisitor argVisitor;
- call->args[i]->visit(argVisitor);
- apiCall->argValues += argVisitor.variant();
- }
+ ApiTraceCall *apiCall = new ApiTraceCall(call);
- apiCall->helpUrl = helpHash.value(apiCall->name);
+ apiCall->setHelpUrl(helpHash.value(apiCall->name()));
//force generation of the internal state
apiCall->filterText();
Trace::Parser p;
if (p.open(m_fileName.toLatin1().constData())) {
- Trace::Call *call;
- call = p.parse_call();
+ Trace::Call *call = p.parse_call();
while (call) {
//std::cout << *call;
if (!currentFrame) {
}
ApiTraceCall *apiCall =
apiCallFromTraceCall(call, helpHash);
- apiCall->parentFrame = currentFrame;
+ apiCall->setParentFrame(currentFrame);
currentFrame->calls.append(apiCall);
if (ApiTrace::isCallAFrameMarker(apiCall,
m_frameMarker)) {
frames.clear();
}
}
+ delete call;
call = p.parse_call();
}
}
#include "apicalldelegate.h"
#include "apitracemodel.h"
#include "apitracefilter.h"
+#include "argumentseditor.h"
#include "imageviewer.h"
#include "jumpwidget.h"
#include "retracer.h"
m_ui.detailsDock->show();
if (call->hasBinaryData()) {
QByteArray data =
- call->argValues[call->binaryDataIndex()].toByteArray();
+ call->arguments()[call->binaryDataIndex()].toByteArray();
m_vdataInterpreter->setData(data);
+ QVariantList args = call->arguments();
- for (int i = 0; i < call->argNames.count(); ++i) {
- QString name = call->argNames[i];
+ for (int i = 0; i < call->argNames().count(); ++i) {
+ QString name = call->argNames()[i];
if (name == QLatin1String("stride")) {
- int stride = call->argValues[i].toInt();
+ int stride = args[i].toInt();
m_ui.vertexStrideSB->setValue(stride);
} else if (name == QLatin1String("size")) {
- int components = call->argValues[i].toInt();
+ int components = args[i].toInt();
m_ui.vertexComponentsSB->setValue(components);
} else if (name == QLatin1String("type")) {
- QString val = call->argValues[i].toString();
+ QString val = args[i].toString();
int textIndex = m_ui.vertexTypeCB->findText(val);
if (textIndex >= 0)
m_ui.vertexTypeCB->setCurrentIndex(textIndex);
void MainWindow::replayStart()
{
+ if (m_trace->isSaving()) {
+ QMessageBox::warning(
+ this,
+ tr("Trace Saving"),
+ tr("QApiTrace is currently saving the edited trace file. "
+ "Please wait until it finishes and try again."));
+ return;
+ }
QDialog dlg;
Ui_RetracerDialog dlgUi;
dlgUi.setupUi(&dlg);
void MainWindow::newTraceFile(const QString &fileName)
{
- m_traceFileName = fileName;
m_trace->setFileName(fileName);
- if (m_traceFileName.isEmpty()) {
+ if (fileName.isEmpty()) {
m_ui.actionReplay->setEnabled(false);
m_ui.actionLookupState->setEnabled(false);
setWindowTitle(tr("QApiTrace"));
void MainWindow::replayTrace(bool dumpState)
{
- if (m_traceFileName.isEmpty())
+ if (m_trace->fileName().isEmpty())
return;
- m_retracer->setFileName(m_traceFileName);
+ m_retracer->setFileName(m_trace->fileName());
m_retracer->setCaptureState(dumpState);
if (m_retracer->captureState() && m_selectedEvent) {
int index = 0;
if (m_selectedEvent->type() == ApiTraceEvent::Call) {
- index = static_cast<ApiTraceCall*>(m_selectedEvent)->index;
+ index = static_cast<ApiTraceCall*>(m_selectedEvent)->index();
} else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
ApiTraceFrame *frame =
static_cast<ApiTraceFrame*>(m_selectedEvent);
qDebug()<<"tried to get a state for an empty frame";
return;
}
- index = frame->calls.first()->index;
+ index = frame->calls.first()->index();
} else {
qDebug()<<"Unknown event type";
return;
tr("To inspect the state select an event in the event list."));
return;
}
+ if (m_trace->isSaving()) {
+ QMessageBox::warning(
+ this,
+ tr("Trace Saving"),
+ tr("QApiTrace is currently saving the edited trace file. "
+ "Please wait until it finishes and try again."));
+ return;
+ }
m_stateEvent = m_selectedEvent;
replayTrace(true);
}
m_ui.callView->resizeColumnToContents(0);
m_ui.callView->header()->swapSections(0, 1);
m_ui.callView->setColumnWidth(1, 42);
+ m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu);
m_progressBar = new QProgressBar();
m_progressBar->setRange(0, 0);
statusBar()->addPermanentWidget(m_progressBar);
m_progressBar->hide();
+ m_argsEditor = new ArgumentsEditor(this);
+
m_ui.detailsDock->hide();
m_ui.vertexDataDock->hide();
m_ui.stateDock->hide();
this, SLOT(startedLoadingTrace()));
connect(m_trace, SIGNAL(finishedLoadingTrace()),
this, SLOT(finishedLoadingTrace()));
+ connect(m_trace, SIGNAL(startedSaving()),
+ this, SLOT(slotStartedSaving()));
+ connect(m_trace, SIGNAL(saved()),
+ this, SLOT(slotSaved()));
connect(m_retracer, SIGNAL(finished(const QString&)),
this, SLOT(replayFinished(const QString&)));
connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
this, SLOT(callItemSelected(const QModelIndex &)));
+ connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
+ this, SLOT(customContextMenuRequested(QPoint)));
connect(m_ui.surfacesTreeWidget,
SIGNAL(customContextMenuRequested(const QPoint &)),
fillStateForFrame();
}
+void MainWindow::customContextMenuRequested(QPoint pos)
+{
+ QMenu menu;
+ QModelIndex index = m_ui.callView->indexAt(pos);
+
+ callItemSelected(index);
+ if (!index.isValid())
+ return;
+
+ ApiTraceEvent *event =
+ index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
+ if (!event || event->type() != ApiTraceEvent::Call)
+ return;
+
+ menu.addAction(QIcon(":/resources/media-record.png"),
+ tr("Lookup state"), this, SLOT(lookupState()));
+ menu.addAction(tr("Edit"), this, SLOT(editCall()));
+
+ menu.exec(QCursor::pos());
+}
+
+void MainWindow::editCall()
+{
+ if (m_selectedEvent && m_selectedEvent->type() == ApiTraceEvent::Call) {
+ ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
+ m_argsEditor->setCall(call);
+ m_argsEditor->show();
+ }
+}
+
+void MainWindow::slotStartedSaving()
+{
+ m_progressBar->show();
+ statusBar()->showMessage(
+ tr("Saving to %1").arg(m_trace->fileName()));
+}
+
+void MainWindow::slotSaved()
+{
+ statusBar()->showMessage(
+ tr("Saved to %1").arg(m_trace->fileName()), 2000);
+ m_progressBar->hide();
+}
+
#include "mainwindow.moc"
class ApiTraceFrame;
class ApiTraceModel;
class ApiTraceState;
+class ArgumentsEditor;
class ImageViewer;
class JumpWidget;
class QModelIndex;
void slotSearchNext(const QString &str, Qt::CaseSensitivity sensitivity);
void slotSearchPrev(const QString &str, Qt::CaseSensitivity sensitivity);
void fillState(bool nonDefaults);
+ void customContextMenuRequested(QPoint pos);
+ void editCall();
+ void slotStartedSaving();
+ void slotSaved();
private:
void initObjects();
QProgressBar *m_progressBar;
- QString m_traceFileName;
-
ApiTraceEvent *m_selectedEvent;
ApiTraceEvent *m_stateEvent;
SearchWidget *m_searchWidget;
TraceProcess *m_traceProcess;
+
+ ArgumentsEditor *m_argsEditor;
};
<file>resources/application-exit.png</file>
<file>resources/dialog-close.png</file>
<file>resources/dialog-information.png</file>
+ <file>resources/document-edit.png</file>
<file>resources/document-new.png</file>
<file>resources/document-open.png</file>
+ <file>resources/edit-undo.png</file>
+ <file>resources/emblem-locked.png</file>
<file>resources/go-down-search.png</file>
<file>resources/go-up-search.png</file>
<file>resources/media-playback-start.png</file>
--- /dev/null
+#include "saverthread.h"
+
+#include "trace_write.hpp"
+
+#include <QFile>
+#include <QHash>
+#include <QUrl>
+
+#include <QDebug>
+
+
+static Trace::FunctionSig *
+createFunctionSig(ApiTraceCall *call, unsigned id)
+{
+ Trace::FunctionSig *sig = new Trace::FunctionSig();
+
+ sig->id = id;
+ sig->name = qstrdup(call->name().toLocal8Bit());
+
+ QStringList args = call->argNames();
+ sig->num_args = args.count();
+ sig->args = new const char*[args.count()];
+ for (int i = 0; i < args.count(); ++i) {
+ sig->args[i] = qstrdup(args[i].toLocal8Bit());
+ }
+
+ return sig;
+}
+
+static void
+deleteFunctionSig(Trace::FunctionSig *sig)
+{
+ for (int i = 0; i < sig->num_args; ++i) {
+ delete [] sig->args[i];
+ }
+ delete [] sig->args;
+ delete [] sig->name;
+ delete sig;
+}
+
+static Trace::StructSig *
+createStructSig(const ApiStruct &str, unsigned id)
+{
+ ApiStruct::Signature aSig = str.signature();
+
+ Trace::StructSig *sig = new Trace::StructSig();
+ sig->id = id;
+ sig->name = qstrdup(aSig.name.toLocal8Bit());
+ sig->num_members = aSig.memberNames.count();
+ char **members = new char*[aSig.memberNames.count()];
+ sig->members = (const char **)members;
+ for (int i = 0; i < aSig.memberNames.count(); ++i) {
+ members[i] = qstrdup(aSig.memberNames[i].toLocal8Bit());
+ }
+ return sig;
+}
+
+static void
+deleteStructSig(Trace::StructSig *sig)
+{
+ for (int i = 0; i < sig->num_members; ++i) {
+ delete [] sig->members[i];
+ }
+ delete [] sig->members;
+ delete [] sig->name;
+ delete sig;
+}
+
+static Trace::EnumSig *
+createEnumSig(const ApiEnum &en, unsigned id)
+{
+ Trace::EnumSig *sig = new Trace::EnumSig();
+
+ sig->id = id;
+ sig->name = qstrdup(en.name().toLocal8Bit());
+ sig->value = en.value().toLongLong();
+
+ return sig;
+}
+
+static void
+deleteEnumSig(Trace::EnumSig *sig)
+{
+ delete [] sig->name;
+ delete sig;
+}
+
+static Trace::BitmaskSig *
+createBitmaskSig(const ApiBitmask &bt, unsigned id)
+{
+ ApiBitmask::Signature bsig = bt.signature();
+ ApiBitmask::Signature::const_iterator itr;
+
+ Trace::BitmaskSig *sig = new Trace::BitmaskSig();
+ Trace::BitmaskVal *values = new Trace::BitmaskVal[bsig.count()];
+
+ sig->id = id;
+ sig->count = bsig.count();
+ sig->values = values;
+
+ int i = 0;
+ for (itr = bsig.constBegin(); itr != bsig.constEnd(); ++itr, ++i) {
+ values[i].name = qstrdup(itr->first.toLocal8Bit());
+ values[i].value = itr->second;
+ }
+
+ return sig;
+}
+
+static void
+deleteBitmaskSig(Trace::BitmaskSig *sig)
+{
+ for (int i = 0; i < sig->count; ++i) {
+ delete [] sig->values[i].name;
+ }
+ delete [] sig->values;
+ delete sig;
+}
+
+static void
+writeValue(const QVariant &var, unsigned &id)
+{
+ int arrayType = QMetaType::type("ApiArray");
+ int bitmaskType = QMetaType::type("ApiBitmask");
+ int structType = QMetaType::type("ApiStruct");
+ int pointerType = QMetaType::type("ApiPointer");
+ int enumType = QMetaType::type("ApiEnum");
+ int type = var.userType();
+
+ switch(type) {
+ case QVariant::Bool:
+ Trace::LiteralBool(var.toBool());
+ break;
+ case QVariant::ByteArray: {
+ QByteArray ba = var.toByteArray();
+ Trace::LiteralBlob((const void*)ba.constData(), ba.size());
+ }
+ break;
+ case QVariant::Double:
+ Trace::LiteralFloat(var.toDouble());
+ break;
+ case QMetaType::Float:
+ Trace::LiteralFloat(var.toFloat());
+ break;
+ case QVariant::Int:
+ Trace::LiteralSInt(var.toInt());
+ break;
+ case QVariant::LongLong:
+ Trace::LiteralSInt(var.toLongLong());
+ break;
+ case QVariant::String: {
+ QString str = var.toString();
+ Trace::LiteralString(str.toLocal8Bit().constData(), str.length());
+ }
+ break;
+ case QVariant::UInt:
+ Trace::LiteralUInt(var.toInt());
+ break;
+ case QVariant::ULongLong:
+ Trace::LiteralUInt(var.toLongLong());
+ break;
+ default:
+ if (type == arrayType) {
+ ApiArray array = var.value<ApiArray>();
+ QList<QVariant> vals = array.values();
+ Trace::BeginArray(vals.count());
+ foreach(QVariant el, vals) {
+ Trace::BeginElement();
+ writeValue(el, ++id);
+ Trace::EndElement();
+ }
+ Trace::EndArray();
+ } else if (type == bitmaskType) {
+ ApiBitmask bm = var.value<ApiBitmask>();
+ Trace::BitmaskSig *sig = createBitmaskSig(bm, ++id);
+ LiteralBitmask(*sig, bm.value());
+ deleteBitmaskSig(sig);
+ } else if (type == structType) {
+ ApiStruct apiStr = var.value<ApiStruct>();
+ QList<QVariant> vals = apiStr.values();
+ Trace::StructSig *str = createStructSig(apiStr, ++id);
+ Trace::BeginStruct(str);
+ foreach(QVariant val, vals) {
+ writeValue(val, ++id);
+ }
+ Trace::EndStruct();
+ deleteStructSig(str);
+ } else if (type == pointerType) {
+ ApiPointer apiPtr = var.value<ApiPointer>();
+ //Trace::BeginArray(1);
+ //Trace::BeginElement();
+ Trace::LiteralOpaque((const void*)apiPtr.value());
+ //Trace::EndElement();
+ //Trace::EndArray();
+ } else if (type == enumType) {
+ ApiEnum apiEnum = var.value<ApiEnum>();
+ Trace::EnumSig *sig = createEnumSig(apiEnum, ++id);
+ Trace::LiteralEnum(sig);
+ deleteEnumSig(sig);
+ } else {
+ qWarning()<<"Unsupported write variant : "
+ << QMetaType::typeName(type);
+ }
+ }
+}
+
+SaverThread::SaverThread(QObject *parent)
+ : QThread(parent)
+{
+}
+
+void SaverThread::saveFile(const QString &fileName,
+ const QList<ApiTraceCall*> &calls)
+{
+ m_fileName = fileName;
+ m_calls = calls;
+ start();
+}
+
+void SaverThread::run()
+{
+ qputenv("TRACE_PATH", m_fileName.toLocal8Bit());
+ unsigned id = 0;
+
+ Trace::Open();
+ for (int i = 0; i < m_calls.count(); ++i) {
+ ApiTraceCall *call = m_calls[i];
+ Trace::FunctionSig *funcSig = createFunctionSig(call, ++id);
+ unsigned callNo = Trace::BeginEnter(*funcSig);
+ {
+ //args
+ QVariantList vars = call->arguments();
+ int index = 0;
+ foreach(QVariant var, vars) {
+ Trace::BeginArg(index++);
+ writeValue(var, ++id);
+ Trace::EndArg();
+ }
+ }
+ Trace::EndEnter();
+ Trace::BeginLeave(callNo);
+ {
+ QVariant ret = call->returnValue();
+ if (!ret.isNull()) {
+ Trace::BeginReturn();
+ writeValue(ret, ++id);
+ Trace::EndReturn();
+ }
+ }
+ Trace::EndLeave();
+
+ deleteFunctionSig(funcSig);
+ }
+ Trace::Close();
+
+ emit traceSaved();
+}
+
+#include "saverthread.moc"
--- /dev/null
+#ifndef SAVERTHREAD_H
+#define SAVERTHREAD_H
+
+
+#include "apitrace.h"
+#include <QThread>
+#include <QList>
+
+class ApiTraceCall;
+class ApiTraceFrame;
+
+class SaverThread : public QThread
+{
+ Q_OBJECT
+public:
+ SaverThread(QObject *parent=0);
+
+public slots:
+ void saveFile(const QString &fileName,
+ const QList<ApiTraceCall*> &calls);
+
+signals:
+ void traceSaved();
+
+protected:
+ virtual void run();
+
+private:
+ QString m_fileName;
+ QList<ApiTraceCall*> m_calls;
+};
+
+
+#endif
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ArgumentsEditor</class>
+ <widget class="QDialog" name="ArgumentsEditor">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>481</width>
+ <height>615</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Trace Arguments Editor</string>
+ </property>
+ <property name="sizeGripEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="modal">
+ <bool>true</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="callLabel">
+ <property name="font">
+ <font>
+ <pointsize>16</pointsize>
+ <weight>50</weight>
+ <bold>false</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>TextLabel</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>128</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="revertButton">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string>Revert</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../qapitrace.qrc">
+ <normaloff>:/resources/edit-undo.png</normaloff>:/resources/edit-undo.png</iconset>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTabWidget" name="argsTabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab">
+ <attribute name="title">
+ <string>Arguments</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTreeView" name="argsTree">
+ <attribute name="headerVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="shaderTab">
+ <attribute name="title">
+ <string>Shader</string>
+ </attribute>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <item>
+ <widget class="QComboBox" name="selectStringCB"/>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Length:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="lengthLabel">
+ <property name="text">
+ <string>100</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="GLSLEdit" name="glslEdit"/>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>GLSLEdit</class>
+ <extends>QPlainTextEdit</extends>
+ <header>glsledit.h</header>
+ </customwidget>
+ </customwidgets>
+ <resources>
+ <include location="../qapitrace.qrc"/>
+ </resources>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ArgumentsEditor</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ArgumentsEditor</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
}
}
-void Close(void) {
- _Close();
-}
-
static unsigned call_no = 0;
inline bool lookup(std::vector<bool> &map, size_t index) {
static std::vector<bool> bitmasks;
+void Close(void) {
+ _Close();
+ call_no = 0;
+ functions = std::vector<bool>();
+ structs = std::vector<bool>();
+ enums = std::vector<bool>();
+ bitmasks = std::vector<bool>();
+}
+
unsigned BeginEnter(const FunctionSig &function) {
OS::AcquireMutex();
Open();