UDP multicast stream dialog.
[metze/wireshark/wip.git] / ui / qt / service_response_time_dialog.cpp
1 /* service_response_time_dialog.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "service_response_time_dialog.h"
23
24 #include "file.h"
25
26 #include <epan/tap.h>
27
28 #include <ui/service_response_time.h>
29
30 #include "rpc_service_response_time_dialog.h"
31 #include "wireshark_application.h"
32
33 #include <QMessageBox>
34 #include <QTreeWidget>
35 #include <QTreeWidgetItemIterator>
36
37 static QHash<const QString, register_srt_t *> cfg_str_to_srt_;
38
39 extern "C" {
40 static void
41 srt_init(const char *args, void*) {
42     QStringList args_l = QString(args).split(',');
43     if (args_l.length() > 1) {
44         QString srt = QString("%1,%2").arg(args_l[0]).arg(args_l[1]);
45         QString filter;
46         if (args_l.length() > 2) {
47             filter = QStringList(args_l.mid(2)).join(",");
48         }
49         wsApp->emitTapParameterSignal(srt, filter, NULL);
50     }
51 }
52 }
53
54 void register_service_response_tables(gpointer data, gpointer)
55 {
56     register_srt_t *srt = (register_srt_t*)data;
57     const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)));
58     const char *cfg_abbr = srt_table_get_tap_string(srt);
59     tpdCreator tpd_creator = ServiceResponseTimeDialog::createSrtDialog;
60
61     /* XXX - These dissectors haven't been converted over to due to an "interactive input dialog" for their
62        tap data.  Let those specific dialogs register for themselves */
63     if (strcmp(short_name, "DCERPC") == 0) {
64         short_name = "DCE-RPC";
65         tpd_creator = RpcServiceResponseTimeDialog::createDceRpcSrtDialog;
66     } else if (strcmp(short_name, "RPC") == 0) {
67         short_name = "ONC-RPC";
68         tpd_creator = RpcServiceResponseTimeDialog::createOncRpcSrtDialog;
69     }
70
71     cfg_str_to_srt_[cfg_abbr] = srt;
72     TapParameterDialog::registerDialog(
73                 short_name,
74                 cfg_abbr,
75                 REGISTER_STAT_GROUP_RESPONSE_TIME,
76                 srt_init,
77                 tpd_creator);
78 }
79
80 enum {
81     srt_table_type_ = 1000,
82     srt_row_type_
83 };
84
85 class SrtRowTreeWidgetItem : public QTreeWidgetItem
86 {
87 public:
88     SrtRowTreeWidgetItem(QTreeWidgetItem *parent, const srt_procedure_t *procedure) :
89         QTreeWidgetItem (parent, srt_row_type_),
90         procedure_(procedure)
91     {
92         setText(SRT_COLUMN_PROCEDURE, procedure_->procedure);
93         setHidden(true);
94     }
95
96     void draw() {
97         setText(SRT_COLUMN_INDEX, QString::number(procedure_->index));
98         setText(SRT_COLUMN_CALLS, QString::number(procedure_->stats.num));
99         setText(SRT_COLUMN_MIN, QString::number(nstime_to_sec(&procedure_->stats.min), 'f', 6));
100         setText(SRT_COLUMN_MAX, QString::number(nstime_to_sec(&procedure_->stats.max), 'f', 6));
101         setText(SRT_COLUMN_AVG, QString::number(get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0, 'f', 6));
102         setText(SRT_COLUMN_SUM, QString::number(nstime_to_sec(&procedure_->stats.tot), 'f', 6));
103
104         for (int col = 0; col < columnCount(); col++) {
105             if (col == SRT_COLUMN_PROCEDURE) continue;
106             setTextAlignment(col, Qt::AlignRight);
107         }
108
109         setHidden(procedure_->stats.num < 1);
110     }
111
112     bool operator< (const QTreeWidgetItem &other) const
113     {
114         if (other.type() != srt_row_type_) return QTreeWidgetItem::operator< (other);
115         const SrtRowTreeWidgetItem *other_row = static_cast<const SrtRowTreeWidgetItem *>(&other);
116
117         switch (treeWidget()->sortColumn()) {
118         case SRT_COLUMN_INDEX:
119             return procedure_->index < other_row->procedure_->index;
120         case SRT_COLUMN_CALLS:
121             return procedure_->stats.num < other_row->procedure_->stats.num;
122         case SRT_COLUMN_MIN:
123             return nstime_cmp(&procedure_->stats.min, &other_row->procedure_->stats.min) < 0;
124         case SRT_COLUMN_MAX:
125             return nstime_cmp(&procedure_->stats.max, &other_row->procedure_->stats.max) < 0;
126         case SRT_COLUMN_AVG:
127         {
128             double our_avg = get_average(&procedure_->stats.tot, procedure_->stats.num);
129             double other_avg = get_average(&other_row->procedure_->stats.tot, other_row->procedure_->stats.num);
130             return our_avg < other_avg;
131         }
132         case SRT_COLUMN_SUM:
133             return nstime_cmp(&procedure_->stats.tot, &other_row->procedure_->stats.tot) < 0;
134         default:
135             break;
136         }
137
138         return QTreeWidgetItem::operator< (other);
139     }
140     QList<QVariant> rowData() {
141         return QList<QVariant>() << QString(procedure_->procedure) << procedure_->index << procedure_->stats.num
142                                  << nstime_to_sec(&procedure_->stats.min) << nstime_to_sec(&procedure_->stats.max)
143                                  << get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0
144                                  << nstime_to_sec(&procedure_->stats.tot);
145     }
146 private:
147     const srt_procedure_t *procedure_;
148 };
149
150 class SrtTableTreeWidgetItem : public QTreeWidgetItem
151 {
152 public:
153     SrtTableTreeWidgetItem(QTreeWidget *parent, const srt_stat_table *srt_table) :
154         QTreeWidgetItem (parent, srt_table_type_),
155         srt_table_(srt_table)
156     {
157         setText(0, srt_table_->name);
158         setFirstColumnSpanned(true);
159         setExpanded(true);
160
161         for (int i = 0; i < srt_table_->num_procs; i++) {
162             new SrtRowTreeWidgetItem(this, &srt_table_->procedures[i]);
163         }
164     }
165     const QString columnTitle() { return srt_table_->proc_column_name; }
166
167     QList<QVariant> rowData() {
168         return QList<QVariant>() << srt_table_->name;
169     }
170     const QString filterField() { return srt_table_->filter_string; }
171
172 private:
173     const srt_stat_table *srt_table_;
174 };
175
176
177 ServiceResponseTimeDialog::ServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, register_srt *srt, const QString filter, int help_topic) :
178     TapParameterDialog(parent, cf, help_topic),
179     srt_(srt)
180 {
181     QString subtitle = QString("%1 Service Response Time Statistics")
182             .arg(proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt))));
183     setWindowSubtitle(subtitle);
184
185     // Add number of columns for this stats_tree
186     QStringList header_labels;
187     for (int col = 0; col < NUM_SRT_COLUMNS; col++) {
188         header_labels.push_back(service_response_time_get_column_name(col));
189     }
190     statsTreeWidget()->setColumnCount(header_labels.count());
191     statsTreeWidget()->setHeaderLabels(header_labels);
192
193     for (int col = 0; col < statsTreeWidget()->columnCount(); col++) {
194         if (col == SRT_COLUMN_PROCEDURE) continue;
195         statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight);
196     }
197
198     addFilterActions();
199
200     if (!filter.isEmpty()) {
201         setDisplayFilter(filter);
202     }
203
204     connect(statsTreeWidget(), SIGNAL(itemChanged(QTreeWidgetItem*,int)),
205             this, SLOT(statsTreeWidgetItemChanged()));
206 }
207
208 TapParameterDialog *ServiceResponseTimeDialog::createSrtDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf)
209 {
210     if (!cfg_str_to_srt_.contains(cfg_str)) {
211         // XXX MessageBox?
212         return NULL;
213     }
214
215     register_srt_t *srt = cfg_str_to_srt_[cfg_str];
216
217     return new ServiceResponseTimeDialog(parent, cf, srt, filter);
218 }
219
220 void ServiceResponseTimeDialog::addSrtTable(const struct _srt_stat_table *srt_table)
221 {
222     new SrtTableTreeWidgetItem(statsTreeWidget(), srt_table);
223 }
224
225 void ServiceResponseTimeDialog::tapReset(void *srtd_ptr)
226 {
227     srt_data_t *srtd = (srt_data_t*) srtd_ptr;
228     ServiceResponseTimeDialog *srt_dlg = static_cast<ServiceResponseTimeDialog *>(srtd->user_data);
229     if (!srt_dlg) return;
230
231     reset_srt_table(srtd->srt_array, NULL, NULL);
232
233     srt_dlg->statsTreeWidget()->clear();
234     for (guint i = 0; i < srtd->srt_array->len; i++) {
235         srt_stat_table *srt_table = g_array_index(srtd->srt_array, srt_stat_table*, i);
236         srt_dlg->addSrtTable(srt_table);
237     }
238 }
239
240 void ServiceResponseTimeDialog::tapDraw(void *srtd_ptr)
241 {
242     srt_data_t *srtd = (srt_data_t*) srtd_ptr;
243     ServiceResponseTimeDialog *srt_dlg = static_cast<ServiceResponseTimeDialog *>(srtd->user_data);
244     if (!srt_dlg || !srt_dlg->statsTreeWidget()) return;
245
246     QTreeWidgetItemIterator it(srt_dlg->statsTreeWidget());
247     while (*it) {
248         if ((*it)->type() == srt_row_type_) {
249             SrtRowTreeWidgetItem *srtr_ti = static_cast<SrtRowTreeWidgetItem *>((*it));
250             srtr_ti->draw();
251         }
252         ++it;
253     }
254
255     for (int i = 0; i < srt_dlg->statsTreeWidget()->columnCount() - 1; i++) {
256         srt_dlg->statsTreeWidget()->resizeColumnToContents(i);
257     }
258 }
259
260 void ServiceResponseTimeDialog::fillTree()
261 {
262     srt_data_t srt_data;
263     srt_data.srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table*));
264     srt_data.user_data = this;
265
266     srt_table_dissector_init(srt_, srt_data.srt_array, NULL, NULL);
267
268     QString display_filter = displayFilter();
269     GString *error_string = register_tap_listener(get_srt_tap_listener_name(srt_),
270                           &srt_data,
271                           display_filter.toUtf8().constData(),
272                           0,
273                           tapReset,
274                           get_srt_packet_func(srt_),
275                           tapDraw);
276     if (error_string) {
277         QMessageBox::critical(this, tr("Failed to attach to tap \"%1\"").arg(get_srt_tap_listener_name(srt_)),
278                              error_string->str);
279         g_string_free(error_string, TRUE);
280         g_array_free(srt_data.srt_array, TRUE);
281         reject();
282     }
283
284     statsTreeWidget()->setSortingEnabled(false);
285
286     cap_file_.retapPackets();
287
288     // We only have one table. Move its tree items up one level.
289     if (statsTreeWidget()->invisibleRootItem()->childCount() == 1) {
290         statsTreeWidget()->setRootIndex(statsTreeWidget()->model()->index(0, 0));
291     }
292
293     tapDraw(&srt_data);
294
295     statsTreeWidget()->sortItems(SRT_COLUMN_PROCEDURE, Qt::AscendingOrder);
296     statsTreeWidget()->setSortingEnabled(true);
297
298     remove_tap_listener(&srt_data);
299     g_array_free(srt_data.srt_array, TRUE);
300 }
301
302 QList<QVariant> ServiceResponseTimeDialog::treeItemData(QTreeWidgetItem *ti) const
303 {
304     QList<QVariant> tid;
305     if (ti->type() == srt_table_type_) {
306         SrtTableTreeWidgetItem *srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti);
307         if (srtt_ti) {
308             tid << srtt_ti->rowData();
309         }
310     } else if (ti->type() == srt_row_type_) {
311         SrtRowTreeWidgetItem *srtr_ti = static_cast<SrtRowTreeWidgetItem *>(ti);
312         if (srtr_ti) {
313             tid << srtr_ti->rowData();
314         }
315     }
316     return tid;
317 }
318
319 const QString ServiceResponseTimeDialog::filterExpression()
320 {
321     QString filter_expr;
322     if (statsTreeWidget()->selectedItems().count() > 0) {
323         QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
324         if (ti->type() == srt_row_type_) {
325             SrtTableTreeWidgetItem *srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti->parent());
326             QString field = srtt_ti->filterField();
327             QString value = ti->text(SRT_COLUMN_INDEX);
328             if (srtt_ti && !field.isEmpty() && !value.isEmpty()) {
329                 filter_expr = QString("%1==%2").arg(srtt_ti->filterField()).arg(value);
330             }
331         }
332     }
333     return filter_expr;
334 }
335
336 void ServiceResponseTimeDialog::statsTreeWidgetItemChanged()
337 {
338     QString procedure_title = service_response_time_get_column_name(SRT_COLUMN_PROCEDURE);
339
340     if (statsTreeWidget()->selectedItems().count() > 0) {
341         QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
342         SrtTableTreeWidgetItem *srtt_ti = NULL;
343         if (ti->type() == srt_row_type_) {
344             srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti->parent());
345         } else {
346             srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti);
347         }
348         if (srtt_ti) {
349             procedure_title = srtt_ti->columnTitle();
350         }
351     }
352     statsTreeWidget()->headerItem()->setText(SRT_COLUMN_PROCEDURE, procedure_title);
353 }
354
355 /*
356  * Editor modelines
357  *
358  * Local Variables:
359  * c-basic-offset: 4
360  * tab-width: 8
361  * indent-tabs-mode: nil
362  * End:
363  *
364  * ex: set shiftwidth=4 tabstop=8 expandtab:
365  * :indentSize=4:tabSize=8:noTabs=true:
366  */