1 #include "mainwindow.h"
2 
3 #include "apitrace.h"
4 #include "apitracecall.h"
5 #include "apicalldelegate.h"
6 #include "apitracemodel.h"
7 #include "apitracefilter.h"
8 #include "argumentseditor.h"
9 #include "imageviewer.h"
10 #include "jumpwidget.h"
11 #include "profiledialog.h"
12 #include "retracer.h"
13 #include "searchwidget.h"
14 #include "settingsdialog.h"
15 #include "shaderssourcewidget.h"
16 #include "tracedialog.h"
17 #include "traceprocess.h"
18 #include "trimprocess.h"
19 #include "thumbnail.h"
20 #include "ui_retracerdialog.h"
21 #include "ui_profilereplaydialog.h"
22 #include "vertexdatainterpreter.h"
23 #include "trace_profiler.hpp"
24 #include "image.hpp"
25 #include "leaktracethread.h"
26 
27 #include <QAction>
28 #include <QApplication>
29 #include <QDebug>
30 #include <QDesktopServices>
31 #include <QDesktopWidget>
32 #include <QDir>
33 #include <QFileDialog>
34 #include <QLineEdit>
35 #include <QMessageBox>
36 #include <QProgressBar>
37 #include <QToolBar>
38 #include <QUrl>
39 #include <QVBoxLayout>
40 #include <QTextBrowser>
41 #include <QSettings>
42 
43 
MainWindow()44 MainWindow::MainWindow()
45     : QMainWindow(),
46       m_api(trace::API_GL),
47       m_initalCallNum(-1),
48       m_selectedEvent(0),
49       m_stateEvent(0),
50       m_nonDefaultsLookupEvent(0)
51 {
52     m_ui.setupUi(this);
53     updateActionsState(false);
54     initObjects();
55     initConnections();
56     updateRecentLaunchesMenu();
57 }
58 
~MainWindow()59 MainWindow::~MainWindow()
60 {
61     delete m_trace;
62     m_trace = 0;
63 
64     delete m_proxyModel;
65     delete m_model;
66 }
67 
createTrace(const RecentLaunch * optionLaunch)68 void MainWindow::createTrace(const RecentLaunch* optionLaunch)
69 {
70     if (!m_traceProcess->canTrace()) {
71         QMessageBox::warning(
72             this,
73             tr("Unsupported"),
74             tr("Current configuration doesn't support tracing."));
75         return;
76     }
77 
78     TraceDialog dialog;
79 
80     if(optionLaunch) {
81         dialog.setApi(optionLaunch->api);
82         dialog.setApplicationPath(optionLaunch->execPath);
83         dialog.setWorkingDirPath(optionLaunch->workingDir);
84         dialog.setArguments(optionLaunch->args);
85     }
86 
87     if (dialog.exec() == QDialog::Accepted) {
88         qDebug()<< "App : " <<dialog.applicationPath();
89         qDebug()<< "  Arguments: "<<dialog.arguments();
90 
91         RecentLaunch rl;
92         rl.api = dialog.api();
93         rl.execPath = dialog.applicationPath();
94         rl.workingDir = dialog.workingDirPath();
95         rl.args = dialog.arguments();
96         addRecentLaunch(rl);
97         updateRecentLaunchesMenu();
98 
99         m_traceProcess->setApi(rl.api);
100         m_traceProcess->setExecutablePathAndWorkingDir(
101                     rl.execPath, rl.workingDir);
102         m_traceProcess->setArguments(rl.args);
103         m_traceProcess->start();
104     }
105 }
106 
openTrace()107 void MainWindow::openTrace()
108 {
109     QString fileName =
110             QFileDialog::getOpenFileName(
111                 this,
112                 tr("Open Trace"),
113                 QDir::homePath(),
114                 tr("Trace Files (*.trace)"));
115 
116     if (!fileName.isEmpty() && QFile::exists(fileName)) {
117         newTraceFile(fileName);
118     }
119 }
120 
saveTrace()121 void MainWindow::saveTrace()
122 {
123     QString localFile = m_trace->fileName();
124 
125     QString fileName =
126             QFileDialog::getSaveFileName(
127                 this,
128                 tr("Save Trace As"),
129                 QFileInfo(localFile).fileName(),
130                 tr("Trace Files (*.trace);;All Files (*)"));
131 
132     if (!fileName.isEmpty()) {
133         // copy won't overwrite existing files!
134         if (QFile::exists(fileName)) {
135             if (!QFile::remove(fileName)) {
136                 QMessageBox::warning(
137                     this, tr("Could not overwrite file"),
138                     tr("The existing file %0 could not be replaced!")
139                         .arg(fileName));
140             }
141         }
142         QFile::copy(localFile, fileName);
143     }
144 }
145 
loadTrace(const QString & fileName,int callNum)146 void MainWindow::loadTrace(const QString &fileName, int callNum)
147 {
148     if (!QFile::exists(fileName)) {
149         QMessageBox::warning(this, tr("File Missing"),
150                              tr("File '%1' doesn't exist.").arg(fileName));
151         return;
152     }
153 
154     m_initalCallNum = callNum;
155     newTraceFile(fileName);
156 }
157 
setRemoteTarget(const QString & host)158 void MainWindow::setRemoteTarget(const QString &host)
159 {
160     m_retracer->setRemoteTarget(host);
161 }
162 
callItemSelected(const QModelIndex & index)163 void MainWindow::callItemSelected(const QModelIndex &index)
164 {
165     ApiTraceEvent *event =
166         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
167 
168     if (event && event->type() == ApiTraceEvent::Call) {
169         ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
170         m_ui.detailsDock->setWindowTitle(
171             tr("Details View. Frame %1, Call %2")
172             .arg(call->parentFrame() ? call->parentFrame()->number : 0)
173             .arg(call->index()));
174         m_ui.detailsWebView->setHtml(call->toHtml());
175         m_ui.detailsDock->show();
176         m_ui.callView->scrollTo(index);
177         if (call->hasBinaryData()) {
178             QByteArray data =
179                 call->arguments()[call->binaryDataIndex()].toByteArray();
180             m_vdataInterpreter->setData(data);
181 
182             QVector<QVariant> args = call->arguments();
183             for (int i = 0; i < call->argNames().count(); ++i) {
184                 QString name = call->argNames()[i];
185                 if (name == QLatin1String("stride")) {
186                     int stride = args[i].toInt();
187                     m_ui.vertexStrideSB->setValue(stride);
188                 } else if (name == QLatin1String("size")) {
189                     int components = args[i].toInt();
190                     m_ui.vertexComponentsSB->setValue(components);
191                 } else if (name == QLatin1String("type")) {
192                     QString val = args[i].toString();
193                     int textIndex = m_ui.vertexTypeCB->findText(val);
194                     if (textIndex >= 0) {
195                         m_ui.vertexTypeCB->setCurrentIndex(textIndex);
196                     }
197                 }
198             }
199         }
200         m_ui.backtraceBrowser->setText(call->backtrace());
201         m_ui.backtraceDock->setVisible(!call->backtrace().isNull());
202         m_ui.vertexDataDock->setVisible(call->hasBinaryData());
203         m_selectedEvent = call;
204     } else {
205         if (event && event->type() == ApiTraceEvent::Frame) {
206             m_selectedEvent = static_cast<ApiTraceFrame*>(event);
207         } else {
208             m_selectedEvent = 0;
209         }
210         m_ui.detailsDock->hide();
211         m_ui.backtraceDock->hide();
212         m_ui.vertexDataDock->hide();
213     }
214     if (m_selectedEvent && m_selectedEvent->hasState()) {
215         fillStateForFrame();
216     } else {
217         m_ui.stateDock->hide();
218     }
219 }
220 
callItemActivated(const QModelIndex & index)221 void MainWindow::callItemActivated(const QModelIndex &index) {
222     lookupState();
223 }
224 
replayStart()225 void MainWindow::replayStart()
226 {
227     if (m_trace->isSaving()) {
228         QMessageBox::warning(
229             this,
230             tr("Trace Saving"),
231             tr("QApiTrace is currently saving the edited trace file. "
232                "Please wait until it finishes and try again."));
233         return;
234     }
235 
236     QDialog dlg;
237     Ui_RetracerDialog dlgUi;
238     dlgUi.setupUi(&dlg);
239 
240     dlgUi.doubleBufferingCB->setChecked(
241         m_retracer->isDoubleBuffered());
242 
243     dlgUi.errorCheckCB->setChecked(
244         !m_retracer->isBenchmarking());
245 
246     dlgUi.singlethreadCB->setChecked(
247         m_retracer->isSinglethread());
248 
249     dlgUi.coreProfileCB->setChecked(
250         m_retracer->isCoreProfile());
251 
252     if (dlg.exec() == QDialog::Accepted) {
253         m_retracer->setDoubleBuffered(
254             dlgUi.doubleBufferingCB->isChecked());
255 
256         m_retracer->setBenchmarking(
257             !dlgUi.errorCheckCB->isChecked());
258 
259         m_retracer->setSinglethread(
260             dlgUi.singlethreadCB->isChecked());
261 
262         m_retracer->setCoreProfile(
263             dlgUi.coreProfileCB->isChecked());
264 
265         m_retracer->setProfiling(false, false, false);
266 
267         replayTrace(false, false);
268     }
269 }
270 
replayProfile()271 void MainWindow::replayProfile()
272 {
273     if (m_trace->isSaving()) {
274         QMessageBox::warning(
275             this,
276             tr("Trace Saving"),
277             tr("QApiTrace is currently saving the edited trace file. "
278                "Please wait until it finishes and try again."));
279         return;
280     }
281 
282     QDialog dlg;
283     Ui_ProfileReplayDialog dlgUi;
284     dlgUi.setupUi(&dlg);
285 
286     if (dlg.exec() == QDialog::Accepted) {
287         m_retracer->setProfiling(
288             dlgUi.gpuTimesCB->isChecked(),
289             dlgUi.cpuTimesCB->isChecked(),
290             dlgUi.pixelsDrawnCB->isChecked());
291 
292         replayTrace(false, false);
293     }
294 }
295 
replayStop()296 void MainWindow::replayStop()
297 {
298     m_retracer->quit();
299     updateActionsState(true, true);
300 }
301 
newTraceFile(const QString & fileName)302 void MainWindow::newTraceFile(const QString &fileName)
303 {
304     qDebug()<< "Loading:" << fileName;
305 
306     m_progressBar->setValue(0);
307     m_trace->setFileName(fileName);
308 
309     if (fileName.isEmpty()) {
310         updateActionsState(false);
311         setWindowTitle(tr("QApiTrace"));
312     } else {
313         updateActionsState(true);
314         QFileInfo info(fileName);
315         setWindowTitle(
316             tr("QApiTrace - %1").arg(info.fileName()));
317     }
318 }
319 
replayFinished(const QString & message)320 void MainWindow::replayFinished(const QString &message)
321 {
322     updateActionsState(true);
323     m_progressBar->hide();
324     statusBar()->showMessage(message, 2000);
325     m_stateEvent = 0;
326     m_ui.actionShowErrorsDock->setEnabled(m_trace->hasErrors());
327     m_ui.errorsDock->setVisible(m_trace->hasErrors());
328     if (!m_trace->hasErrors()) {
329         m_ui.errorsTreeWidget->clear();
330     }
331 }
332 
replayError(const QString & message)333 void MainWindow::replayError(const QString &message)
334 {
335     updateActionsState(true);
336     m_stateEvent = 0;
337     m_nonDefaultsLookupEvent = 0;
338 
339     m_progressBar->hide();
340     statusBar()->showMessage(
341         tr("Replaying unsuccessful."), 2000);
342     QMessageBox::warning(
343         this, tr("Replay Failed"), message);
344 }
345 
loadError(const QString & message)346 void MainWindow::loadError(const QString &message)
347 {
348     m_progressBar->hide();
349     statusBar()->showMessage(
350         tr("Load unsuccessful."), 2000);
351     QMessageBox::warning(
352         this, tr("Load Failed"), message);
353 }
354 
startedLoadingTrace()355 void MainWindow::startedLoadingTrace()
356 {
357     Q_ASSERT(m_trace);
358     m_progressBar->show();
359     QFileInfo info(m_trace->fileName());
360     statusBar()->showMessage(
361         tr("Loading %1...").arg(info.fileName()));
362 }
363 
finishedLoadingTrace()364 void MainWindow::finishedLoadingTrace()
365 {
366     m_progressBar->hide();
367     if (!m_trace) {
368         return;
369     }
370     m_api = m_trace->api();
371     QFileInfo info(m_trace->fileName());
372     statusBar()->showMessage(
373         tr("Loaded %1").arg(info.fileName()), 3000);
374     if (m_initalCallNum >= 0) {
375         m_trace->findCallIndex(m_initalCallNum);
376         m_initalCallNum = -1;
377     } else {
378        m_trace->finishedParsing();
379     }
380 }
381 
replayTrace(bool dumpState,bool dumpThumbnails)382 void MainWindow::replayTrace(bool dumpState, bool dumpThumbnails)
383 {
384     if (m_trace->fileName().isEmpty()) {
385         return;
386     }
387 
388     m_retracer->setFileName(m_trace->fileName());
389     m_retracer->setAPI(m_api);
390     m_retracer->setCaptureState(dumpState);
391     m_retracer->setMsaaResolve(m_ui.surfacesResolveMSAA->isChecked());
392     m_retracer->setCaptureThumbnails(dumpThumbnails);
393     if (m_retracer->captureState() && m_selectedEvent) {
394         int index = 0;
395         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
396             index = static_cast<ApiTraceCall*>(m_selectedEvent)->index();
397         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
398             ApiTraceFrame *frame =
399                 static_cast<ApiTraceFrame*>(m_selectedEvent);
400             if (frame->isEmpty()) {
401                 //XXX i guess we could still get the current state
402                 qDebug()<<"tried to get a state for an empty frame";
403                 return;
404             }
405             index = frame->lastCallIndex();
406         } else {
407             qDebug()<<"Unknown event type";
408             return;
409         }
410         m_retracer->setCaptureAtCallNumber(index);
411     }
412     if (m_trace->isMissingThumbnails()) {
413         m_retracer->resetThumbnailsToCapture();
414         m_trace->iterateMissingThumbnails(this, this->thumbnailCallback);
415         m_trace->resetMissingThumbnails();
416     }
417     m_retracer->start();
418 
419     m_ui.actionStop->setEnabled(true);
420     m_progressBar->show();
421     if (dumpState || dumpThumbnails) {
422         if (dumpState && dumpThumbnails) {
423             statusBar()->showMessage(
424                 tr("Looking up the state and capturing thumbnails..."));
425         } else if (dumpState) {
426             statusBar()->showMessage(
427                 tr("Looking up the state..."));
428         } else if (dumpThumbnails) {
429             statusBar()->showMessage(
430                 tr("Capturing thumbnails..."));
431         }
432     } else if (m_retracer->isProfiling()) {
433         statusBar()->showMessage(
434                     tr("Profiling draw calls in trace file..."));
435     } else {
436         statusBar()->showMessage(
437             tr("Replaying the trace file..."));
438     }
439 }
440 
trimEvent()441 void MainWindow::trimEvent()
442 {
443     int trimIndex = 0;
444 
445     Q_ASSERT(m_trimEvent->type() == ApiTraceEvent::Call ||
446              m_trimEvent->type() == ApiTraceEvent::Frame);
447 
448     if (m_trimEvent->type() == ApiTraceEvent::Call) {
449         ApiTraceCall *call = static_cast<ApiTraceCall*>(m_trimEvent);
450         trimIndex = call->index();
451     } else if (m_trimEvent->type() == ApiTraceEvent::Frame) {
452         ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(m_trimEvent);
453         const QList<ApiTraceFrame*> frames = m_trace->frames();
454         trimIndex = frame->lastCallIndex();
455     }
456 
457     m_trimProcess->setTracePath(m_trace->fileName());
458     m_trimProcess->setTrimIndex(trimIndex);
459 
460     m_trimProcess->start();
461 }
462 
lookupState()463 void MainWindow::lookupState()
464 {
465     if (!m_selectedEvent) {
466         QMessageBox::warning(
467             this, tr("Unknown Event"),
468             tr("To inspect the state select an event in the event list."));
469         return;
470     }
471     if (m_trace->isSaving()) {
472         QMessageBox::warning(
473             this,
474             tr("Trace Saving"),
475             tr("QApiTrace is currently saving the edited trace file. "
476                "Please wait until it finishes and try again."));
477         return;
478     }
479     m_stateEvent = m_selectedEvent;
480     replayTrace(true, false);
481 }
482 
showThumbnails()483 void MainWindow::showThumbnails()
484 {
485     replayTrace(false, true);
486 }
487 
trim()488 void MainWindow::trim()
489 {
490     if (!m_selectedEvent) {
491         QMessageBox::warning(
492             this, tr("Unknown Event"),
493             tr("To trim select a frame or an event in the event list."));
494         return;
495     }
496     m_trimEvent = m_selectedEvent;
497     trimEvent();
498 }
499 
500 static void
variantToString(const QVariant & var,QString & str)501 variantToString(const QVariant &var, QString &str)
502 {
503     if (var.type() == QVariant::List) {
504         QVector<QVariant> lst = var.toList().toVector();
505         str += QLatin1String("[");
506         for (int i = 0; i < lst.count(); ++i) {
507             QVariant val = lst[i];
508             variantToString(val, str);
509             if (i < lst.count() - 1)
510                 str += QLatin1String(", ");
511         }
512         str += QLatin1String("]");
513     } else if (var.type() == QVariant::Map) {
514         Q_ASSERT(!"unsupported state type");
515     } else if (var.type() == QVariant::Hash) {
516         Q_ASSERT(!"unsupported state type");
517     } else {
518         str += var.toString();
519     }
520 }
521 
522 static QTreeWidgetItem *
523 variantToItem(const QString &key, const QVariant &var,
524               const QVariant &defaultVar);
525 
526 static void
variantMapToItems(const QVariantMap & map,const QVariantMap & defaultMap,QList<QTreeWidgetItem * > & items)527 variantMapToItems(const QVariantMap &map, const QVariantMap &defaultMap,
528                   QList<QTreeWidgetItem *> &items)
529 {
530     QVariantMap::const_iterator itr;
531     for (itr = map.constBegin(); itr != map.constEnd(); ++itr) {
532         QString key = itr.key();
533         QVariant var = itr.value();
534         QVariant defaultVar = defaultMap[key];
535 
536         QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
537         if (item) {
538             items.append(item);
539         }
540     }
541 }
542 
543 static void
variantListToItems(const QVector<QVariant> & lst,const QVector<QVariant> & defaultLst,QList<QTreeWidgetItem * > & items)544 variantListToItems(const QVector<QVariant> &lst,
545                    const QVector<QVariant> &defaultLst,
546                    QList<QTreeWidgetItem *> &items)
547 {
548     int width = QString::number(lst.count()).length();
549     for (int i = 0; i < lst.count(); ++i) {
550         QString key = QString::number(i).rightJustified(width, ' ');
551         QVariant var = lst[i];
552         QVariant defaultVar;
553 
554         if (i < defaultLst.count()) {
555             defaultVar = defaultLst[i];
556         }
557 
558         QTreeWidgetItem *item = variantToItem(key, var, defaultVar);
559         if (item) {
560             items.append(item);
561         }
562     }
563 }
564 
565 // Get the depth (dimensionality) of the variant:
566 //
567 // It will return:
568 //  0: scalar
569 //  1: vector (up to 4 elems)
570 //  2: matrix (up to 4x4 elements)
571 //  3: array
572 //  4: map, etc.
573 static unsigned
getVariantDepth(const QVariant & var)574 getVariantDepth(const QVariant &var)
575 {
576     if (var.type() == QVariant::List) {
577         QVector<QVariant> lst = var.toList().toVector();
578         unsigned maxDepth = 0;
579         for (int i = 0; i < lst.count(); ++i) {
580             unsigned elemDepth = getVariantDepth(lst[i]);
581             if (elemDepth > maxDepth) {
582                 if (elemDepth >= 4) {
583                     return elemDepth;
584                 }
585                 maxDepth = elemDepth;
586             }
587         }
588         if (lst.count() > 1) {
589             if (lst.count() > 4) {
590                 return 3;
591             }
592             maxDepth += 1;
593         }
594         return maxDepth;
595     } else if (var.type() == QVariant::Map) {
596         return 4;
597     } else if (var.type() == QVariant::Hash) {
598         return 4;
599     } else {
600         return 0;
601     }
602 }
603 
604 static QTreeWidgetItem *
variantToItem(const QString & key,const QVariant & var,const QVariant & defaultVar)605 variantToItem(const QString &key, const QVariant &var,
606               const QVariant &defaultVar)
607 {
608     if (var == defaultVar) {
609         return NULL;
610     }
611 
612     QString val;
613 
614     bool deep = getVariantDepth(var) >= 3;
615     if (!deep) {
616         variantToString(var, val);
617     }
618 
619     //qDebug()<<"key = "<<key;
620     //qDebug()<<"val = "<<val;
621     QStringList lst;
622     lst += key;
623     lst += val;
624 
625     QTreeWidgetItem *item = new QTreeWidgetItem((QTreeWidgetItem *)0, lst);
626 
627     if (deep) {
628         QList<QTreeWidgetItem *> children;
629         if (var.type() == QVariant::Map) {
630             QVariantMap map = var.toMap();
631             QVariantMap defaultMap = defaultVar.toMap();
632             variantMapToItems(map, defaultMap, children);
633         }
634         if (var.type() == QVariant::List) {
635             QVector<QVariant> lst = var.toList().toVector();
636             QVector<QVariant> defaultLst = defaultVar.toList().toVector();
637             variantListToItems(lst, defaultLst, children);
638         }
639         item->addChildren(children);
640     }
641 
642     return item;
643 }
644 
addSurfaceItem(const ApiSurface & surface,const QString & label,QTreeWidgetItem * parent,QTreeWidget * tree,bool opaque,bool alpha)645 static void addSurfaceItem(const ApiSurface &surface,
646                            const QString &label,
647                            QTreeWidgetItem *parent,
648                            QTreeWidget *tree,
649                            bool opaque, bool alpha)
650 {
651     QIcon icon(QPixmap::fromImage(surface.calculateThumbnail(opaque, alpha)));
652     QTreeWidgetItem *item = new QTreeWidgetItem(parent);
653     item->setIcon(0, icon);
654 
655     int width = surface.size().width();
656     int height = surface.size().height();
657     QString descr =
658         QString::fromLatin1("%1, %2, %3 x %4")
659         .arg(label)
660         .arg(surface.formatName())
661         .arg(width)
662         .arg(height);
663 
664     QString toolTip;
665     toolTip += QString::fromLatin1("label = %1\n").arg(label);
666     toolTip += QString::fromLatin1("format = %1\n").arg(surface.formatName());
667     toolTip += QString::fromLatin1("width = %1\n").arg(width);
668     toolTip += QString::fromLatin1("height = %1\n").arg(height);
669     item->setToolTip(0, toolTip);
670     item->setToolTip(1, toolTip);
671 
672     //item->setText(1, descr);
673     QLabel *l = new QLabel(descr, tree);
674     l->setWordWrap(true);
675     tree->setItemWidget(item, 1, l);
676 
677     item->setData(0, Qt::UserRole, surface.data());
678 }
679 
addSurface(const ApiTexture & image,QTreeWidgetItem * parent)680 void MainWindow::addSurface(const ApiTexture &image, QTreeWidgetItem *parent) {
681     addSurface(image, image.label(), parent);
682 }
683 
addSurface(const ApiFramebuffer & fbo,QTreeWidgetItem * parent)684 void MainWindow::addSurface(const ApiFramebuffer &fbo, QTreeWidgetItem *parent) {
685     addSurface(fbo, fbo.type(), parent);
686 }
687 
addSurface(const ApiSurface & surface,const QString & label,QTreeWidgetItem * parent)688 void MainWindow::addSurface(const ApiSurface &surface, const QString &label,
689                             QTreeWidgetItem *parent)
690 {
691     addSurfaceItem(surface, label, parent,
692                    m_ui.surfacesTreeWidget, m_ui.surfacesOpaqueCB->isChecked(),
693                    m_ui.surfacesAlphaCB->isChecked());
694 }
695 
696 template <typename Surface>
addSurfaces(const QList<Surface> & surfaces,const char * label)697 void MainWindow::addSurfaces(const QList<Surface> &surfaces, const char *label) {
698     if (!surfaces.isEmpty()) {
699         QTreeWidgetItem *imageItem = new QTreeWidgetItem(m_ui.surfacesTreeWidget);
700         imageItem->setText(0, tr(label));
701         if (surfaces.count() <= 6) {
702             imageItem->setExpanded(true);
703         }
704         for (int i = 0; i < surfaces.count(); ++i) {
705             addSurface(surfaces[i], imageItem);
706         }
707     }
708 }
709 
shortenReferencingShaderNames(QStringList referencingShaders)710 QStringList shortenReferencingShaderNames(QStringList referencingShaders)
711 {
712     static QMap<QString, QString> map = {
713         {"GL_REFERENCED_BY_VERTEX_SHADER", "VS"},
714         {"GL_REFERENCED_BY_TESS_CONTROL_SHADER", "TCS"},
715         {"GL_REFERENCED_BY_TESS_EVALUATION_SHADER", "TES"},
716         {"GL_REFERENCED_BY_GEOMETRY_SHADER", "GS"},
717         {"GL_REFERENCED_BY_FRAGMENT_SHADER", "FS"},
718         {"GL_REFERENCED_BY_COMPUTE_SHADER", "CS"}};
719     for (auto &referencingShader : referencingShaders) {
720         assert(map.count(referencingShader));
721         referencingShader = map[referencingShader];
722     }
723     return referencingShaders;
724 }
725 
getReferencingShaders(QTreeWidgetItem * bufferItem)726 QStringList getReferencingShaders(QTreeWidgetItem *bufferItem)
727 {
728     QStringList referencingShaders;
729     for (int i = 0; i < bufferItem->childCount(); ++i) {
730         const auto &text = bufferItem->child(i)->text(0);
731         if (text.startsWith("GL_REFERENCED_BY_") && text.endsWith("_SHADER")) {
732             referencingShaders.append(text);
733         }
734     }
735     return referencingShaders;
736 }
737 
setValueOfSSBBItem(const ApiTraceState & state,QTreeWidgetItem * bufferItem)738 void setValueOfSSBBItem(const ApiTraceState &state, QTreeWidgetItem *bufferItem)
739 {
740     assert(bufferItem);
741     const auto &bufferBindingItem = bufferItem->child(0);
742     assert(bufferBindingItem->text(0) == "GL_BUFFER_BINDING");
743     const int bufferBindingIndex = bufferBindingItem->text(1).toInt();
744     qDebug() << bufferBindingIndex;
745     assert(state.parameters().count("GL_SHADER_STORAGE_BUFFER"));
746     assert(state.parameters()["GL_SHADER_STORAGE_BUFFER"].toMap().count("i"));
747     assert(bufferBindingIndex < state.parameters()["GL_SHADER_STORAGE_BUFFER"]
748                                     .toMap()["i"]
749                                     .toList()
750                                     .size());
751     const auto &SSB = state.parameters()["GL_SHADER_STORAGE_BUFFER"]
752                           .toMap()["i"]
753                           .toList()[bufferBindingIndex]
754                           .toMap();
755 
756     assert(SSB.count("GL_SHADER_STORAGE_BUFFER_START"));
757     auto start = SSB["GL_SHADER_STORAGE_BUFFER_START"].toInt();
758 
759     assert(SSB.count("GL_SHADER_STORAGE_BUFFER_SIZE"));
760     auto size = SSB["GL_SHADER_STORAGE_BUFFER_SIZE"].toInt();
761 
762     assert(SSB.count("GL_SHADER_STORAGE_BUFFER_BINDING"));
763     auto bufferName = SSB["GL_SHADER_STORAGE_BUFFER_BINDING"].toInt();
764 
765     // Build overview text like:
766     // "Binding 1 in VS, FS; Buffer 16 (6 Bytes starting at 2)"
767     QString bindingText = QString("Binding %0").arg(bufferBindingIndex);
768 
769     QStringList referencingShaders =
770         shortenReferencingShaderNames(getReferencingShaders(bufferItem));
771     if (!referencingShaders.empty()) {
772         bindingText += " in ";
773         bindingText += referencingShaders.join(", ");
774     }
775 
776     QString bufferText;
777     if (bufferName != 0) {
778         bufferText = QString("Buffer %0").arg(bufferName);
779         if (size != 0) {
780             if (start != 0) {
781                 bufferText +=
782                     QString(" (%0 Bytes starting at %1)").arg(size).arg(start);
783             } else {
784                 bufferText += QString(" (first %0 Bytes)").arg(size);
785             }
786         } else {
787             if (start != 0) {
788                 bufferText += QString(" (starting at offset %0)").arg(start);
789             }
790         }
791     }
792     if (bufferText.isEmpty()) {
793         bufferItem->setText(1, bindingText);
794     } else {
795         bufferItem->setText(1, bindingText + "; " + bufferText);
796     }
797 }
798 
updateSurfacesView()799 void MainWindow::updateSurfacesView()
800 {
801     updateSurfacesView(*m_selectedEvent->state());
802 }
803 
updateSurfacesView(const ApiTraceState & state)804 void MainWindow::updateSurfacesView(const ApiTraceState &state)
805 {
806     const QList<ApiTexture> &textures =
807         state.textures();
808     const QList<ApiFramebuffer> &fbos =
809         state.framebuffers();
810 
811     m_ui.surfacesTreeWidget->clear();
812     if (textures.isEmpty() && fbos.isEmpty()) {
813         m_ui.surfacesTab->setDisabled(false);
814     } else {
815         m_ui.surfacesTreeWidget->setIconSize(QSize(THUMBNAIL_SIZE, THUMBNAIL_SIZE));
816         addSurfaces(textures, "Textures");
817         addSurfaces(fbos, "Framebuffers");
818         m_ui.surfacesTab->setEnabled(true);
819     }
820 }
821 
fillStateForFrame()822 void MainWindow::fillStateForFrame()
823 {
824     if (!m_selectedEvent || !m_selectedEvent->hasState()) {
825         return;
826     }
827 
828     if (m_nonDefaultsLookupEvent) {
829         m_ui.nonDefaultsCB->blockSignals(true);
830         m_ui.nonDefaultsCB->setChecked(true);
831         m_ui.nonDefaultsCB->blockSignals(false);
832     }
833 
834     bool nonDefaults = m_ui.nonDefaultsCB->isChecked();
835     QVariantMap defaultParams;
836     if (nonDefaults) {
837         ApiTraceState defaultState = m_trace->defaultState();
838         defaultParams = defaultState.parameters();
839     }
840 
841     const ApiTraceState &state = *m_selectedEvent->state();
842     m_ui.stateTreeWidget->clear();
843     QList<QTreeWidgetItem *> items;
844     variantMapToItems(state.parameters(), defaultParams, items);
845     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
846 
847     QMap<QString, QString> shaderSources = state.shaderSources();
848     if (shaderSources.isEmpty()) {
849         m_sourcesWidget->setShaders(shaderSources);
850     } else {
851         m_sourcesWidget->setShaders(shaderSources);
852     }
853 
854     m_ui.uniformsTreeWidget->clear();
855     QList<QTreeWidgetItem *> uniformsItems;
856     variantMapToItems(state.uniforms(), QVariantMap(), uniformsItems);
857     m_ui.uniformsTreeWidget->insertTopLevelItems(0, uniformsItems);
858 
859     m_ui.buffersTreeWidget->clear();
860     QList<QTreeWidgetItem *> buffersItems;
861     variantMapToItems(state.buffers(), QVariantMap(), buffersItems);
862     m_ui.buffersTreeWidget->insertTopLevelItems(0, buffersItems);
863 
864     updateSurfacesView(state);
865     m_ui.stateDock->show();
866 
867     {
868         m_ui.ssbsTreeWidget->clear();
869         QList<QTreeWidgetItem *> buffersItems;
870         variantMapToItems(state.shaderStorageBufferBlocks(), QVariantMap(),
871                           buffersItems);
872         const bool hasSSBs = buffersItems.size() > 0;
873         for (auto const &bufferItem : buffersItems) {
874             setValueOfSSBBItem(state, bufferItem);
875         }
876         m_ui.ssbsTreeWidget->insertTopLevelItems(0, buffersItems);
877         m_ui.ssbTab->setEnabled(hasSSBs);
878     }
879 }
880 
showSettings()881 void MainWindow::showSettings()
882 {
883     SettingsDialog dialog;
884     dialog.setFilterModel(m_proxyModel);
885 
886     dialog.exec();
887 }
888 
leakTrace()889 void MainWindow::leakTrace()
890 {
891     LeakTraceThread *t=new LeakTraceThread(m_trace->fileName());
892 
893     connect (t,SIGNAL(finished()),this,SLOT(leakTraceFinished()));
894 
895     connect (t,SIGNAL(leakTraceErrors(const QList<ApiTraceError> &)),
896             this,SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
897 
898     t->start();
899 }
900 
leakTraceFinished()901 void MainWindow::leakTraceFinished(){
902 
903     LeakTraceThread *t = qobject_cast<LeakTraceThread*>(sender());
904 
905     m_ui.errorsDock->setVisible(t->hasError());
906 
907     delete t;
908 }
909 
showSurfacesMenu(const QPoint & pos)910 void MainWindow::showSurfacesMenu(const QPoint &pos)
911 {
912     QTreeWidget *tree = m_ui.surfacesTreeWidget;
913     QTreeWidgetItem *item = tree->itemAt(pos);
914     if (!item) {
915         return;
916     }
917 
918     QMenu menu(tr("Surfaces"), this);
919 
920     QAction *act = menu.addAction(tr("View Image"));
921     act->setStatusTip(tr("View the currently selected surface"));
922     connect(act, SIGNAL(triggered()),
923             SLOT(showSelectedSurface()));
924 
925     act = menu.addAction(tr("Save Image"));
926     act->setStatusTip(tr("Save the currently selected surface"));
927     connect(act, SIGNAL(triggered()),
928             SLOT(saveSelectedSurface()));
929 
930     menu.exec(tree->viewport()->mapToGlobal(pos));
931 }
932 
showSelectedSurface()933 void MainWindow::showSelectedSurface()
934 {
935     QTreeWidgetItem *item =
936         m_ui.surfacesTreeWidget->currentItem();
937     if (!item) {
938         return;
939     }
940 
941     QVariant var = item->data(0, Qt::UserRole);
942     if (!var.isValid()) {
943         return;
944     }
945 
946     ImageViewer *viewer =
947         new ImageViewer(this, m_ui.surfacesOpaqueCB->isChecked(),
948                               m_ui.surfacesAlphaCB->isChecked());
949 
950     QString title;
951     if (selectedCall()) {
952         title = tr("QApiTrace - Surface at %1 (%2)")
953                 .arg(selectedCall()->name())
954                 .arg(selectedCall()->index());
955     } else {
956         title = tr("QApiTrace - Surface Viewer");
957     }
958     viewer->setWindowTitle(title);
959 
960     viewer->setAttribute(Qt::WA_DeleteOnClose, true);
961 
962     QByteArray data = var.value<QByteArray>();
963     viewer->setData(data);
964 
965     viewer->show();
966     viewer->raise();
967     viewer->activateWindow();
968 }
969 
initObjects()970 void MainWindow::initObjects()
971 {
972     m_ui.stateTreeWidget->sortByColumn(0, Qt::AscendingOrder);
973     m_ui.uniformsTreeWidget->sortByColumn(0, Qt::AscendingOrder);
974 
975     m_sourcesWidget = new ShadersSourceWidget(m_ui.shadersTab);
976     QVBoxLayout *layout = new QVBoxLayout;
977     layout->addWidget(m_sourcesWidget);
978     m_ui.shadersTab->setLayout(layout);
979 
980     m_trace = new ApiTrace();
981     m_retracer = new Retracer(this);
982 
983     m_vdataInterpreter = new VertexDataInterpreter(this);
984     m_vdataInterpreter->setListWidget(m_ui.vertexDataListWidget);
985     m_vdataInterpreter->setStride(
986         m_ui.vertexStrideSB->value());
987     m_vdataInterpreter->setComponents(
988         m_ui.vertexComponentsSB->value());
989     m_vdataInterpreter->setStartingOffset(
990         m_ui.startingOffsetSB->value());
991     m_vdataInterpreter->setTypeFromString(
992         m_ui.vertexTypeCB->currentText());
993 
994     m_model = new ApiTraceModel();
995     m_model->setApiTrace(m_trace);
996     m_proxyModel = new ApiTraceFilter();
997     m_proxyModel->setSourceModel(m_model);
998     m_ui.callView->setModel(m_proxyModel);
999     m_ui.callView->setItemDelegate(
1000         new ApiCallDelegate(m_ui.callView));
1001     m_ui.callView->resizeColumnToContents(0);
1002     m_ui.callView->header()->swapSections(0, 1);
1003     m_ui.callView->setColumnWidth(1, 42);
1004     m_ui.callView->setContextMenuPolicy(Qt::CustomContextMenu);
1005 
1006     m_progressBar = new QProgressBar();
1007     m_progressBar->setRange(0, 100);
1008     statusBar()->addPermanentWidget(m_progressBar);
1009     m_progressBar->hide();
1010 
1011     m_argsEditor = new ArgumentsEditor(this);
1012 
1013     m_ui.detailsDock->hide();
1014     m_ui.backtraceDock->hide();
1015     m_ui.errorsDock->hide();
1016     m_ui.vertexDataDock->hide();
1017     m_ui.stateDock->hide();
1018     setDockOptions(dockOptions() | QMainWindow::ForceTabbedDocks);
1019 
1020     tabifyDockWidget(m_ui.stateDock, m_ui.vertexDataDock);
1021     tabifyDockWidget(m_ui.detailsDock, m_ui.errorsDock);
1022     tabifyDockWidget(m_ui.detailsDock, m_ui.backtraceDock);
1023 
1024     m_ui.surfacesTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
1025 
1026     m_jumpWidget = new JumpWidget(this);
1027     m_ui.centralLayout->addWidget(m_jumpWidget);
1028     m_jumpWidget->hide();
1029 
1030     m_searchWidget = new SearchWidget(this);
1031     m_ui.centralLayout->addWidget(m_searchWidget);
1032     m_searchWidget->hide();
1033 
1034     m_traceProcess = new TraceProcess(this);
1035     m_trimProcess = new TrimProcess(this);
1036 
1037     m_profileDialog = new ProfileDialog();
1038 }
1039 
initConnections()1040 void MainWindow::initConnections()
1041 {
1042     connect(m_trace, SIGNAL(problemLoadingTrace(const QString&)),
1043             this, SLOT(loadError(const QString&)));
1044     connect(m_trace, SIGNAL(startedLoadingTrace()),
1045             this, SLOT(startedLoadingTrace()));
1046     connect(m_trace, SIGNAL(loaded(int)),
1047             this, SLOT(loadProgess(int)));
1048     connect(m_trace, SIGNAL(finishedLoadingTrace()),
1049             this, SLOT(finishedLoadingTrace()));
1050     connect(m_trace, SIGNAL(startedSaving()),
1051             this, SLOT(slotStartedSaving()));
1052     connect(m_trace, SIGNAL(saved()),
1053             this, SLOT(slotSaved()));
1054     connect(m_trace, SIGNAL(changed(ApiTraceEvent*)),
1055             this, SLOT(slotTraceChanged(ApiTraceEvent*)));
1056     connect(m_trace, SIGNAL(findResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)),
1057             this, SLOT(slotSearchResult(ApiTrace::SearchRequest,ApiTrace::SearchResult,ApiTraceCall*)));
1058     connect(m_trace, SIGNAL(foundFrameStart(ApiTraceFrame*)),
1059             this, SLOT(slotFoundFrameStart(ApiTraceFrame*)));
1060     connect(m_trace, SIGNAL(foundFrameEnd(ApiTraceFrame*)),
1061             this, SLOT(slotFoundFrameEnd(ApiTraceFrame*)));
1062     connect(m_trace, SIGNAL(foundCallIndex(ApiTraceCall*)),
1063             this, SLOT(slotJumpToResult(ApiTraceCall*)));
1064 
1065     connect(m_retracer, SIGNAL(finished(const QString&)),
1066             this, SLOT(replayFinished(const QString&)));
1067     connect(m_retracer, SIGNAL(error(const QString&)),
1068             this, SLOT(replayError(const QString&)));
1069     connect(m_retracer, SIGNAL(foundState(ApiTraceState*)),
1070             this, SLOT(replayStateFound(ApiTraceState*)));
1071     connect(m_retracer, SIGNAL(foundProfile(trace::Profile*)),
1072             this, SLOT(replayProfileFound(trace::Profile*)));
1073     connect(m_retracer, SIGNAL(foundThumbnails(const ImageHash&)),
1074             this, SLOT(replayThumbnailsFound(const ImageHash&)));
1075     connect(m_retracer, SIGNAL(retraceErrors(const QList<ApiTraceError>&)),
1076             this, SLOT(slotRetraceErrors(const QList<ApiTraceError>&)));
1077 
1078     connect(m_ui.vertexInterpretButton, SIGNAL(clicked()),
1079             m_vdataInterpreter, SLOT(interpretData()));
1080     connect(m_ui.bufferExportButton, SIGNAL(clicked()),
1081             this, SLOT(exportBufferData()));
1082     connect(m_ui.vertexTypeCB, SIGNAL(currentIndexChanged(const QString&)),
1083             m_vdataInterpreter, SLOT(setTypeFromString(const QString&)));
1084     connect(m_ui.vertexStrideSB, SIGNAL(valueChanged(int)),
1085             m_vdataInterpreter, SLOT(setStride(int)));
1086     connect(m_ui.vertexComponentsSB, SIGNAL(valueChanged(int)),
1087             m_vdataInterpreter, SLOT(setComponents(int)));
1088     connect(m_ui.startingOffsetSB, SIGNAL(valueChanged(int)),
1089             m_vdataInterpreter, SLOT(setStartingOffset(int)));
1090 
1091 
1092     connect(m_ui.actionNew, SIGNAL(triggered()),
1093             this, SLOT(createTrace()));
1094     connect(m_ui.actionOpen, SIGNAL(triggered()),
1095             this, SLOT(openTrace()));
1096     connect(m_ui.actionSave, SIGNAL(triggered()),
1097             this, SLOT(saveTrace()));
1098     connect(m_ui.actionQuit, SIGNAL(triggered()),
1099             this, SLOT(close()));
1100 
1101     connect(m_ui.actionFind, SIGNAL(triggered()),
1102             this, SLOT(slotSearch()));
1103     connect(m_ui.actionGo, SIGNAL(triggered()),
1104             this, SLOT(slotGoTo()));
1105     connect(m_ui.actionGoFrameStart, SIGNAL(triggered()),
1106             this, SLOT(slotGoFrameStart()));
1107     connect(m_ui.actionGoFrameEnd, SIGNAL(triggered()),
1108             this, SLOT(slotGoFrameEnd()));
1109 
1110     connect(m_ui.actionReplay, SIGNAL(triggered()),
1111             this, SLOT(replayStart()));
1112     connect(m_ui.actionProfile, SIGNAL(triggered()),
1113             this, SLOT(replayProfile()));
1114     connect(m_ui.actionStop, SIGNAL(triggered()),
1115             this, SLOT(replayStop()));
1116     connect(m_ui.actionLookupState, SIGNAL(triggered()),
1117             this, SLOT(lookupState()));
1118     connect(m_ui.actionTrim, SIGNAL(triggered()),
1119             this, SLOT(trim()));
1120     connect(m_ui.actionShowThumbnails, SIGNAL(triggered()),
1121             this, SLOT(showThumbnails()));
1122     connect(m_ui.actionOptions, SIGNAL(triggered()),
1123             this, SLOT(showSettings()));
1124     connect(m_ui.actionLeakTrace,SIGNAL(triggered()),
1125             this, SLOT(leakTrace()));
1126 
1127     connect(m_ui.callView->selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
1128             this, SLOT(callItemSelected(const QModelIndex &)));
1129     connect(m_ui.callView, SIGNAL(doubleClicked(const QModelIndex &)),
1130             this, SLOT(callItemActivated(const QModelIndex &)));
1131     connect(m_ui.callView, SIGNAL(customContextMenuRequested(QPoint)),
1132             this, SLOT(customContextMenuRequested(QPoint)));
1133 
1134     connect(m_ui.surfacesTreeWidget,
1135             SIGNAL(customContextMenuRequested(const QPoint &)),
1136             SLOT(showSurfacesMenu(const QPoint &)));
1137     connect(m_ui.surfacesTreeWidget,
1138             SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
1139             SLOT(showSelectedSurface()));
1140 
1141     connect(m_ui.nonDefaultsCB, SIGNAL(toggled(bool)),
1142             this, SLOT(fillState(bool)));
1143 
1144     connect(m_jumpWidget, SIGNAL(jumpTo(int)),
1145             SLOT(slotJumpTo(int)));
1146 
1147     connect(m_searchWidget,
1148             SIGNAL(searchNext(const QString&, Qt::CaseSensitivity, bool)),
1149             SLOT(slotSearchNext(const QString&, Qt::CaseSensitivity, bool)));
1150     connect(m_searchWidget,
1151             SIGNAL(searchPrev(const QString&, Qt::CaseSensitivity, bool)),
1152             SLOT(slotSearchPrev(const QString&, Qt::CaseSensitivity, bool)));
1153 
1154     connect(m_traceProcess, SIGNAL(tracedFile(const QString&)),
1155             SLOT(createdTrace(const QString&)));
1156     connect(m_traceProcess, SIGNAL(error(const QString&)),
1157             SLOT(traceError(const QString&)));
1158 
1159     connect(m_trimProcess, SIGNAL(trimmedFile(const QString&)),
1160             SLOT(createdTrim(const QString&)));
1161     connect(m_trimProcess, SIGNAL(error(const QString&)),
1162             SLOT(trimError(const QString&)));
1163 
1164     connect(m_ui.errorsDock, SIGNAL(visibilityChanged(bool)),
1165             m_ui.actionShowErrorsDock, SLOT(setChecked(bool)));
1166     connect(m_ui.actionShowErrorsDock, SIGNAL(triggered(bool)),
1167             m_ui.errorsDock, SLOT(setVisible(bool)));
1168     connect(m_ui.errorsTreeWidget,
1169             SIGNAL(itemActivated(QTreeWidgetItem*, int)),
1170             this, SLOT(slotErrorSelected(QTreeWidgetItem*)));
1171 
1172     connect(m_ui.actionShowProfileDialog, SIGNAL(triggered(bool)),
1173             m_profileDialog, SLOT(show()));
1174     connect(m_profileDialog, SIGNAL(jumpToCall(int)),
1175             this, SLOT(slotJumpTo(int)));
1176 
1177     connect(m_ui.surfacesOpaqueCB, SIGNAL(stateChanged(int)), this,
1178             SLOT(updateSurfacesView()));
1179     connect(m_ui.surfacesAlphaCB, SIGNAL(stateChanged(int)), this,
1180             SLOT(updateSurfacesView()));
1181     connect(m_ui.surfacesResolveMSAA, SIGNAL(stateChanged(int)), this,
1182             SLOT(updateSurfacesView()));
1183 }
1184 
updateActionsState(bool traceLoaded,bool stopped)1185 void MainWindow::updateActionsState(bool traceLoaded, bool stopped)
1186 {
1187     if (traceLoaded) {
1188         /* File */
1189         m_ui.actionSave          ->setEnabled(true);
1190 
1191         /* Edit */
1192         m_ui.actionFind          ->setEnabled(true);
1193         m_ui.actionGo            ->setEnabled(true);
1194         m_ui.actionGoFrameStart  ->setEnabled(true);
1195         m_ui.actionGoFrameEnd    ->setEnabled(true);
1196 
1197         /* Trace */
1198         if (stopped) {
1199             m_ui.actionStop->setEnabled(false);
1200             m_ui.actionReplay->setEnabled(true);
1201         }
1202         else {
1203             m_ui.actionStop->setEnabled(true);
1204             m_ui.actionReplay->setEnabled(false);
1205         }
1206 
1207         m_ui.actionProfile       ->setEnabled(true);
1208         m_ui.actionLookupState   ->setEnabled(true);
1209         m_ui.actionShowThumbnails->setEnabled(true);
1210         m_ui.actionTrim          ->setEnabled(true);
1211     }
1212     else {
1213         /* File */
1214         m_ui.actionSave          ->setEnabled(false);
1215 
1216         /* Edit */
1217         m_ui.actionFind          ->setEnabled(false);
1218         m_ui.actionGo            ->setEnabled(false);
1219         m_ui.actionGoFrameStart  ->setEnabled(false);
1220         m_ui.actionGoFrameEnd    ->setEnabled(false);
1221 
1222         /* Trace */
1223         m_ui.actionReplay        ->setEnabled(false);
1224         m_ui.actionProfile       ->setEnabled(false);
1225         m_ui.actionStop          ->setEnabled(false);
1226         m_ui.actionLookupState   ->setEnabled(false);
1227         m_ui.actionShowThumbnails->setEnabled(false);
1228         m_ui.actionTrim          ->setEnabled(false);
1229     }
1230 }
1231 
closeEvent(QCloseEvent * event)1232 void MainWindow::closeEvent(QCloseEvent * event)
1233 {
1234     m_profileDialog->close();
1235     QMainWindow::closeEvent(event);
1236 }
1237 
replayProfileFound(trace::Profile * profile)1238 void MainWindow::replayProfileFound(trace::Profile *profile)
1239 {
1240     m_ui.actionShowProfileDialog->setEnabled(true);
1241     m_profileDialog->setProfile(profile);
1242     m_profileDialog->show();
1243     m_profileDialog->activateWindow();
1244     m_profileDialog->setFocus();
1245 }
1246 
replayStateFound(ApiTraceState * state)1247 void MainWindow::replayStateFound(ApiTraceState *state)
1248 {
1249     m_stateEvent->setState(state);
1250     m_model->stateSetOnEvent(m_stateEvent);
1251     if (m_selectedEvent == m_stateEvent ||
1252         m_nonDefaultsLookupEvent == m_selectedEvent) {
1253         fillStateForFrame();
1254     } else {
1255         m_ui.stateDock->hide();
1256     }
1257     m_nonDefaultsLookupEvent = 0;
1258 }
1259 
replayThumbnailsFound(const ImageHash & thumbnails)1260 void MainWindow::replayThumbnailsFound(const ImageHash &thumbnails)
1261 {
1262     m_ui.callView->setUniformRowHeights(false);
1263     m_trace->bindThumbnails(thumbnails);
1264 }
1265 
slotGoTo()1266 void MainWindow::slotGoTo()
1267 {
1268     m_searchWidget->hide();
1269     m_jumpWidget->show();
1270 }
1271 
slotJumpTo(int callNum)1272 void MainWindow::slotJumpTo(int callNum)
1273 {
1274     m_trace->findCallIndex(callNum);
1275 }
1276 
createdTrace(const QString & path)1277 void MainWindow::createdTrace(const QString &path)
1278 {
1279     qDebug()<<"Done tracing "<<path;
1280     newTraceFile(path);
1281 }
1282 
traceError(const QString & msg)1283 void MainWindow::traceError(const QString &msg)
1284 {
1285     QMessageBox::warning(
1286             this,
1287             tr("Tracing Error"),
1288             msg);
1289 }
1290 
createdTrim(const QString & path)1291 void MainWindow::createdTrim(const QString &path)
1292 {
1293     qDebug()<<"Done trimming "<<path;
1294 
1295     newTraceFile(path);
1296 }
1297 
trimError(const QString & msg)1298 void MainWindow::trimError(const QString &msg)
1299 {
1300     QMessageBox::warning(
1301             this,
1302             tr("Trim Error"),
1303             msg);
1304 }
1305 
slotSearch()1306 void MainWindow::slotSearch()
1307 {
1308     m_jumpWidget->hide();
1309     m_searchWidget->show();
1310 }
1311 
slotSearchNext(const QString & str,Qt::CaseSensitivity sensitivity,bool useRegex)1312 void MainWindow::slotSearchNext(const QString &str,
1313                                 Qt::CaseSensitivity sensitivity,
1314                                 bool useRegex)
1315 {
1316     ApiTraceCall *call = currentCall();
1317     ApiTraceFrame *frame = currentFrame();
1318 
1319     if (!frame) {
1320         // Trace is still loading.
1321         if (!call) {
1322             return;
1323         }
1324         frame = call->parentFrame();
1325     }
1326     Q_ASSERT(frame);
1327 
1328     m_trace->findNext(frame, call, str, sensitivity, useRegex);
1329 }
1330 
slotSearchPrev(const QString & str,Qt::CaseSensitivity sensitivity,bool useRegex)1331 void MainWindow::slotSearchPrev(const QString &str,
1332                                 Qt::CaseSensitivity sensitivity,
1333                                 bool useRegex)
1334 {
1335     ApiTraceCall *call = currentCall();
1336     ApiTraceFrame *frame = currentFrame();
1337 
1338     if (!frame) {
1339         // Trace is still loading.
1340         if (!call) {
1341             return;
1342         }
1343         frame = call->parentFrame();
1344     }
1345     Q_ASSERT(frame);
1346 
1347     m_trace->findPrev(frame, call, str, sensitivity, useRegex);
1348 }
1349 
fillState(bool nonDefaults)1350 void MainWindow::fillState(bool nonDefaults)
1351 {
1352     if (nonDefaults) {
1353         ApiTraceState defaultState = m_trace->defaultState();
1354         if (defaultState.isEmpty()) {
1355             m_ui.nonDefaultsCB->blockSignals(true);
1356             m_ui.nonDefaultsCB->setChecked(false);
1357             m_ui.nonDefaultsCB->blockSignals(false);
1358             ApiTraceFrame *firstFrame =
1359                 m_trace->frameAt(0);
1360             if (!firstFrame) {
1361                 return;
1362             }
1363             if (!firstFrame->isLoaded()) {
1364                 m_trace->loadFrame(firstFrame);
1365                 return;
1366             }
1367             ApiTraceCall *firstCall = firstFrame->calls().first();
1368             ApiTraceEvent *oldSelected = m_selectedEvent;
1369             m_nonDefaultsLookupEvent = m_selectedEvent;
1370             m_selectedEvent = firstCall;
1371             lookupState();
1372             m_selectedEvent = oldSelected;
1373         }
1374     }
1375     fillStateForFrame();
1376 }
1377 
customContextMenuRequested(QPoint pos)1378 void MainWindow::customContextMenuRequested(QPoint pos)
1379 {
1380     QModelIndex index = m_ui.callView->indexAt(pos);
1381 
1382     callItemSelected(index);
1383     if (!index.isValid()) {
1384         return;
1385     }
1386 
1387     ApiTraceEvent *event =
1388         index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
1389     if (!event) {
1390         return;
1391     }
1392 
1393     QMenu menu;
1394     menu.addAction(QIcon(":/resources/media-record.png"),
1395                    tr("Lookup state"), this, SLOT(lookupState()));
1396     if (event->type() == ApiTraceEvent::Call) {
1397         menu.addAction(tr("Edit"), this, SLOT(editCall()));
1398     }
1399 
1400     menu.exec(QCursor::pos());
1401 }
1402 
editCall()1403 void MainWindow::editCall()
1404 {
1405     if (m_selectedEvent && m_selectedEvent->type() == ApiTraceEvent::Call) {
1406         ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
1407         m_argsEditor->setCall(call);
1408         m_argsEditor->show();
1409     }
1410 }
1411 
slotStartedSaving()1412 void MainWindow::slotStartedSaving()
1413 {
1414     m_progressBar->show();
1415     statusBar()->showMessage(
1416         tr("Saving to %1").arg(m_trace->fileName()));
1417 }
1418 
slotSaved()1419 void MainWindow::slotSaved()
1420 {
1421     statusBar()->showMessage(
1422         tr("Saved to %1").arg(m_trace->fileName()), 2000);
1423     m_progressBar->hide();
1424 }
1425 
slotGoFrameStart()1426 void MainWindow::slotGoFrameStart()
1427 {
1428     ApiTraceFrame *frame = currentFrame();
1429     ApiTraceCall *call = currentCall();
1430 
1431     if (!frame && call) {
1432         frame = call->parentFrame();
1433     }
1434 
1435     m_trace->findFrameStart(frame);
1436 }
1437 
slotGoFrameEnd()1438 void MainWindow::slotGoFrameEnd()
1439 {
1440     ApiTraceFrame *frame = currentFrame();
1441     ApiTraceCall *call = currentCall();
1442 
1443     if (!frame && call) {
1444         frame = call->parentFrame();
1445     }
1446 
1447     m_trace->findFrameEnd(frame);
1448 }
1449 
selectedFrame() const1450 ApiTraceFrame * MainWindow::selectedFrame() const
1451 {
1452     if (m_selectedEvent) {
1453         if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
1454             return static_cast<ApiTraceFrame*>(m_selectedEvent);
1455         } else {
1456             Q_ASSERT(m_selectedEvent->type() == ApiTraceEvent::Call);
1457             ApiTraceCall *call = static_cast<ApiTraceCall*>(m_selectedEvent);
1458             return call->parentFrame();
1459         }
1460     }
1461     return NULL;
1462 }
1463 
slotTraceChanged(ApiTraceEvent * event)1464 void MainWindow::slotTraceChanged(ApiTraceEvent *event)
1465 {
1466     Q_ASSERT(event);
1467     if (event == m_selectedEvent) {
1468         if (event->type() == ApiTraceEvent::Call) {
1469             ApiTraceCall *call = static_cast<ApiTraceCall*>(event);
1470             m_ui.detailsWebView->setHtml(call->toHtml());
1471         }
1472     }
1473 }
1474 
slotRetraceErrors(const QList<ApiTraceError> & errors)1475 void MainWindow::slotRetraceErrors(const QList<ApiTraceError> &errors)
1476 {
1477     m_ui.errorsTreeWidget->clear();
1478 
1479     foreach(ApiTraceError error, errors) {
1480         m_trace->setCallError(error);
1481 
1482         QTreeWidgetItem *item =
1483             new QTreeWidgetItem(m_ui.errorsTreeWidget);
1484         item->setData(0, Qt::DisplayRole, error.callIndex);
1485         item->setData(0, Qt::UserRole, error.callIndex);
1486         QString type = error.type;
1487         type[0] = type[0].toUpper();
1488         item->setData(1, Qt::DisplayRole, type);
1489         item->setData(2, Qt::DisplayRole, error.message);
1490     }
1491 }
1492 
slotErrorSelected(QTreeWidgetItem * current)1493 void MainWindow::slotErrorSelected(QTreeWidgetItem *current)
1494 {
1495     if (current) {
1496         int callIndex =
1497             current->data(0, Qt::UserRole).toInt();
1498         m_trace->findCallIndex(callIndex);
1499     }
1500 }
1501 
selectedCall() const1502 ApiTraceCall * MainWindow::selectedCall() const
1503 {
1504     if (m_selectedEvent &&
1505         m_selectedEvent->type() == ApiTraceEvent::Call) {
1506         return static_cast<ApiTraceCall*>(m_selectedEvent);
1507     }
1508     return NULL;
1509 }
1510 
saveSelectedSurface()1511 void MainWindow::saveSelectedSurface()
1512 {
1513     QTreeWidgetItem *item =
1514         m_ui.surfacesTreeWidget->currentItem();
1515 
1516     if (!item || !m_trace) {
1517         return;
1518     }
1519 
1520     QVariant var = item->data(0, Qt::UserRole);
1521     if (!var.isValid()) {
1522         return;
1523     }
1524 
1525     QImage img = var.value<QImage>();
1526     if (img.isNull()) {
1527         image::Image *traceImage = ApiSurface::imageFromData(var.value<QByteArray>());
1528         img = ApiSurface::qimageFromRawImage(traceImage);
1529         delete traceImage;
1530     }
1531     if (img.isNull()) {
1532         statusBar()->showMessage( "Failed to save image", 5000);
1533         return;
1534     }
1535 
1536     QString imageIndex;
1537     ApiTraceCall *call = selectedCall();
1538     if (call) {
1539         imageIndex = tr("_call_%1")
1540                      .arg(call->index());
1541     } else {
1542         ApiTraceFrame *frame = selectedFrame();
1543         if (frame) {
1544             imageIndex = tr("_frame_%1")
1545                          .arg(frame->number);
1546         }
1547     }
1548 
1549     //which of the surfaces are we saving
1550     QTreeWidgetItem *parent = item->parent();
1551     int parentIndex =
1552         m_ui.surfacesTreeWidget->indexOfTopLevelItem(parent);
1553     if (parentIndex < 0) {
1554         parentIndex = 0;
1555     }
1556     int childIndex = 0;
1557     if (parent) {
1558         childIndex = parent->indexOfChild(item);
1559     } else {
1560         childIndex = m_ui.surfacesTreeWidget->indexOfTopLevelItem(item);
1561     }
1562 
1563 
1564     QString fileName =
1565         tr("%1%2-%3_%4.png")
1566         .arg(m_trace->fileName())
1567         .arg(imageIndex)
1568         .arg(parentIndex)
1569         .arg(childIndex);
1570     //qDebug()<<"save "<<fileName;
1571     if (img.save(fileName, "PNG")) {
1572         statusBar()->showMessage( tr("Saved '%1'").arg(fileName), 5000);
1573     } else {
1574         statusBar()->showMessage( tr("Failed to save '%1'").arg(fileName), 5000);
1575     }
1576 }
1577 
exportBufferData()1578 void MainWindow::exportBufferData()
1579 {
1580     QByteArray data = m_vdataInterpreter->data();
1581     if (data.isEmpty())
1582         return;
1583 
1584     QString bufferIndex;
1585     ApiTraceCall *call = selectedCall();
1586     if (call) {
1587         bufferIndex = tr("_call_%1").arg(call->index());
1588     } else {
1589         ApiTraceFrame *frame = selectedFrame();
1590         if (frame) {
1591             bufferIndex = tr("_frame_%1")
1592                          .arg(frame->number);
1593         }
1594     }
1595 
1596     QString filename =
1597         tr("%1%2_buffer.raw")
1598         .arg(m_trace->fileName())
1599         .arg(bufferIndex);
1600 
1601     QFile file(filename);
1602     if (!file.open(QIODevice::WriteOnly)) {
1603         statusBar()->showMessage("Failed to save buffer data", 5000);
1604         return;
1605     }
1606 
1607     if (file.write(data) == -1) {
1608         statusBar()->showMessage("Failed to save buffer data", 5000);
1609         return;
1610     }
1611 
1612     file.close();
1613 
1614     statusBar()->showMessage( tr("Saved buffer to '%1'").arg(filename), 5000);
1615 }
1616 
loadProgess(int percent)1617 void MainWindow::loadProgess(int percent)
1618 {
1619     m_progressBar->setValue(percent);
1620 }
1621 
slotSearchResult(const ApiTrace::SearchRequest & request,ApiTrace::SearchResult result,ApiTraceCall * call)1622 void MainWindow::slotSearchResult(const ApiTrace::SearchRequest &request,
1623                                   ApiTrace::SearchResult result,
1624                                   ApiTraceCall *call)
1625 {
1626     switch (result) {
1627     case ApiTrace::SearchResult_NotFound:
1628         m_searchWidget->setFound(false);
1629         break;
1630     case ApiTrace::SearchResult_Found: {
1631         QModelIndex index = m_proxyModel->indexForCall(call);
1632 
1633         if (index.isValid()) {
1634             m_ui.callView->setCurrentIndex(index);
1635             m_searchWidget->setFound(true);
1636         } else {
1637             //call is filtered out, so continue searching but from the
1638             // filtered call
1639             if (!call) {
1640                 qDebug()<<"Error: search success with no call";
1641                 return;
1642             }
1643 //            qDebug()<<"filtered! search from "<<call->searchText()
1644 //                   <<", call idx = "<<call->index();
1645 
1646             if (request.direction == ApiTrace::SearchRequest::Next) {
1647                 m_trace->findNext(call->parentFrame(), call,
1648                                   request.text, request.cs, request.useRegex);
1649             } else {
1650                 m_trace->findNext(call->parentFrame(), call,
1651                                   request.text, request.cs, request.useRegex);
1652             }
1653         }
1654     }
1655         break;
1656     case ApiTrace::SearchResult_Wrapped:
1657         m_searchWidget->setFound(false);
1658         break;
1659     }
1660 }
1661 
currentFrame() const1662 ApiTraceFrame * MainWindow::currentFrame() const
1663 {
1664     QModelIndex index = m_ui.callView->currentIndex();
1665     ApiTraceEvent *event = 0;
1666 
1667     if (!index.isValid()) {
1668         index = m_proxyModel->index(0, 0, QModelIndex());
1669         if (!index.isValid()) {
1670             qDebug()<<"no currently valid index";
1671             return 0;
1672         }
1673     }
1674 
1675     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
1676     Q_ASSERT(event);
1677     if (!event) {
1678         return 0;
1679     }
1680 
1681     ApiTraceFrame *frame = 0;
1682     if (event->type() == ApiTraceCall::Frame) {
1683         frame = static_cast<ApiTraceFrame*>(event);
1684     }
1685     return frame;
1686 }
1687 
currentCall() const1688 ApiTraceCall * MainWindow::currentCall() const
1689 {
1690     QModelIndex index = m_ui.callView->currentIndex();
1691     ApiTraceEvent *event = 0;
1692 
1693     if (!index.isValid()) {
1694         index = m_proxyModel->index(0, 0, QModelIndex());
1695         if (!index.isValid()) {
1696             qDebug()<<"no currently valid index";
1697             return 0;
1698         }
1699     }
1700 
1701     event = index.data(ApiTraceModel::EventRole).value<ApiTraceEvent*>();
1702     Q_ASSERT(event);
1703     if (!event) {
1704         return 0;
1705     }
1706 
1707     ApiTraceCall *call = 0;
1708     if (event->type() == ApiTraceCall::Call) {
1709         call = static_cast<ApiTraceCall*>(event);
1710     }
1711 
1712     return call;
1713 
1714 }
1715 
slotFoundFrameStart(ApiTraceFrame * frame)1716 void MainWindow::slotFoundFrameStart(ApiTraceFrame *frame)
1717 {
1718     Q_ASSERT(frame->isLoaded());
1719     if (!frame || frame->isEmpty()) {
1720         return;
1721     }
1722 
1723     QVector<ApiTraceCall*>::const_iterator itr;
1724     QVector<ApiTraceCall*> calls = frame->calls();
1725 
1726     itr = calls.constBegin();
1727     while (itr != calls.constEnd()) {
1728         ApiTraceCall *call = *itr;
1729         QModelIndex idx = m_proxyModel->indexForCall(call);
1730         if (idx.isValid()) {
1731             m_ui.callView->setCurrentIndex(idx);
1732             m_ui.callView->scrollTo(idx, QAbstractItemView::PositionAtTop);
1733             break;
1734         }
1735         ++itr;
1736     }
1737 }
1738 
slotFoundFrameEnd(ApiTraceFrame * frame)1739 void MainWindow::slotFoundFrameEnd(ApiTraceFrame *frame)
1740 {
1741     Q_ASSERT(frame->isLoaded());
1742     if (!frame || frame->isEmpty()) {
1743         return;
1744     }
1745     QVector<ApiTraceCall*>::const_iterator itr;
1746     QVector<ApiTraceCall*> calls = frame->calls();
1747 
1748     itr = calls.constEnd();
1749     do {
1750         --itr;
1751         ApiTraceCall *call = *itr;
1752         QModelIndex idx = m_proxyModel->indexForCall(call);
1753         if (idx.isValid()) {
1754             m_ui.callView->setCurrentIndex(idx);
1755             m_ui.callView->scrollTo(idx, QAbstractItemView::PositionAtBottom);
1756             break;
1757         }
1758     } while (itr != calls.constBegin());
1759 }
1760 
slotJumpToResult(ApiTraceCall * call)1761 void MainWindow::slotJumpToResult(ApiTraceCall *call)
1762 {
1763     QModelIndex idx = m_proxyModel->indexForCall(call);
1764     if (idx.isValid()) {
1765         activateWindow();
1766         m_ui.callView->setFocus();
1767         m_ui.callView->setCurrentIndex(idx);
1768         m_ui.callView->scrollTo(idx, QAbstractItemView::PositionAtCenter);
1769     } else {
1770         statusBar()->showMessage(tr("Call has been filtered out."));
1771     }
1772 }
1773 
thumbnailCallback(void * object,int thumbnailIdx)1774 void MainWindow::thumbnailCallback(void *object, int thumbnailIdx)
1775 {
1776     //qDebug() << QLatin1String("debug: transfer from trace to retracer thumbnail index: ") << thumbnailIdx;
1777     MainWindow *mySelf = (MainWindow *) object;
1778     mySelf->m_retracer->addThumbnailToCapture(thumbnailIdx);
1779 }
1780 
addRecentLaunch(const RecentLaunch & launch)1781 void MainWindow::addRecentLaunch(const RecentLaunch& launch)
1782 {
1783     QList<RecentLaunch> launches = readRecentLaunches();
1784 
1785     for(int i = 0; i < launches.size(); ++i) {
1786         const RecentLaunch& rl = launches[i];
1787 
1788         if(rl.api == launch.api
1789                 && rl.execPath == launch.execPath
1790                 && rl.workingDir == launch.workingDir
1791                 && rl.args == launch.args) {
1792             launches.removeAt(i);
1793         } else {
1794             ++i;
1795         }
1796     }
1797 
1798     launches.push_front(launch);
1799 
1800     const int maxHistorySize = 10;
1801     if(launches.size() > maxHistorySize) {
1802         launches.erase(launches.begin() + maxHistorySize, launches.end());
1803     }
1804 
1805     QSettings settings;
1806     settings.beginWriteArray("launch-history", launches.size());
1807     for(int i = 0; i < launches.size(); ++i) {
1808         settings.setArrayIndex(i);
1809         settings.setValue("api", launches[i].api);
1810         settings.setValue("exec", launches[i].execPath);
1811         settings.setValue("working-dir", launches[i].workingDir);
1812         settings.setValue("args", launches[i].args);
1813     }
1814     settings.endArray();
1815 }
1816 
readRecentLaunches() const1817 QList<MainWindow::RecentLaunch> MainWindow::readRecentLaunches() const
1818 {
1819     QList<RecentLaunch> launches;
1820     QSettings settings;
1821 
1822     int historySize = settings.beginReadArray("launch-history");
1823     for(int i = 0; i < historySize; ++i) {
1824         settings.setArrayIndex(i);
1825 
1826         RecentLaunch rl;
1827         rl.execPath = settings.value("exec").toString();
1828         rl.workingDir = settings.value("working-dir").toString();
1829         rl.args = settings.value("args").toStringList();
1830         launches.push_back(rl);
1831     }
1832     settings.endArray();
1833 
1834     return launches;
1835 }
1836 
updateRecentLaunchesMenu()1837 void MainWindow::updateRecentLaunchesMenu()
1838 {
1839     QList<RecentLaunch> launches = readRecentLaunches();
1840 
1841     QMenu* rootMenu = m_ui.actionRecentLaunches->menu();
1842     if(!rootMenu) {
1843         rootMenu = new QMenu();
1844         m_ui.actionRecentLaunches->setMenu(rootMenu);
1845     }
1846 
1847     rootMenu->clear();
1848     for(int i = 0; i < launches.size(); ++i) {
1849         const RecentLaunch& rl = launches[i];
1850         QAction* action = rootMenu->addAction(rl.execPath);
1851 
1852         connect(action, &QAction::triggered, [this, i](){
1853             QList<RecentLaunch> launches = readRecentLaunches();
1854             if(i < launches.size())
1855                 createTrace(&launches[i]);
1856         });
1857     }
1858 }
1859 
1860 #include "mainwindow.moc"
1861