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