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 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
9
10 #include "service_response_time_dialog.h"
11
12 #include "file.h"
13
14 #include <epan/tap.h>
15 #include <wsutil/ws_assert.h>
16 #include <ui/service_response_time.h>
17
18 #include "rpc_service_response_time_dialog.h"
19 #include "scsi_service_response_time_dialog.h"
20 #include "wireshark_application.h"
21
22 #include <QTreeWidget>
23 #include <QTreeWidgetItemIterator>
24
25 static QHash<const QString, register_srt_t *> cfg_str_to_srt_;
26
27 extern "C" {
28 static void
srt_init(const char * args,void *)29 srt_init(const char *args, void*) {
30 QStringList args_l = QString(args).split(',');
31 if (args_l.length() > 1) {
32 QString srt = QString("%1,%2").arg(args_l[0]).arg(args_l[1]);
33 QString filter;
34 if (args_l.length() > 2) {
35 filter = QStringList(args_l.mid(2)).join(",");
36 }
37 wsApp->emitTapParameterSignal(srt, filter, NULL);
38 }
39 }
40 }
41
register_service_response_tables(const void *,void * value,void *)42 gboolean register_service_response_tables(const void *, void *value, void*)
43 {
44 register_srt_t *srt = (register_srt_t*)value;
45 const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)));
46 char *cfg_abbr = srt_table_get_tap_string(srt);
47 tpdCreator tpd_creator = ServiceResponseTimeDialog::createSrtDialog;
48
49 /* XXX - These dissectors haven't been converted over to due to an "interactive input dialog" for their
50 tap data. Let those specific dialogs register for themselves */
51 if (strcmp(short_name, "DCERPC") == 0) {
52 short_name = "DCE-RPC";
53 tpd_creator = RpcServiceResponseTimeDialog::createDceRpcSrtDialog;
54 } else if (strcmp(short_name, "RPC") == 0) {
55 short_name = "ONC-RPC";
56 tpd_creator = RpcServiceResponseTimeDialog::createOncRpcSrtDialog;
57 } else if (strcmp(short_name, "SCSI") == 0) {
58 tpd_creator = ScsiServiceResponseTimeDialog::createScsiSrtDialog;
59 }
60
61 cfg_str_to_srt_[cfg_abbr] = srt;
62 TapParameterDialog::registerDialog(
63 short_name,
64 cfg_abbr,
65 REGISTER_STAT_GROUP_RESPONSE_TIME,
66 srt_init,
67 tpd_creator);
68 g_free(cfg_abbr);
69 return FALSE;
70 }
71
72 enum {
73 srt_table_type_ = 1000,
74 srt_row_type_
75 };
76
77 class SrtRowTreeWidgetItem : public QTreeWidgetItem
78 {
79 public:
SrtRowTreeWidgetItem(QTreeWidgetItem * parent,const srt_procedure_t * procedure)80 SrtRowTreeWidgetItem(QTreeWidgetItem *parent, const srt_procedure_t *procedure) :
81 QTreeWidgetItem (parent, srt_row_type_),
82 procedure_(procedure)
83 {
84 setText(SRT_COLUMN_PROCEDURE, procedure_->procedure);
85 setHidden(true);
86 }
87
draw()88 void draw() {
89 setText(SRT_COLUMN_INDEX, QString::number(procedure_->proc_index));
90 setText(SRT_COLUMN_CALLS, QString::number(procedure_->stats.num));
91 setText(SRT_COLUMN_MIN, QString::number(nstime_to_sec(&procedure_->stats.min), 'f', 6));
92 setText(SRT_COLUMN_MAX, QString::number(nstime_to_sec(&procedure_->stats.max), 'f', 6));
93 setText(SRT_COLUMN_AVG, QString::number(get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0, 'f', 6));
94 setText(SRT_COLUMN_SUM, QString::number(nstime_to_sec(&procedure_->stats.tot), 'f', 6));
95
96 for (int col = 0; col < columnCount(); col++) {
97 if (col == SRT_COLUMN_PROCEDURE) continue;
98 setTextAlignment(col, Qt::AlignRight);
99 }
100
101 setHidden(procedure_->stats.num < 1);
102 }
103
operator <(const QTreeWidgetItem & other) const104 bool operator< (const QTreeWidgetItem &other) const
105 {
106 if (other.type() != srt_row_type_) return QTreeWidgetItem::operator< (other);
107 const SrtRowTreeWidgetItem *other_row = static_cast<const SrtRowTreeWidgetItem *>(&other);
108
109 switch (treeWidget()->sortColumn()) {
110 case SRT_COLUMN_INDEX:
111 return procedure_->proc_index < other_row->procedure_->proc_index;
112 case SRT_COLUMN_CALLS:
113 return procedure_->stats.num < other_row->procedure_->stats.num;
114 case SRT_COLUMN_MIN:
115 return nstime_cmp(&procedure_->stats.min, &other_row->procedure_->stats.min) < 0;
116 case SRT_COLUMN_MAX:
117 return nstime_cmp(&procedure_->stats.max, &other_row->procedure_->stats.max) < 0;
118 case SRT_COLUMN_AVG:
119 {
120 double our_avg = get_average(&procedure_->stats.tot, procedure_->stats.num);
121 double other_avg = get_average(&other_row->procedure_->stats.tot, other_row->procedure_->stats.num);
122 return our_avg < other_avg;
123 }
124 case SRT_COLUMN_SUM:
125 return nstime_cmp(&procedure_->stats.tot, &other_row->procedure_->stats.tot) < 0;
126 default:
127 break;
128 }
129
130 return QTreeWidgetItem::operator< (other);
131 }
rowData()132 QList<QVariant> rowData() {
133 return QList<QVariant>() << QString(procedure_->procedure) << procedure_->proc_index << procedure_->stats.num
134 << nstime_to_sec(&procedure_->stats.min) << nstime_to_sec(&procedure_->stats.max)
135 << get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0
136 << nstime_to_sec(&procedure_->stats.tot);
137 }
138 private:
139 const srt_procedure_t *procedure_;
140 };
141
142 class SrtTableTreeWidgetItem : public QTreeWidgetItem
143 {
144 public:
SrtTableTreeWidgetItem(QTreeWidget * parent,const srt_stat_table * srt_table)145 SrtTableTreeWidgetItem(QTreeWidget *parent, const srt_stat_table *srt_table) :
146 QTreeWidgetItem (parent, srt_table_type_),
147 srt_table_(srt_table)
148 {
149 setText(0, srt_table_->name);
150 setFirstColumnSpanned(true);
151 setExpanded(true);
152
153 for (int i = 0; i < srt_table_->num_procs; i++) {
154 new SrtRowTreeWidgetItem(this, &srt_table_->procedures[i]);
155 }
156 }
columnTitle()157 const QString columnTitle() { return srt_table_->proc_column_name; }
158
rowData()159 QList<QVariant> rowData() {
160 return QList<QVariant>() << srt_table_->name;
161 }
filterField()162 const QString filterField() { return srt_table_->filter_string; }
163
164 private:
165 const srt_stat_table *srt_table_;
166 };
167
168
ServiceResponseTimeDialog(QWidget & parent,CaptureFile & cf,register_srt * srt,const QString filter,int help_topic)169 ServiceResponseTimeDialog::ServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, register_srt *srt, const QString filter, int help_topic) :
170 TapParameterDialog(parent, cf, help_topic),
171 srt_(srt)
172 {
173 QString subtitle = QString("%1 Service Response Time Statistics")
174 .arg(proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt))));
175 setWindowSubtitle(subtitle);
176 loadGeometry(0, 0, "ServiceResponseTimeDialog");
177
178 srt_data_.srt_array = NULL;
179 srt_data_.user_data = NULL;
180
181 // Add number of columns for this stats_tree
182 QStringList header_labels;
183 for (int col = 0; col < NUM_SRT_COLUMNS; col++) {
184 header_labels.push_back(service_response_time_get_column_name(col));
185 }
186 statsTreeWidget()->setColumnCount(header_labels.count());
187 statsTreeWidget()->setHeaderLabels(header_labels);
188
189 for (int col = 0; col < statsTreeWidget()->columnCount(); col++) {
190 if (col == SRT_COLUMN_PROCEDURE) continue;
191 statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight);
192 }
193
194 addFilterActions();
195
196 if (!filter.isEmpty()) {
197 setDisplayFilter(filter);
198 }
199
200 connect(statsTreeWidget(), SIGNAL(itemChanged(QTreeWidgetItem*,int)),
201 this, SLOT(statsTreeWidgetItemChanged()));
202 }
203
~ServiceResponseTimeDialog()204 ServiceResponseTimeDialog::~ServiceResponseTimeDialog()
205 {
206 if (srt_data_.srt_array) {
207 free_srt_table(srt_, srt_data_.srt_array);
208 g_array_free(srt_data_.srt_array, TRUE);
209 }
210 }
211
createSrtDialog(QWidget & parent,const QString cfg_str,const QString filter,CaptureFile & cf)212 TapParameterDialog *ServiceResponseTimeDialog::createSrtDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf)
213 {
214 if (!cfg_str_to_srt_.contains(cfg_str)) {
215 // XXX MessageBox?
216 return NULL;
217 }
218
219 register_srt_t *srt = cfg_str_to_srt_[cfg_str];
220
221 return new ServiceResponseTimeDialog(parent, cf, srt, filter);
222 }
223
addSrtTable(const struct _srt_stat_table * srt_table)224 void ServiceResponseTimeDialog::addSrtTable(const struct _srt_stat_table *srt_table)
225 {
226 new SrtTableTreeWidgetItem(statsTreeWidget(), srt_table);
227 }
228
tapReset(void * srtd_ptr)229 void ServiceResponseTimeDialog::tapReset(void *srtd_ptr)
230 {
231 srt_data_t *srtd = (srt_data_t*) srtd_ptr;
232 ServiceResponseTimeDialog *srt_dlg = static_cast<ServiceResponseTimeDialog *>(srtd->user_data);
233 if (!srt_dlg) return;
234
235 reset_srt_table(srtd->srt_array);
236
237 srt_dlg->statsTreeWidget()->clear();
238 }
239
tapDraw(void * srtd_ptr)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
endRetapPackets()260 void ServiceResponseTimeDialog::endRetapPackets()
261 {
262 for (guint i = 0; i < srt_data_.srt_array->len; i++) {
263 srt_stat_table *srt_table = g_array_index(srt_data_.srt_array, srt_stat_table*, i);
264 addSrtTable(srt_table);
265 }
266 WiresharkDialog::endRetapPackets();
267 }
268
fillTree()269 void ServiceResponseTimeDialog::fillTree()
270 {
271 if (srt_data_.srt_array) {
272 free_srt_table(srt_, srt_data_.srt_array);
273 g_array_free(srt_data_.srt_array, TRUE);
274 }
275 srt_data_.srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table*));
276 srt_data_.user_data = this;
277
278 provideParameterData();
279
280 srt_table_dissector_init(srt_, srt_data_.srt_array);
281
282 QString display_filter = displayFilter();
283 if (!registerTapListener(get_srt_tap_listener_name(srt_),
284 &srt_data_,
285 display_filter.toUtf8().constData(),
286 0,
287 tapReset,
288 get_srt_packet_func(srt_),
289 tapDraw)) {
290 reject(); // XXX Stay open instead?
291 return;
292 }
293
294 statsTreeWidget()->setSortingEnabled(false);
295
296 cap_file_.retapPackets();
297
298 // We only have one table. Move its tree items up one level.
299 if (statsTreeWidget()->invisibleRootItem()->childCount() == 1) {
300 statsTreeWidget()->setRootIndex(statsTreeWidget()->model()->index(0, 0));
301 }
302
303 tapDraw(&srt_data_);
304
305 statsTreeWidget()->sortItems(SRT_COLUMN_PROCEDURE, Qt::AscendingOrder);
306 statsTreeWidget()->setSortingEnabled(true);
307
308 removeTapListeners();
309 }
310
treeItemData(QTreeWidgetItem * ti) const311 QList<QVariant> ServiceResponseTimeDialog::treeItemData(QTreeWidgetItem *ti) const
312 {
313 QList<QVariant> tid;
314 if (ti->type() == srt_table_type_) {
315 SrtTableTreeWidgetItem *srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti);
316 if (srtt_ti) {
317 tid << srtt_ti->rowData();
318 }
319 } else if (ti->type() == srt_row_type_) {
320 SrtRowTreeWidgetItem *srtr_ti = static_cast<SrtRowTreeWidgetItem *>(ti);
321 if (srtr_ti) {
322 tid << srtr_ti->rowData();
323 }
324 }
325 return tid;
326 }
327
filterExpression()328 const QString ServiceResponseTimeDialog::filterExpression()
329 {
330 QString filter_expr;
331 if (statsTreeWidget()->selectedItems().count() > 0) {
332 QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
333 if (ti->type() == srt_row_type_) {
334 SrtTableTreeWidgetItem *srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti->parent());
335 ws_assert(srtt_ti);
336 QString field = srtt_ti->filterField();
337 QString value = ti->text(SRT_COLUMN_INDEX);
338 if (!field.isEmpty() && !value.isEmpty()) {
339 filter_expr = QString("%1==%2").arg(field).arg(value);
340 }
341 }
342 }
343 return filter_expr;
344 }
345
statsTreeWidgetItemChanged()346 void ServiceResponseTimeDialog::statsTreeWidgetItemChanged()
347 {
348 QString procedure_title = service_response_time_get_column_name(SRT_COLUMN_PROCEDURE);
349
350 if (statsTreeWidget()->selectedItems().count() > 0) {
351 QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0];
352 SrtTableTreeWidgetItem *srtt_ti = NULL;
353 if (ti->type() == srt_row_type_) {
354 srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti->parent());
355 } else {
356 srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti);
357 }
358 if (srtt_ti) {
359 procedure_title = srtt_ti->columnTitle();
360 }
361 }
362 statsTreeWidget()->headerItem()->setText(SRT_COLUMN_PROCEDURE, procedure_title);
363 }
364