]> git.cworth.org Git - apitrace/blob - gui/mainwindow.cpp
Allow filling state for frames and calls.
[apitrace] / gui / mainwindow.cpp
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
9 #include <qjson/parser.h>
10
11 #include <QAction>
12 #include <QDebug>
13 #include <QDir>
14 #include <QFileDialog>
15 #include <QLineEdit>
16 #include <QMessageBox>
17 #include <QProcess>
18 #include <QProgressBar>
19 #include <QToolBar>
20 #include <QWebView>
21
22
23 MainWindow::MainWindow()
24     : QMainWindow(),
25       m_replayProcess(0),
26       m_selectedEvent(0),
27       m_stateEvent(0),
28       m_findingState(false),
29       m_jsonParser(new QJson::Parser())
30 {
31     m_ui.setupUi(this);
32
33     m_trace = new ApiTrace();
34     connect(m_trace, SIGNAL(startedLoadingTrace()),
35             this, SLOT(startedLoadingTrace()));
36     connect(m_trace, SIGNAL(finishedLoadingTrace()),
37             this, SLOT(finishedLoadingTrace()));
38
39     m_model = new ApiTraceModel();
40     m_model->setApiTrace(m_trace);
41     m_proxyModel = new ApiTraceFilter();
42     m_proxyModel->setSourceModel(m_model);
43     m_ui.callView->setModel(m_proxyModel);
44     m_ui.callView->setItemDelegate(new ApiCallDelegate);
45     for (int column = 0; column < m_model->columnCount(); ++column)
46         m_ui.callView->resizeColumnToContents(column);
47
48     QToolBar *toolBar = addToolBar(tr("Navigation"));
49     m_filterEdit = new QLineEdit(toolBar);
50     toolBar->addWidget(m_filterEdit);
51
52     m_progressBar = new QProgressBar();
53     m_progressBar->setRange(0, 0);
54     statusBar()->addPermanentWidget(m_progressBar);
55     m_progressBar->hide();
56
57     m_ui.detailsDock->hide();
58     m_ui.stateDock->hide();
59
60     connect(m_ui.actionOpen, SIGNAL(triggered()),
61             this, SLOT(openTrace()));
62     connect(m_ui.actionQuit, SIGNAL(triggered()),
63             this, SLOT(close()));
64
65     connect(m_ui.actionReplay, SIGNAL(triggered()),
66             this, SLOT(replayStart()));
67     connect(m_ui.actionStop, SIGNAL(triggered()),
68             this, SLOT(replayStop()));
69     connect(m_ui.actionLookupState, SIGNAL(triggered()),
70             this, SLOT(lookupState()));
71
72     connect(m_ui.callView, SIGNAL(activated(const QModelIndex &)),
73             this, SLOT(callItemSelected(const QModelIndex &)));
74     connect(m_filterEdit, SIGNAL(returnPressed()),
75             this, SLOT(filterTrace()));
76 }
77
78 void MainWindow::openTrace()
79 {
80     QString fileName =
81         QFileDialog::getOpenFileName(
82             this,
83             tr("Open Trace"),
84             QDir::homePath(),
85             tr("Trace Files (*.trace)"));
86
87     qDebug()<< "File name : " <<fileName;
88
89     newTraceFile(fileName);
90 }
91
92 void MainWindow::loadTrace(const QString &fileName)
93 {
94     if (!QFile::exists(fileName)) {
95         QMessageBox::warning(this, tr("File Missing"),
96                              tr("File '%1' doesn't exist.").arg(fileName));
97         return;
98     }
99     qDebug()<< "Loading  : " <<fileName;
100
101     m_progressBar->setValue(0);
102     newTraceFile(fileName);
103 }
104
105 void MainWindow::callItemSelected(const QModelIndex &index)
106 {
107     ApiTraceCall *call = index.data().value<ApiTraceCall*>();
108     if (call) {
109         m_ui.detailsWebView->setHtml(call->toHtml());
110         m_ui.detailsDock->show();
111         m_selectedEvent = call;
112     } else {
113         m_selectedEvent = index.data().value<ApiTraceFrame*>();
114         m_ui.detailsDock->hide();
115     }
116     if (m_selectedEvent && !m_selectedEvent->state().isEmpty()) {
117         fillStateForFrame();
118     } else
119         m_ui.stateDock->hide();
120 }
121
122 void MainWindow::filterTrace()
123 {
124     m_proxyModel->setFilterString(m_filterEdit->text());
125 }
126
127 void MainWindow::replayStart()
128 {
129     replayTrace(false);
130 }
131
132 void MainWindow::replayStop()
133 {
134     if (m_replayProcess) {
135         m_replayProcess->kill();
136
137         m_ui.actionStop->setEnabled(false);
138         m_ui.actionReplay->setEnabled(true);
139         m_ui.actionLookupState->setEnabled(true);
140     }
141 }
142
143 void MainWindow::newTraceFile(const QString &fileName)
144 {
145     m_traceFileName = fileName;
146     m_trace->setFileName(fileName);
147
148     if (m_traceFileName.isEmpty()) {
149         m_ui.actionReplay->setEnabled(false);
150         m_ui.actionLookupState->setEnabled(false);
151     } else {
152         m_ui.actionReplay->setEnabled(true);
153         m_ui.actionLookupState->setEnabled(true);
154     }
155 }
156
157 void MainWindow::replayFinished()
158 {
159     m_ui.actionStop->setEnabled(false);
160     m_ui.actionReplay->setEnabled(true);
161     m_ui.actionLookupState->setEnabled(true);
162
163     QByteArray output = m_replayProcess->readAllStandardOutput();
164
165 #if 0
166     qDebug()<<"Process finished = ";
167     qDebug()<<"\terr = "<<m_replayProcess->readAllStandardError();
168     qDebug()<<"\tout = "<<output;
169 #endif
170
171     if (m_findingState) {
172         bool ok = false;
173         QVariantMap parsedJson = m_jsonParser->parse(output, &ok).toMap();
174         parseState(parsedJson[QLatin1String("parameters")].toMap());
175     } else if (output.length() < 80) {
176         statusBar()->showMessage(output);
177     }
178     m_stateEvent = 0;
179     m_findingState = false;
180 }
181
182 void MainWindow::replayError(QProcess::ProcessError err)
183 {
184     m_ui.actionStop->setEnabled(false);
185     m_ui.actionReplay->setEnabled(true);
186     m_ui.actionLookupState->setEnabled(true);
187     m_findingState = false;
188     m_stateEvent = 0;
189
190     qDebug()<<"Process error = "<<err;
191     qDebug()<<"\terr = "<<m_replayProcess->readAllStandardError();
192     qDebug()<<"\tout = "<<m_replayProcess->readAllStandardOutput();
193     QMessageBox::warning(
194         this, tr("Replay Failed"),
195         tr("Couldn't execute the replay file '%1'").arg(m_traceFileName));
196 }
197
198 void MainWindow::startedLoadingTrace()
199 {
200     Q_ASSERT(m_trace);
201     m_progressBar->show();
202     QFileInfo info(m_trace->fileName());
203     statusBar()->showMessage(
204         tr("Loading %1...").arg(info.fileName()));
205 }
206
207 void MainWindow::finishedLoadingTrace()
208 {
209     m_progressBar->hide();
210     if (!m_trace) {
211         return;
212     }
213     QFileInfo info(m_trace->fileName());
214     statusBar()->showMessage(
215         tr("Loaded %1").arg(info.fileName()), 3000);
216 }
217
218 void MainWindow::replayTrace(bool dumpState)
219 {
220     if (!m_replayProcess) {
221 #ifdef Q_OS_WIN
222         QString format = QLatin1String("%1;");
223 #else
224         QString format = QLatin1String("%1:");
225 #endif
226         QString buildPath = format.arg(BUILD_DIR);
227         QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
228         env.insert("PATH", buildPath + env.value("PATH"));
229
230         qputenv("PATH", env.value("PATH").toLatin1());
231
232         m_replayProcess = new QProcess(this);
233         m_replayProcess->setProcessEnvironment(env);
234
235         connect(m_replayProcess, SIGNAL(finished(int, QProcess::ExitStatus)),
236                 this, SLOT(replayFinished()));
237         connect(m_replayProcess, SIGNAL(error(QProcess::ProcessError)),
238                 this, SLOT(replayError(QProcess::ProcessError)));
239     }
240
241     if (m_traceFileName.isEmpty())
242         return;
243
244     QStringList arguments;
245     if (dumpState &&
246         m_selectedEvent) {
247         int index = 0;
248         if (m_selectedEvent->type() == ApiTraceEvent::Call) {
249             index = static_cast<ApiTraceCall*>(m_selectedEvent)->index;
250         } else if (m_selectedEvent->type() == ApiTraceEvent::Frame) {
251             ApiTraceFrame *frame = static_cast<ApiTraceFrame*>(m_selectedEvent);
252             if (frame->calls.isEmpty()) {
253                 //XXX i guess we could still get the current state
254                 qDebug()<<"tried to get a state for an empty frame";
255                 return;
256             }
257             index = frame->calls.first()->index;
258         } else {
259             qDebug()<<"Unknown event type";
260             return;
261         }
262         arguments << QLatin1String("-D");
263         arguments << QString::number(index);
264     }
265     arguments << m_traceFileName;
266
267     m_replayProcess->start(QLatin1String("glretrace"),
268                            arguments);
269
270     m_ui.actionStop->setEnabled(true);
271 }
272
273 void MainWindow::lookupState()
274 {
275     if (!m_selectedEvent) {
276         QMessageBox::warning(
277             this, tr("Unknown Event"),
278             tr("To inspect the state select an event in the event list."));
279         return;
280     }
281     m_stateEvent = m_selectedEvent;
282     m_findingState = true;
283     replayTrace(true);
284 }
285
286 MainWindow::~MainWindow()
287 {
288     delete m_jsonParser;
289 }
290
291 void MainWindow::parseState(const QVariantMap &params)
292 {
293     QVariantMap::const_iterator itr;
294
295     m_stateEvent->setState(params);
296     if (m_selectedEvent == m_stateEvent) {
297         fillStateForFrame();
298     } else {
299         m_ui.stateDock->hide();
300     }
301 }
302
303 static void
304 variantToString(const QVariant &var, QString &str)
305 {
306     if (var.type() == QVariant::List) {
307         QVariantList lst = var.toList();
308         str += QLatin1String("[");
309         for (int i = 0; i < lst.count(); ++i) {
310             QVariant val = lst[i];
311             variantToString(val, str);
312             if (i < lst.count() - 1)
313                 str += QLatin1String(", ");
314         }
315         str += QLatin1String("]");
316     } else if (var.type() == QVariant::Map) {
317         Q_ASSERT(!"unsupported state type");
318     } else if (var.type() == QVariant::Hash) {
319         Q_ASSERT(!"unsupported state type");
320     } else {
321         str += var.toString();
322     }
323 }
324
325 void MainWindow::fillStateForFrame()
326 {
327     QVariantMap::const_iterator itr;
328     QVariantMap params;
329
330     if (!m_selectedEvent || m_selectedEvent->state().isEmpty())
331         return;
332
333     m_ui.stateTreeWidget->clear();
334     params = m_selectedEvent->state();
335     QList<QTreeWidgetItem *> items;
336     for (itr = params.constBegin(); itr != params.constEnd(); ++itr) {
337         QString key = itr.key();
338         QString val;
339
340         variantToString(itr.value(), val);
341         //qDebug()<<"key = "<<key;
342         //qDebug()<<"val = "<<val;
343         QStringList lst;
344         lst += key;
345         lst += val;
346         items.append(new QTreeWidgetItem((QTreeWidget*)0, lst));
347     }
348     m_ui.stateTreeWidget->insertTopLevelItems(0, items);
349     m_ui.stateDock->show();
350 }
351
352 #include "mainwindow.moc"