]> git.cworth.org Git - apitrace/blob - gui/apitracecall.cpp
Fix saving of traces with pointers.
[apitrace] / gui / apitracecall.cpp
1 #include "apitracecall.h"
2
3 #include "apitrace.h"
4 #include "trace_model.hpp"
5
6 #include <QDebug>
7 #include <QObject>
8 #define QT_USE_FAST_OPERATOR_PLUS
9 #include <QStringBuilder>
10
11 ApiPointer::ApiPointer(unsigned long long val)
12     : m_value(val)
13 {
14 }
15
16 QString ApiPointer::toString() const
17 {
18     if (m_value)
19         return QString("0x%1").arg(m_value, 0, 16);
20     else
21         return QLatin1String("NULL");
22 }
23
24 QString apiVariantToString(const QVariant &variant)
25 {
26     if (variant.userType() == QVariant::Double) {
27         return QString::number(variant.toFloat());
28     }
29     if (variant.userType() == QVariant::ByteArray) {
30         if (variant.toByteArray().size() < 1024) {
31             int bytes = variant.toByteArray().size();
32             return QObject::tr("[binary data, size = %1 bytes]").arg(bytes);
33         } else {
34             float kb = variant.toByteArray().size()/1024.;
35             return QObject::tr("[binary data, size = %1 kb]").arg(kb);
36         }
37     }
38
39     if (variant.userType() < QVariant::UserType) {
40         return variant.toString();
41     }
42
43     if (variant.canConvert<ApiPointer>()) {
44         return variant.value<ApiPointer>().toString();
45     }
46     if (variant.canConvert<ApiBitmask>()) {
47         return variant.value<ApiBitmask>().toString();
48     }
49     if (variant.canConvert<ApiStruct>()) {
50         return variant.value<ApiStruct>().toString();
51     }
52     if (variant.canConvert<ApiArray>()) {
53         return variant.value<ApiArray>().toString();
54     }
55     if (variant.canConvert<ApiEnum>()) {
56         return variant.value<ApiEnum>().toString();
57     }
58
59     return QString();
60 }
61
62 ApiBitmask::ApiBitmask(const Trace::Bitmask *bitmask)
63     : m_value(0)
64 {
65     init(bitmask);
66 }
67
68
69 void ApiBitmask::init(const Trace::Bitmask *bitmask)
70 {
71     if (!bitmask)
72         return;
73
74     m_value = bitmask->value;
75     for (Trace::Bitmask::Signature::const_iterator it = bitmask->sig->begin();
76          it != bitmask->sig->end(); ++it) {
77         assert(it->second);
78         QPair<QString, unsigned long long> pair;
79
80         pair.first = QString::fromStdString(it->first);
81         pair.second = it->second;
82
83         m_sig.append(pair);
84     }
85 }
86
87 QString ApiBitmask::toString() const
88 {
89     QString str;
90     unsigned long long value = m_value;
91     bool first = true;
92     for (Signature::const_iterator it = m_sig.begin();
93          value != 0 && it != m_sig.end(); ++it) {
94         Q_ASSERT(it->second);
95         if ((value & it->second) == it->second) {
96             if (!first) {
97                 str += QLatin1String(" | ");
98             }
99             str += it->first;
100             value &= ~it->second;
101             first = false;
102         }
103     }
104     if (value || first) {
105         if (!first) {
106             str += QLatin1String(" | ");
107         }
108         str += QString::fromLatin1("0x%1").arg(value, 0, 16);
109     }
110     return str;
111 }
112
113 ApiStruct::ApiStruct(const Trace::Struct *s)
114 {
115     init(s);
116 }
117
118 QString ApiStruct::toString() const
119 {
120     QString str;
121
122     str += QLatin1String("{");
123     for (unsigned i = 0; i < m_members.count(); ++i) {
124         str += m_sig.memberNames[i] %
125                QLatin1Literal(" = ") %
126                apiVariantToString(m_members[i]);
127         if (i < m_members.count() - 1)
128             str += QLatin1String(", ");
129     }
130     str += QLatin1String("}");
131
132     return str;
133 }
134
135 void ApiStruct::init(const Trace::Struct *s)
136 {
137     if (!s)
138         return;
139
140     m_sig.name = QString::fromStdString(s->sig->name);
141     for (unsigned i = 0; i < s->members.size(); ++i) {
142         VariantVisitor vis;
143         m_sig.memberNames.append(
144             QString::fromStdString(s->sig->member_names[i]));
145         s->members[i]->visit(vis);
146         m_members.append(vis.variant());
147     }
148 }
149
150 void VariantVisitor::visit(Trace::Null *)
151 {
152     m_variant = QVariant::fromValue(ApiPointer(0));
153 }
154
155 void VariantVisitor::visit(Trace::Bool *node)
156 {
157     m_variant = QVariant(node->value);
158 }
159
160 void VariantVisitor::visit(Trace::SInt *node)
161 {
162     m_variant = QVariant(node->value);
163 }
164
165 void VariantVisitor::visit(Trace::UInt *node)
166 {
167     m_variant = QVariant(node->value);
168 }
169
170 void VariantVisitor::visit(Trace::Float *node)
171 {
172     m_variant = QVariant(node->value);
173 }
174
175 void VariantVisitor::visit(Trace::String *node)
176 {
177     m_variant = QVariant(QString::fromStdString(node->value));
178 }
179
180 void VariantVisitor::visit(Trace::Enum *e)
181 {
182     VariantVisitor vis;
183     e->sig->second->visit(vis);
184
185     QVariant val = vis.variant();
186
187     m_variant = QVariant::fromValue(
188         ApiEnum(QString::fromStdString(e->sig->first), val));
189 }
190
191 void VariantVisitor::visit(Trace::Bitmask *bitmask)
192 {
193     m_variant = QVariant::fromValue(ApiBitmask(bitmask));
194 }
195
196 void VariantVisitor::visit(Trace::Struct *str)
197 {
198     m_variant = QVariant::fromValue(ApiStruct(str));
199 }
200
201 void VariantVisitor::visit(Trace::Array *array)
202 {
203     m_variant = QVariant::fromValue(ApiArray(array));
204 }
205
206 void VariantVisitor::visit(Trace::Blob *blob)
207 {
208     //XXX
209     //FIXME: this is a nasty hack. Trace::Blob's can't
210     //   delete the contents in the destructor because
211     //   the data is being used by other calls. We piggy back
212     //   on that assumption and don't deep copy the data. If
213     //   Blob's will start deleting the data we will need to
214     //   start deep copying it or switch to using something like
215     //   Boost's shared_ptr or Qt's QSharedPointer to handle it
216     QByteArray barray = QByteArray::fromRawData(blob->buf, blob->size);
217     m_variant = QVariant(barray);
218 }
219
220 void VariantVisitor::visit(Trace::Pointer *ptr)
221 {
222     m_variant = QVariant::fromValue(ApiPointer(ptr->value));
223 }
224
225 ApiArray::ApiArray(const Trace::Array *arr)
226 {
227     init(arr);
228 }
229
230 ApiArray::ApiArray(const QList<QVariant> &vals)
231     : m_array(vals)
232 {
233 }
234
235 QString ApiArray::toString() const
236 {
237     QString str;
238     str += QLatin1String("[");
239     for(int i = 0; i < m_array.count(); ++i) {
240         const QVariant &var = m_array[i];
241         str += apiVariantToString(var);
242         if (i < m_array.count() - 1)
243             str += QLatin1String(", ");
244     }
245     str += QLatin1String("]");
246
247     return str;
248 }
249
250 void ApiArray::init(const Trace::Array *arr)
251 {
252     if (!arr)
253         return;
254
255     for (int i = 0; i < arr->values.size(); ++i) {
256         VariantVisitor vis;
257         arr->values[i]->visit(vis);
258
259         m_array.append(vis.variant());
260     }
261 }
262
263 QStaticText ApiTraceCall::staticText() const
264 {
265     if (m_staticText && !m_staticText->text().isEmpty())
266         return *m_staticText;
267
268     QVariantList argValues = arguments();
269
270     QString richText = QString::fromLatin1(
271         "<span style=\"font-weight:bold\">%1</span>(").arg(m_name);
272     for (int i = 0; i < m_argNames.count(); ++i) {
273         richText += QLatin1String("<span style=\"color:#0000ff\">");
274         QString argText = apiVariantToString(argValues[i]);
275
276         //if arguments are really long (e.g. shader text), cut them
277         // and elide it
278         if (argText.length() > 40) {
279             QString shortened = argText.mid(0, 40);
280             shortened[argText.length() - 5] = '.';
281             shortened[argText.length() - 4] = '.';
282             shortened[argText.length() - 3] = '.';
283             shortened[argText.length() - 2] = argText[argText.length() - 2];
284             shortened[argText.length() - 1] = argText[argText.length() - 1];
285             richText += shortened;
286         } else {
287             richText += argText;
288         }
289         richText += QLatin1String("</span>");
290         if (i < m_argNames.count() - 1)
291             richText += QLatin1String(", ");
292     }
293     richText += QLatin1String(")");
294     if (m_returnValue.isValid()) {
295         richText +=
296             QLatin1Literal(" = ") %
297             QLatin1Literal("<span style=\"color:#0000ff\">") %
298             apiVariantToString(m_returnValue) %
299             QLatin1Literal("</span>");
300     }
301
302     if (!m_staticText)
303         m_staticText = new QStaticText(richText);
304     else
305         m_staticText->setText(richText);
306     QTextOption opt;
307     opt.setWrapMode(QTextOption::NoWrap);
308     m_staticText->setTextOption(opt);
309     m_staticText->prepare();
310
311     return *m_staticText;
312 }
313
314 QString ApiTraceCall::toHtml() const
315 {
316     if (!m_richText.isEmpty())
317         return m_richText;
318
319     if (m_helpUrl.isEmpty()) {
320         m_richText = QString::fromLatin1(
321             "%1) <span style=\"font-weight:bold\">%2</span>(")
322                      .arg(m_index)
323                      .arg(m_name);
324     } else {
325         m_richText = QString::fromLatin1(
326             "%1) <span style=\"font-weight:bold\"><a href=\"%2\">%3</a></span>(")
327                       .arg(m_index)
328                       .arg(m_helpUrl.toString())
329                       .arg(m_name);
330     }
331
332     QVariantList argValues = arguments();
333     for (int i = 0; i < m_argNames.count(); ++i) {
334         m_richText += m_argNames[i] +
335                       QLatin1Literal(" = ") +
336                       QLatin1Literal("<span style=\"color:#0000ff\">") +
337                       apiVariantToString(argValues[i]) +
338                       QLatin1Literal("</span>");
339         if (i < m_argNames.count() - 1)
340             m_richText += QLatin1String(", ");
341     }
342     m_richText += QLatin1String(")");
343
344     if (m_returnValue.isValid()) {
345         m_richText +=
346             QLatin1String(" = ") +
347             QLatin1String("<span style=\"color:#0000ff\">") +
348             apiVariantToString(m_returnValue) +
349             QLatin1String("</span>");
350     }
351     m_richText.squeeze();
352     return m_richText;
353 }
354
355 QString ApiTraceCall::filterText() const
356 {
357     if (!m_filterText.isEmpty())
358         return m_filterText;
359
360     QVariantList argValues = arguments();
361     m_filterText = m_name + QLatin1Literal("(");
362     for (int i = 0; i < m_argNames.count(); ++i) {
363         m_filterText += m_argNames[i] +
364                         QLatin1Literal(" = ") +
365                         apiVariantToString(argValues[i]);
366         if (argValues[i].type() == QVariant::ByteArray) {
367             m_hasBinaryData = true;
368             m_binaryDataIndex = i;
369         }
370         if (i < m_argNames.count() - 1)
371             m_filterText += QLatin1String(", ");
372     }
373     m_filterText += QLatin1String(")");
374
375     if (m_returnValue.isValid()) {
376         m_filterText += QLatin1Literal(" = ") +
377                         apiVariantToString(m_returnValue);
378     }
379     m_filterText.squeeze();
380     return m_filterText;
381 }
382
383 QStaticText ApiTraceFrame::staticText() const
384 {
385     if (m_staticText && !m_staticText->text().isEmpty())
386         return *m_staticText;
387
388     QString richText =
389         QString::fromLatin1("<span style=\"font-weight:bold\">Frame %1</span>").arg(number);
390
391     if (!m_staticText)
392         m_staticText = new QStaticText(richText);
393
394     QTextOption opt;
395     opt.setWrapMode(QTextOption::NoWrap);
396     m_staticText->setTextOption(opt);
397     m_staticText->prepare();
398
399     return *m_staticText;
400 }
401
402 int ApiTraceCall::numChildren() const
403 {
404     return 0;
405 }
406
407 int ApiTraceFrame::numChildren() const
408 {
409     return calls.count();
410 }
411
412 ApiTraceFrame::ApiTraceFrame()
413     : ApiTraceEvent(ApiTraceEvent::Frame),
414       m_parentTrace(0)
415 {
416 }
417
418 ApiTraceCall::ApiTraceCall()
419     : ApiTraceEvent(ApiTraceEvent::Call),
420       m_hasBinaryData(false),
421       m_binaryDataIndex(0)
422 {
423 }
424
425 ApiTraceEvent::ApiTraceEvent()
426     : m_type(ApiTraceEvent::None),
427       m_staticText(0)
428 {
429 }
430
431 ApiTraceEvent::ApiTraceEvent(Type t)
432     : m_type(t),
433       m_staticText(0)
434 {
435 }
436
437 ApiTraceCall::~ApiTraceCall()
438 {
439 }
440
441 QVariantMap ApiTraceEvent::stateParameters() const
442 {
443     return m_state.parameters();
444 }
445
446 ApiTraceState ApiTraceEvent::state() const
447 {
448     return m_state;
449 }
450
451 void ApiTraceEvent::setState(const ApiTraceState &state)
452 {
453     m_state = state;
454 }
455
456 bool ApiTraceCall::hasBinaryData() const
457 {
458     return m_hasBinaryData;
459 }
460
461 int ApiTraceCall::binaryDataIndex() const
462 {
463     return m_binaryDataIndex;
464 }
465
466 ApiTraceState::ApiTraceState()
467 {
468 }
469
470 ApiTraceState::ApiTraceState(const QVariantMap &parsedJson)
471 {
472     m_parameters = parsedJson[QLatin1String("parameters")].toMap();
473     QVariantMap attachedShaders =
474         parsedJson[QLatin1String("shaders")].toMap();
475     QVariantMap::const_iterator itr;
476
477
478     for (itr = attachedShaders.constBegin(); itr != attachedShaders.constEnd();
479          ++itr) {
480         QString type = itr.key();
481         QString source = itr.value().toString();
482         m_shaderSources[type] = source;
483     }
484
485     QVariantList textureUnits =
486         parsedJson[QLatin1String("textures")].toList();
487     for (int i = 0; i < textureUnits.count(); ++i) {
488         QVariantMap unit = textureUnits[i].toMap();
489         for (itr = unit.constBegin(); itr != unit.constEnd(); ++itr) {
490             QVariantMap target = itr.value().toMap();
491             if (target.count()) {
492                 QVariantList levels = target[QLatin1String("levels")].toList();
493                 for (int j = 0; j < levels.count(); ++j) {
494                     QVariantMap level = levels[j].toMap();
495                     QVariantMap image = level[QLatin1String("image")].toMap();
496                     QSize size(image[QLatin1String("__width__")].toInt(),
497                                image[QLatin1String("__height__")].toInt());
498                     QString cls = image[QLatin1String("__class__")].toString();
499                     QString type = image[QLatin1String("__type__")].toString();
500                     bool normalized =
501                         image[QLatin1String("__normalized__")].toBool();
502                     int numChannels =
503                         image[QLatin1String("__channels__")].toInt();
504
505                     Q_ASSERT(type == QLatin1String("uint8"));
506                     Q_ASSERT(normalized == true);
507
508                     QByteArray dataArray =
509                         image[QLatin1String("__data__")].toByteArray();
510
511                     ApiTexture tex;
512                     tex.setSize(size);
513                     tex.setNumChannels(numChannels);
514                     tex.setLevel(j);
515                     tex.setUnit(i);
516                     tex.setTarget(itr.key());
517                     tex.contentsFromBase64(dataArray);
518
519                     m_textures.append(tex);
520                 }
521             }
522         }
523     }
524
525     QVariantMap fbos =
526         parsedJson[QLatin1String("framebuffer")].toMap();
527     for (itr = fbos.constBegin(); itr != fbos.constEnd(); ++itr) {
528         QVariantMap buffer = itr.value().toMap();
529         QSize size(buffer[QLatin1String("__width__")].toInt(),
530                    buffer[QLatin1String("__height__")].toInt());
531         QString cls = buffer[QLatin1String("__class__")].toString();
532         QString type = buffer[QLatin1String("__type__")].toString();
533         bool normalized = buffer[QLatin1String("__normalized__")].toBool();
534         int numChannels = buffer[QLatin1String("__channels__")].toInt();
535
536         Q_ASSERT(type == QLatin1String("uint8"));
537         Q_ASSERT(normalized == true);
538
539         QByteArray dataArray =
540             buffer[QLatin1String("__data__")].toByteArray();
541
542         ApiFramebuffer fbo;
543         fbo.setSize(size);
544         fbo.setNumChannels(numChannels);
545         fbo.setType(itr.key());
546         fbo.contentsFromBase64(dataArray);
547         m_framebuffers.append(fbo);
548     }
549 }
550
551 QVariantMap ApiTraceState::parameters() const
552 {
553     return m_parameters;
554 }
555
556 QMap<QString, QString> ApiTraceState::shaderSources() const
557 {
558     return m_shaderSources;
559 }
560
561 bool ApiTraceState::isEmpty() const
562 {
563     return m_parameters.isEmpty();
564 }
565
566 QList<ApiTexture> ApiTraceState::textures() const
567 {
568     return m_textures;
569 }
570
571 QList<ApiFramebuffer> ApiTraceState::framebuffers() const
572 {
573     return m_framebuffers;
574 }
575
576 QList<QVariant> ApiArray::values() const
577 {
578     return m_array;
579 }
580
581 int ApiTraceCall::index() const
582 {
583     return m_index;
584 }
585
586 QString ApiTraceCall::name() const
587 {
588     return m_name;
589 }
590
591 QStringList ApiTraceCall::argNames() const
592 {
593     return m_argNames;
594 }
595
596 QVariantList ApiTraceCall::arguments() const
597 {
598     if (m_editedValues.isEmpty())
599         return m_argValues;
600     else
601         return m_editedValues;
602 }
603
604 QVariant ApiTraceCall::returnValue() const
605 {
606     return m_returnValue;
607 }
608
609 QUrl ApiTraceCall::helpUrl() const
610 {
611     return m_helpUrl;
612 }
613
614 ApiTraceCall::ApiTraceCall(const Trace::Call *call)
615     : ApiTraceEvent(ApiTraceEvent::Call),
616       m_hasBinaryData(false),
617       m_binaryDataIndex(0)
618 {
619     m_name = QString::fromStdString(call->sig->name);
620     m_index = call->no;
621
622     QString argumentsText;
623     for (int i = 0; i < call->sig->arg_names.size(); ++i) {
624         m_argNames +=
625             QString::fromStdString(call->sig->arg_names[i]);
626     }
627     if (call->ret) {
628         VariantVisitor retVisitor;
629         call->ret->visit(retVisitor);
630         m_returnValue = retVisitor.variant();
631     }
632     for (int i = 0; i < call->args.size(); ++i) {
633         VariantVisitor argVisitor;
634         call->args[i]->visit(argVisitor);
635         m_argValues += argVisitor.variant();
636     }
637 }
638
639 void ApiTraceCall::setHelpUrl(const QUrl &url)
640 {
641     m_helpUrl = url;
642 }
643
644 void ApiTraceCall::setParentFrame(ApiTraceFrame *frame)
645 {
646     m_parentFrame = frame;
647 }
648
649 ApiTraceFrame * ApiTraceCall::parentFrame()const
650 {
651     return m_parentFrame;
652 }
653
654 ApiTraceEvent::~ApiTraceEvent()
655 {
656     delete m_staticText;
657 }
658
659 void ApiTraceCall::revert()
660 {
661     setEditedValues(QVariantList());
662 }
663
664 ApiTrace * ApiTraceFrame::parentTrace() const
665 {
666     return m_parentTrace;
667 }
668
669 void ApiTraceFrame::setParentTrace(ApiTrace *trace)
670 {
671     m_parentTrace = trace;
672 }
673
674 QVariantList ApiTraceCall::originalValues() const
675 {
676     return m_argValues;
677 }
678
679 void ApiTraceCall::setEditedValues(const QVariantList &lst)
680 {
681     ApiTrace *trace = 0;
682     if (m_parentFrame)
683         trace = m_parentFrame->parentTrace();
684
685     m_editedValues = lst;
686     //lets regenerate data
687     m_richText = QString();
688     m_filterText = QString();
689     delete m_staticText;
690     m_staticText = 0;
691
692     if (trace) {
693         if (!lst.isEmpty()) {
694             trace->callEdited(this);
695         } else {
696             trace->callReverted(this);
697         }
698     }
699 }
700
701 QVariantList ApiTraceCall::editedValues() const
702 {
703     return m_editedValues;
704 }
705
706 bool ApiTraceCall::edited() const
707 {
708     return !m_editedValues.isEmpty();
709 }
710
711 ApiEnum::ApiEnum(const QString &name, const QVariant &val)
712     : m_name(name),
713       m_value(val)
714 {
715 }
716
717 QString ApiEnum::toString() const
718 {
719     return m_name;
720 }
721
722 QVariant ApiEnum::value() const
723 {
724     return m_value;
725 }
726
727 QString ApiEnum::name() const
728 {
729     return m_name;
730 }
731
732 unsigned long long ApiBitmask::value() const
733 {
734     return m_value;
735 }
736
737 ApiBitmask::Signature ApiBitmask::signature() const
738 {
739     return m_sig;
740 }
741
742 ApiStruct::Signature ApiStruct::signature() const
743 {
744     return m_sig;
745 }
746
747 QList<QVariant> ApiStruct::values() const
748 {
749     return m_members;
750 }
751
752 unsigned long long ApiPointer::value() const
753 {
754     return m_value;
755 }
756