blob: 8bdaa684807a50481e5fdf17aae8e61dcbbc5578 [file] [log] [blame]
#include "apitracecall.h"
#include "apitrace.h"
#include "traceloader.h"
#include "trace_model.hpp"
#include <QDebug>
#include <QLocale>
#include <QObject>
#define QT_USE_FAST_OPERATOR_PLUS
#include <QStringBuilder>
#include <QTextDocument>
const char * const styleSheet =
".call {\n"
" font-weight:bold;\n"
// text shadow looks great but doesn't work well in qtwebkit 4.7
" /*text-shadow: 0px 2px 3px #555;*/\n"
" font-size: 1.1em;\n"
"}\n"
".arg-name {\n"
" border: 1px solid rgb(238,206,0);\n"
" border-radius: 4px;\n"
" background: yellow;\n"
" padding: 2px;\n"
" box-shadow: 0px 1px 3px dimgrey;\n"
" -webkit-transition: background 1s linear;\n"
"}\n"
".arg-name:hover {\n"
" background: white;\n"
"}\n"
".arg-value {\n"
" color: #0000ff;\n"
"}\n"
".thread-id {\n"
" color: #aaaaaa;\n"
" min-width: 3em;\n"
"}\n"
".error {\n"
" border: 1px solid rgb(255,0,0);\n"
" margin: 10px;\n"
" padding: 1;\n"
" border-radius: 4px;\n"
// also looks great but qtwebkit doesn't support it
//" background: #6fb2e5;\n"
//" box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
//" -o-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
//" -webkit-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
//" -moz-box-shadow: 0 1px 5px #0061aa, inset 0 10px 20px #b6f9ff;\n"
"}\n";
// Qt::convertFromPlainText doesn't do precisely what we want
static QString
plainTextToHTML(const QString & plain, bool multiLine, bool forceNoQuote = false)
{
int col = 0;
bool quote = false;
QString rich;
for (auto & ch : plain) {
if (ch == QLatin1Char('\n')){
if (multiLine) {
rich += QLatin1String("<br>\n");
} else {
rich += QLatin1String("\\n");
}
col = 0;
quote = true;
} else {
if (ch == QLatin1Char('\t')){
if (multiLine) {
rich += QChar(0x00a0U);
++col;
while (col % 8) {
rich += QChar(0x00a0U);
++col;
}
} else {
rich += QLatin1String("\\t");
}
quote = true;
} else if (ch.isSpace()) {
rich += QChar(0x00a0U);
quote = true;
} else if (ch == QLatin1Char('<')) {
rich += QLatin1String("&lt;");
} else if (ch == QLatin1Char('>')) {
rich += QLatin1String("&gt;");
} else if (ch == QLatin1Char('&')) {
rich += QLatin1String("&amp;");
} else {
rich += ch;
}
++col;
}
}
if (quote && !forceNoQuote) {
return QLatin1Literal("\"") + rich + QLatin1Literal("\"");
}
return rich;
}
QString
apiVariantToString(const QVariant &variant, bool multiLine)
{
if (variant.isNull()) {
return QLatin1String("?");
}
if (variant.userType() == QMetaType::Float) {
return QString::number(variant.toFloat());
}
if (variant.userType() == QVariant::Double) {
return QString::number(variant.toDouble());
}
if (variant.userType() == QVariant::ByteArray) {
if (variant.toByteArray().size() < 1024) {
int bytes = variant.toByteArray().size();
return QObject::tr("[binary data, size = %1 bytes]").arg(bytes);
} else {
float kb = variant.toByteArray().size()/1024.;
return QObject::tr("[binary data, size = %1 kb]").arg(kb);
}
}
if (variant.userType() == QVariant::String) {
return plainTextToHTML(variant.toString(), multiLine);
}
if (variant.userType() < QVariant::UserType) {
return variant.toString();
}
if (variant.canConvert<ApiPointer>()) {
return variant.value<ApiPointer>().toString();
}
if (variant.canConvert<ApiBitmask>()) {
return variant.value<ApiBitmask>().toString();
}
if (variant.canConvert<ApiStruct>()) {
return variant.value<ApiStruct>().toString(multiLine);
}
if (variant.canConvert<ApiArray>()) {
return variant.value<ApiArray>().toString(multiLine);
}
if (variant.canConvert<ApiEnum>()) {
return variant.value<ApiEnum>().toString();
}
return QString();
}
void VariantVisitor::visit(trace::Null *)
{
m_variant = QVariant::fromValue(ApiPointer(0));
}
void VariantVisitor::visit(trace::Bool *node)
{
m_variant = QVariant(node->value);
}
void VariantVisitor::visit(trace::SInt *node)
{
m_variant = QVariant(node->value);
}
void VariantVisitor::visit(trace::UInt *node)
{
m_variant = QVariant(node->value);
}
void VariantVisitor::visit(trace::Float *node)
{
m_variant = QVariant(node->value);
}
void VariantVisitor::visit(trace::Double *node)
{
m_variant = QVariant(node->value);
}
void VariantVisitor::visit(trace::String *node)
{
m_variant = QVariant(QString::fromLatin1(node->value));
}
void VariantVisitor::visit(trace::WString *node)
{
m_variant = QVariant(QString::fromWCharArray(node->value));
}
void VariantVisitor::visit(trace::Enum *e)
{
m_variant = QVariant::fromValue(ApiEnum(e->sig, e->value));
}
void VariantVisitor::visit(trace::Bitmask *bitmask)
{
m_variant = QVariant::fromValue(ApiBitmask(bitmask));
}
void VariantVisitor::visit(trace::Struct *str)
{
m_variant = QVariant::fromValue(ApiStruct(str));
}
void VariantVisitor::visit(trace::Array *array)
{
m_variant = QVariant::fromValue(ApiArray(array));
}
void VariantVisitor::visit(trace::Blob *blob)
{
QByteArray barray = QByteArray(blob->buf, blob->size);
m_variant = QVariant(barray);
}
void VariantVisitor::visit(trace::Pointer *ptr)
{
m_variant = QVariant::fromValue(ApiPointer(ptr->value));
}
void VariantVisitor::visit(trace::Repr *repr)
{
/* TODO: Preserve both the human and machine value */
repr->humanValue->visit(*this);
}
ApiEnum::ApiEnum(const trace::EnumSig *sig, signed long long value)
: m_sig(sig), m_value(value)
{
Q_ASSERT(m_sig);
}
QString ApiEnum::toString() const
{
Q_ASSERT(m_sig);
for (const trace::EnumValue *it = m_sig->values;
it != m_sig->values + m_sig->num_values; ++it) {
if (m_value == it->value) {
return QString::fromLatin1(it->name);
}
}
return QString::fromLatin1("%1").arg(m_value);
}
QVariant ApiEnum::value() const
{
if (m_sig) {
return QVariant::fromValue(m_value);
}
Q_ASSERT(!"should never happen");
return QVariant();
}
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;
}
ApiPointer::ApiPointer(unsigned long long val)
: m_value(val)
{
}
unsigned long long ApiPointer::value() const
{
return m_value;
}
QString ApiPointer::toString() const
{
if (m_value)
return QString("0x%1").arg(m_value, 0, 16);
else
return QLatin1String("NULL");
}
ApiBitmask::ApiBitmask(const trace::Bitmask *bitmask)
: m_value(0)
{
init(bitmask);
}
void ApiBitmask::init(const trace::Bitmask *bitmask)
{
if (!bitmask)
return;
m_value = bitmask->value;
for (const trace::BitmaskFlag *it = bitmask->sig->flags;
it != bitmask->sig->flags + bitmask->sig->num_flags; ++it) {
QPair<QString, unsigned long long> pair;
pair.first = QString::fromLatin1(it->name);
pair.second = it->value;
m_sig.append(pair);
}
}
QString ApiBitmask::toString() const
{
QString str;
unsigned long long value = m_value;
bool first = true;
for (Signature::const_iterator it = m_sig.begin(); it != m_sig.end(); ++it) {
Q_ASSERT(it->second || first);
if ((it->second && (value & it->second) == it->second) ||
(!it->second && value == 0)) {
if (!first) {
str += QLatin1String(" | ");
}
str += it->first;
value &= ~it->second;
first = false;
}
if (value == 0) {
break;
}
}
if (value || first) {
if (!first) {
str += QLatin1String(" | ");
}
str += QString::fromLatin1("0x%1").arg(value, 0, 16);
}
return str;
}
ApiStruct::ApiStruct(const trace::Struct *s)
{
init(s);
}
QString ApiStruct::toString(bool multiLine) const
{
QString str;
str += QLatin1String("{");
for (unsigned i = 0; i < m_members.count(); ++i) {
str += m_sig.memberNames[i] %
QLatin1Literal(" = ") %
apiVariantToString(m_members[i], multiLine);
if (i < m_members.count() - 1)
str += QLatin1String(", ");
}
str += QLatin1String("}");
return str;
}
void ApiStruct::init(const trace::Struct *s)
{
if (!s)
return;
m_sig.name = QString::fromLatin1(s->sig->name);
for (unsigned i = 0; i < s->sig->num_members; ++i) {
VariantVisitor vis;
m_sig.memberNames.append(
QString::fromLatin1(s->sig->member_names[i]));
s->members[i]->visit(vis);
m_members.append(vis.variant());
}
}
ApiArray::ApiArray(const trace::Array *arr)
{
init(arr);
}
ApiArray::ApiArray(const QVector<QVariant> &vals)
: m_array(vals)
{
}
QVector<QVariant> ApiArray::values() const
{
return m_array;
}
QString ApiArray::toString(bool multiLine) const
{
QString str;
str += QLatin1String("[");
for(int i = 0; i < m_array.count(); ++i) {
const QVariant &var = m_array[i];
str += apiVariantToString(var, multiLine);
if (i < m_array.count() - 1)
str += QLatin1String(", ");
}
str += QLatin1String("]");
return str;
}
void ApiArray::init(const trace::Array *arr)
{
if (!arr)
return;
m_array.reserve(arr->values.size());
for (auto & value : arr->values) {
VariantVisitor vis;
value->visit(vis);
m_array.append(vis.variant());
}
m_array.squeeze();
}
ApiTraceState::ApiTraceState()
{
}
static ApiTexture getTextureFrom(QVariantMap const &image, QString label)
{
QSize size(image[QLatin1String("__width__")].toInt(),
image[QLatin1String("__height__")].toInt());
QString cls = image[QLatin1String("__class__")].toString();
int depth =
image[QLatin1String("__depth__")].toInt();
QString formatName =
image[QLatin1String("__format__")].toString();
QByteArray dataArray =
image[QLatin1String("__data__")].toByteArray();
QString userLabel =
image[QLatin1String("__label__")].toString();
if (!userLabel.isEmpty()) {
label += QString(", \"%1\"").arg(userLabel);
}
ApiTexture tex;
tex.setSize(size);
tex.setDepth(depth);
tex.setFormatName(formatName);
tex.setLabel(label);
tex.setData(dataArray);
return tex;
}
ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
{
m_parameters = parsedJson[QLatin1String("parameters")].toMap();
QVariantMap attachedShaders =
parsedJson[QLatin1String("shaders")].toMap();
QVariantMap::const_iterator itr;
for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
++itr) {
QString type = itr.key();
QString source = itr.value().toString();
m_shaderSources[type] = source;
}
m_uniforms = parsedJson[QLatin1String("uniforms")].toMap();
m_buffers = parsedJson[QLatin1String("buffers")].toMap();
m_shaderStorageBufferBlocks =
parsedJson[QLatin1String("shaderstoragebufferblocks")].toMap();
{
QVariantMap textures =
parsedJson[QLatin1String("textures")].toMap();
for (itr = textures.constBegin(); itr != textures.constEnd(); ++itr) {
m_textures.append(getTextureFrom(itr.value().toMap(), itr.key()));
}
}
QVariantMap fbos =
parsedJson[QLatin1String("framebuffer")].toMap();
for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) {
QVariantMap buffer = itr.value().toMap();
QSize size(buffer[QLatin1String("__width__")].toInt(),
buffer[QLatin1String("__height__")].toInt());
QString cls = buffer[QLatin1String("__class__")].toString();
int depth = buffer[QLatin1String("__depth__")].toInt();
QString formatName = buffer[QLatin1String("__format__")].toString();
QByteArray dataArray =
buffer[QLatin1String("__data__")].toByteArray();
QString label = itr.key();
QString userLabel =
buffer[QLatin1String("__label__")].toString();
if (!userLabel.isEmpty()) {
label += QString(", \"%1\"").arg(userLabel);
}
ApiFramebuffer fbo;
fbo.setSize(size);
fbo.setDepth(depth);
fbo.setFormatName(formatName);
fbo.setType(label);
fbo.setData(dataArray);
m_framebuffers.append(fbo);
}
}
const QVariantMap & ApiTraceState::parameters() const
{
return m_parameters;
}
const QMap<QString, QString> & ApiTraceState::shaderSources() const
{
return m_shaderSources;
}
const QVariantMap & ApiTraceState::uniforms() const
{
return m_uniforms;
}
const QVariantMap & ApiTraceState::buffers() const
{
return m_buffers;
}
const QVariantMap &ApiTraceState::shaderStorageBufferBlocks() const
{
return m_shaderStorageBufferBlocks;
}
bool ApiTraceState::isEmpty() const
{
return m_parameters.isEmpty() &&
m_shaderSources.isEmpty() &&
m_textures.isEmpty() &&
m_framebuffers.isEmpty();
}
const QList<ApiTexture> & ApiTraceState::textures() const
{
return m_textures;
}
const QList<ApiFramebuffer> & ApiTraceState::framebuffers() const
{
return m_framebuffers;
}
ApiFramebuffer ApiTraceState::colorBuffer() const
{
foreach (ApiFramebuffer fbo, m_framebuffers) {
if (fbo.type() == QLatin1String("GL_BACK")) {
return fbo;
}
}
foreach (ApiFramebuffer fbo, m_framebuffers) {
if (fbo.type() == QLatin1String("GL_FRONT")) {
return fbo;
}
}
return ApiFramebuffer();
}
ApiTraceCallSignature::ApiTraceCallSignature(const QString &name,
const QStringList &argNames)
: m_name(name),
m_argNames(argNames)
{
}
ApiTraceCallSignature::~ApiTraceCallSignature()
{
}
QUrl ApiTraceCallSignature::helpUrl() const
{
return m_helpUrl;
}
void ApiTraceCallSignature::setHelpUrl(const QUrl &url)
{
m_helpUrl = url;
}
ApiTraceEvent::ApiTraceEvent()
: m_type(ApiTraceEvent::None),
m_binaryDataIndex(-1),
m_state(0),
m_staticText(0)
{
}
ApiTraceEvent::ApiTraceEvent(Type t)
: m_type(t),
m_binaryDataIndex(-1),
m_state(0),
m_staticText(0)
{
Q_ASSERT(m_type == t);
}
ApiTraceEvent::~ApiTraceEvent()
{
delete m_state;
delete m_staticText;
}
ApiTraceState *ApiTraceEvent::state() const
{
return m_state;
}
void ApiTraceEvent::setState(ApiTraceState *state)
{
m_state = state;
}
void ApiTraceEvent::setThumbnail(const QImage & thumbnail)
{
m_thumbnail = thumbnail;
}
const QImage & ApiTraceEvent::thumbnail() const
{
return m_thumbnail;
}
ApiTraceCall::ApiTraceCall(ApiTraceFrame *parentFrame,
TraceLoader *loader,
const trace::Call *call)
: ApiTraceEvent(ApiTraceEvent::Call),
m_parentFrame(parentFrame),
m_parentCall(0)
{
loadData(loader, call);
}
ApiTraceCall::ApiTraceCall(ApiTraceCall *parentCall,
TraceLoader *loader,
const trace::Call *call)
: ApiTraceEvent(ApiTraceEvent::Call),
m_parentFrame(parentCall->parentFrame()),
m_parentCall(parentCall)
{
loadData(loader, call);
}
ApiTraceCall::~ApiTraceCall()
{
}
void
ApiTraceCall::loadData(TraceLoader *loader,
const trace::Call *call)
{
m_index = call->no;
m_thread = call->thread_id;
m_signature = loader->signature(call->sig->id);
if (!m_signature) {
QString name = QString::fromLatin1(call->sig->name);
QStringList argNames;
argNames.reserve(call->sig->num_args);
for (int i = 0; i < call->sig->num_args; ++i) {
argNames += QString::fromLatin1(call->sig->arg_names[i]);
}
m_signature = new ApiTraceCallSignature(name, argNames);
loader->addSignature(call->sig->id, m_signature);
}
if (call->ret) {
VariantVisitor retVisitor;
call->ret->visit(retVisitor);
m_returnValue = retVisitor.variant();
}
m_argValues.reserve(call->args.size());
for (int i = 0; i < call->args.size(); ++i) {
if (call->args[i].value) {
VariantVisitor argVisitor;
call->args[i].value->visit(argVisitor);
m_argValues.append(argVisitor.variant());
if (m_argValues[i].type() == QVariant::ByteArray) {
m_binaryDataIndex = i;
}
} else {
m_argValues.append(QVariant());
}
}
m_argValues.squeeze();
m_flags = call->flags;
if (call->backtrace != NULL) {
QString qbacktrace;
for (auto frame : *call->backtrace) {
if (frame->module != NULL) {
qbacktrace += QString("%1 ").arg(frame->module);
}
if (frame->function != NULL) {
qbacktrace += QString("at %1() ").arg(frame->function);
}
if (frame->filename != NULL) {
qbacktrace += QString("at %1").arg(frame->filename);
if (frame->linenumber >= 0) {
qbacktrace += QString(":%1 ").arg(frame->linenumber);
}
}
else {
if (frame->offset >= 0) {
qbacktrace += QString("[0x%1]").arg(frame->offset, 0, 16);
}
}
qbacktrace += "\n";
}
this->setBacktrace(qbacktrace);
}
}
ApiTraceCall *
ApiTraceCall::parentCall() const
{
return m_parentCall;
}
ApiTraceEvent *
ApiTraceCall::parentEvent() const
{
if (m_parentCall)
return m_parentCall;
else
return m_parentFrame;
}
QVector<ApiTraceCall*>
ApiTraceCall::children() const
{
return m_children;
}
void
ApiTraceCall::addChild(ApiTraceCall *call)
{
m_children.append(call);
}
int
ApiTraceCall::callIndex(ApiTraceCall *call) const
{
return m_children.indexOf(call);
}
void
ApiTraceCall::finishedAddingChildren()
{
m_children.squeeze();
}
bool ApiTraceCall::hasError() const
{
return !m_error.isEmpty();
}
QString ApiTraceCall::error() const
{
return m_error;
}
void ApiTraceCall::setError(const QString &msg)
{
if (m_error != msg) {
m_error = msg;
m_richText = QString();
}
}
ApiTrace * ApiTraceCall::parentTrace() const
{
if (m_parentFrame)
return m_parentFrame->parentTrace();
return NULL;
}
QVector<QVariant> ApiTraceCall::originalValues() const
{
return m_argValues;
}
void ApiTraceCall::setEditedValues(const QVector<QVariant> &lst)
{
ApiTrace *trace = parentTrace();
m_editedValues = lst;
//lets regenerate data
m_richText = QString();
m_searchText = QString();
delete m_staticText;
m_staticText = 0;
if (trace) {
if (!lst.isEmpty()) {
trace->callEdited(this);
} else {
trace->callReverted(this);
}
}
}
QVector<QVariant> ApiTraceCall::editedValues() const
{
return m_editedValues;
}
bool ApiTraceCall::edited() const
{
return !m_editedValues.isEmpty();
}
void ApiTraceCall::revert()
{
setEditedValues(QVector<QVariant>());
}
void ApiTraceCall::setHelpUrl(const QUrl &url)
{
m_signature->setHelpUrl(url);
}
void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
{
m_parentFrame = frame;
}
ApiTraceFrame * ApiTraceCall::parentFrame()const
{
return m_parentFrame;
}
int ApiTraceCall::index() const
{
return m_index;
}
QString ApiTraceCall::name() const
{
return m_signature->name();
}
QStringList ApiTraceCall::argNames() const
{
return m_signature->argNames();
}
QVector<QVariant> ApiTraceCall::arguments() const
{
if (m_editedValues.isEmpty())
return m_argValues;
else
return m_editedValues;
}
ApiTraceEvent *
ApiTraceCall::eventAtRow(int row) const
{
if (row < m_children.count())
return m_children.value(row);
else
return NULL;
}
QVariant ApiTraceCall::returnValue() const
{
return m_returnValue;
}
trace::CallFlags ApiTraceCall::flags() const
{
return m_flags;
}
QUrl ApiTraceCall::helpUrl() const
{
return m_signature->helpUrl();
}
bool ApiTraceCall::hasBinaryData() const
{
return m_binaryDataIndex >= 0;
}
int ApiTraceCall::binaryDataIndex() const
{
Q_ASSERT(hasBinaryData());
return m_binaryDataIndex;
}
QString ApiTraceCall::backtrace() const
{
return m_backtrace;
}
void ApiTraceCall::setBacktrace(QString backtrace)
{
m_backtrace = backtrace;
}
QStaticText ApiTraceCall::staticText() const
{
if (m_staticText && !m_staticText->text().isEmpty())
return *m_staticText;
QStringList argNames = m_signature->argNames();
QVector<QVariant> argValues = arguments();
QString richText;
// TODO: Toggle this via a menu option.
if (0) {
richText += QString::fromLatin1("<span style=\"color: #aaaaaa; min-width: 3em;\">@%1</span> ")
.arg(m_thread);
}
if (m_flags & trace::CALL_FLAG_MARKER &&
argNames.count() &&
argValues.last().userType() == QVariant::String)
{
// special handling for string markers
QString msgText = plainTextToHTML(argValues.last().toString(), false, true);
richText += QString::fromLatin1(
"<span style=\"font-weight:bold;color:green;\">%1</span>")
.arg(msgText);
} else {
richText += QString::fromLatin1(
"<span style=\"font-weight:bold\">%1</span>(").arg(
m_signature->name());
for (int i = 0; i < argNames.count(); ++i) {
richText += QLatin1String("<span style=\"color:#0000ff\">");
QString argText = apiVariantToString(argValues[i]);
//if arguments are really long (e.g. shader text), cut them
// and elide it
if (argText.length() > 40) {
QString shortened = argText.mid(0, 40);
shortened[argText.length() - 5] = '.';
shortened[argText.length() - 4] = '.';
shortened[argText.length() - 3] = '.';
shortened[argText.length() - 2] = argText.at(argText.length() - 2);
shortened[argText.length() - 1] = argText.at(argText.length() - 1);
richText += shortened;
} else {
richText += argText;
}
richText += QLatin1String("</span>");
if (i < argNames.count() - 1)
richText += QLatin1String(", ");
}
richText += QLatin1String(")");
if (m_returnValue.isValid()) {
richText +=
QLatin1Literal(" = ") %
QLatin1Literal("<span style=\"color:#0000ff\">") %
apiVariantToString(m_returnValue) %
QLatin1Literal("</span>");
}
}
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();
return *m_staticText;
}
QString ApiTraceCall::toHtml() const
{
if (!m_richText.isEmpty())
return m_richText;
m_richText += QLatin1String("<div class=\"call\">");
m_richText +=
QString::fromLatin1("%1 ")
.arg(m_index);
QString parentTip;
if (m_parentFrame) {
parentTip =
QString::fromLatin1("Frame %1")
.arg(m_parentFrame->number);
}
m_richText += QString::fromLatin1("<span class=\"thread-id\">@%1</span> ")
.arg(m_thread);
QUrl helpUrl = m_signature->helpUrl();
if (helpUrl.isEmpty()) {
m_richText += QString::fromLatin1(
"<span class=\"callName\" title=\"%1\">%2</span>(")
.arg(parentTip)
.arg(m_signature->name());
} else {
m_richText += QString::fromLatin1(
"<span class=\"callName\" title=\"%1\"><a href=\"%2\">%3</a></span>(")
.arg(parentTip)
.arg(helpUrl.toString())
.arg(m_signature->name());
}
QVector<QVariant> argValues = arguments();
QStringList argNames = m_signature->argNames();
for (int i = 0; i < argNames.count(); ++i) {
m_richText +=
QLatin1String("<span class=\"arg-name\">") +
argNames[i] +
QLatin1String("</span>") +
QLatin1Literal(" = ") +
QLatin1Literal("<span class=\"arg-value\">") +
apiVariantToString(argValues[i], true) +
QLatin1Literal("</span>");
if (i < argNames.count() - 1)
m_richText += QLatin1String(", ");
}
m_richText += QLatin1String(")");
if (m_returnValue.isValid()) {
m_richText +=
QLatin1String(" = ") +
QLatin1String("<span style=\"color:#0000ff\">") +
apiVariantToString(m_returnValue, true) +
QLatin1String("</span>");
}
m_richText += QLatin1String("</div>");
if (hasError()) {
QString errorStr =
QString::fromLatin1(
"<div class=\"error\">%1</div>")
.arg(m_error);
m_richText += errorStr;
}
m_richText =
QString::fromLatin1(
"<html><head><style type=\"text/css\" media=\"all\">"
"%1</style></head><body>%2</body></html>")
.arg(styleSheet)
.arg(m_richText);
m_richText.squeeze();
//qDebug()<<m_richText;
return m_richText;
}
QString ApiTraceCall::searchText() const
{
if (!m_searchText.isEmpty())
return m_searchText;
QVector<QVariant> argValues = arguments();
m_searchText = m_signature->name() + QLatin1Literal("(");
QStringList argNames = m_signature->argNames();
for (int i = 0; i < argNames.count(); ++i) {
m_searchText += argNames[i] +
QLatin1Literal(" = ") +
apiVariantToString(argValues[i]);
if (i < argNames.count() - 1)
m_searchText += QLatin1String(", ");
}
m_searchText += QLatin1String(")");
if (m_returnValue.isValid()) {
m_searchText += QLatin1Literal(" = ") +
apiVariantToString(m_returnValue);
}
m_searchText.squeeze();
return m_searchText;
}
int ApiTraceCall::numChildren() const
{
return m_children.count();
}
bool ApiTraceCall::contains(const QString &str,
Qt::CaseSensitivity sensitivity,
bool useRegex) const
{
QString txt = searchText();
return useRegex ? txt.contains(QRegExp(str, sensitivity))
: txt.contains(str, sensitivity);
}
void ApiTraceCall::missingThumbnail()
{
m_parentFrame->parentTrace()->missingThumbnail(this);
}
ApiTraceFrame::ApiTraceFrame(ApiTrace *parentTrace)
: ApiTraceEvent(ApiTraceEvent::Frame),
m_parentTrace(parentTrace),
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 = QObject::tr(
"<span style=\"font-weight:bold\">Frame %1</span>"
"&nbsp;&nbsp;&nbsp;"
"<span style=\"font-style:italic;font-size:small;font-weight:lighter;\"> "
"(%2 calls)</span>")
.arg(number)
.arg(m_loaded ? m_calls.count() : m_callsToLoad);
//mark the frame if it uploads more than a meg a frame
if (m_binaryDataSize > (1024*1024)) {
richText =
QObject::tr(
"%1"
"<span style=\"font-style:italic;\">"
"&nbsp;&nbsp;&nbsp;&nbsp;(%2MB)</span>")
.arg(richText)
.arg(double(m_binaryDataSize / (1024.*1024.)), 0, 'g', 2);
}
if (!m_staticText)
m_staticText = new QStaticText(richText);
QTextOption opt;
opt.setWrapMode(QTextOption::NoWrap);
m_staticText->setTextOption(opt);
m_staticText->prepare();
return *m_staticText;
}
int ApiTraceFrame::numChildren() const
{
return m_children.count();
}
int ApiTraceFrame::numTotalCalls() const
{
return m_calls.count();
}
ApiTrace * ApiTraceFrame::parentTrace() const
{
return m_parentTrace;
}
QVector<ApiTraceCall*> ApiTraceFrame::calls() const
{
return m_calls;
}
ApiTraceEvent * ApiTraceFrame::eventAtRow(int row) const
{
if (row < m_children.count())
return m_children.value(row);
else
return NULL;
}
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_children.indexOf(call);
}
bool ApiTraceFrame::isEmpty() const
{
if (m_loaded) {
return m_calls.isEmpty();
} else {
return m_callsToLoad == 0;
}
}
int ApiTraceFrame::binaryDataSize() const
{
return m_binaryDataSize;
}
void ApiTraceFrame::setCalls(const QVector<ApiTraceCall*> &children,
const QVector<ApiTraceCall*> &calls,
quint64 binaryDataSize)
{
m_children = children;
m_calls = calls;
m_binaryDataSize = binaryDataSize;
m_loaded = true;
delete m_staticText;
m_staticText = 0;
}
bool ApiTraceFrame::isLoaded() const
{
return m_loaded;
}
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,
bool useRegex) 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, useRegex)) {
return call;
}
}
return 0;
}
ApiTraceCall *
ApiTraceFrame::findPrevCall(ApiTraceCall *from,
const QString &str,
Qt::CaseSensitivity sensitivity,
bool useRegex) 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, useRegex)) {
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;
}
}
void ApiTraceFrame::missingThumbnail()
{
m_parentTrace->missingThumbnail(this);
}