/* service_response_time_dialog.cpp * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "service_response_time_dialog.h" #include "file.h" #include #include #include "rpc_service_response_time_dialog.h" #include "scsi_service_response_time_dialog.h" #include "wireshark_application.h" #include #include static QHash cfg_str_to_srt_; extern "C" { static void srt_init(const char *args, void*) { QStringList args_l = QString(args).split(','); if (args_l.length() > 1) { QString srt = QString("%1,%2").arg(args_l[0]).arg(args_l[1]); QString filter; if (args_l.length() > 2) { filter = QStringList(args_l.mid(2)).join(","); } wsApp->emitTapParameterSignal(srt, filter, NULL); } } } gboolean register_service_response_tables(const void *, void *value, void*) { register_srt_t *srt = (register_srt_t*)value; const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt))); char *cfg_abbr = srt_table_get_tap_string(srt); tpdCreator tpd_creator = ServiceResponseTimeDialog::createSrtDialog; /* XXX - These dissectors haven't been converted over to due to an "interactive input dialog" for their tap data. Let those specific dialogs register for themselves */ if (strcmp(short_name, "DCERPC") == 0) { short_name = "DCE-RPC"; tpd_creator = RpcServiceResponseTimeDialog::createDceRpcSrtDialog; } else if (strcmp(short_name, "RPC") == 0) { short_name = "ONC-RPC"; tpd_creator = RpcServiceResponseTimeDialog::createOncRpcSrtDialog; } else if (strcmp(short_name, "SCSI") == 0) { tpd_creator = ScsiServiceResponseTimeDialog::createScsiSrtDialog; } cfg_str_to_srt_[cfg_abbr] = srt; TapParameterDialog::registerDialog( short_name, cfg_abbr, REGISTER_STAT_GROUP_RESPONSE_TIME, srt_init, tpd_creator); g_free(cfg_abbr); return FALSE; } enum { srt_table_type_ = 1000, srt_row_type_ }; class SrtRowTreeWidgetItem : public QTreeWidgetItem { public: SrtRowTreeWidgetItem(QTreeWidgetItem *parent, const srt_procedure_t *procedure) : QTreeWidgetItem (parent, srt_row_type_), procedure_(procedure) { setText(SRT_COLUMN_PROCEDURE, procedure_->procedure); setHidden(true); } void draw() { setText(SRT_COLUMN_INDEX, QString::number(procedure_->proc_index)); setText(SRT_COLUMN_CALLS, QString::number(procedure_->stats.num)); setText(SRT_COLUMN_MIN, QString::number(nstime_to_sec(&procedure_->stats.min), 'f', 6)); setText(SRT_COLUMN_MAX, QString::number(nstime_to_sec(&procedure_->stats.max), 'f', 6)); setText(SRT_COLUMN_AVG, QString::number(get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0, 'f', 6)); setText(SRT_COLUMN_SUM, QString::number(nstime_to_sec(&procedure_->stats.tot), 'f', 6)); for (int col = 0; col < columnCount(); col++) { if (col == SRT_COLUMN_PROCEDURE) continue; setTextAlignment(col, Qt::AlignRight); } setHidden(procedure_->stats.num < 1); } bool operator< (const QTreeWidgetItem &other) const { if (other.type() != srt_row_type_) return QTreeWidgetItem::operator< (other); const SrtRowTreeWidgetItem *other_row = static_cast(&other); switch (treeWidget()->sortColumn()) { case SRT_COLUMN_INDEX: return procedure_->proc_index < other_row->procedure_->proc_index; case SRT_COLUMN_CALLS: return procedure_->stats.num < other_row->procedure_->stats.num; case SRT_COLUMN_MIN: return nstime_cmp(&procedure_->stats.min, &other_row->procedure_->stats.min) < 0; case SRT_COLUMN_MAX: return nstime_cmp(&procedure_->stats.max, &other_row->procedure_->stats.max) < 0; case SRT_COLUMN_AVG: { double our_avg = get_average(&procedure_->stats.tot, procedure_->stats.num); double other_avg = get_average(&other_row->procedure_->stats.tot, other_row->procedure_->stats.num); return our_avg < other_avg; } case SRT_COLUMN_SUM: return nstime_cmp(&procedure_->stats.tot, &other_row->procedure_->stats.tot) < 0; default: break; } return QTreeWidgetItem::operator< (other); } QList rowData() { return QList() << QString(procedure_->procedure) << procedure_->proc_index << procedure_->stats.num << nstime_to_sec(&procedure_->stats.min) << nstime_to_sec(&procedure_->stats.max) << get_average(&procedure_->stats.tot, procedure_->stats.num) / 1000.0 << nstime_to_sec(&procedure_->stats.tot); } private: const srt_procedure_t *procedure_; }; class SrtTableTreeWidgetItem : public QTreeWidgetItem { public: SrtTableTreeWidgetItem(QTreeWidget *parent, const srt_stat_table *srt_table) : QTreeWidgetItem (parent, srt_table_type_), srt_table_(srt_table) { setText(0, srt_table_->name); setFirstColumnSpanned(true); setExpanded(true); for (int i = 0; i < srt_table_->num_procs; i++) { new SrtRowTreeWidgetItem(this, &srt_table_->procedures[i]); } } const QString columnTitle() { return srt_table_->proc_column_name; } QList rowData() { return QList() << srt_table_->name; } const QString filterField() { return srt_table_->filter_string; } private: const srt_stat_table *srt_table_; }; ServiceResponseTimeDialog::ServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, register_srt *srt, const QString filter, int help_topic) : TapParameterDialog(parent, cf, help_topic), srt_(srt) { QString subtitle = QString("%1 Service Response Time Statistics") .arg(proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)))); setWindowSubtitle(subtitle); loadGeometry(0, 0, "ServiceResponseTimeDialog"); srt_data_.srt_array = NULL; srt_data_.user_data = NULL; // Add number of columns for this stats_tree QStringList header_labels; for (int col = 0; col < NUM_SRT_COLUMNS; col++) { header_labels.push_back(service_response_time_get_column_name(col)); } statsTreeWidget()->setColumnCount(header_labels.count()); statsTreeWidget()->setHeaderLabels(header_labels); for (int col = 0; col < statsTreeWidget()->columnCount(); col++) { if (col == SRT_COLUMN_PROCEDURE) continue; statsTreeWidget()->headerItem()->setTextAlignment(col, Qt::AlignRight); } addFilterActions(); if (!filter.isEmpty()) { setDisplayFilter(filter); } connect(statsTreeWidget(), SIGNAL(itemChanged(QTreeWidgetItem*,int)), this, SLOT(statsTreeWidgetItemChanged())); } ServiceResponseTimeDialog::~ServiceResponseTimeDialog() { if (srt_data_.srt_array) { free_srt_table(srt_, srt_data_.srt_array); g_array_free(srt_data_.srt_array, TRUE); } } TapParameterDialog *ServiceResponseTimeDialog::createSrtDialog(QWidget &parent, const QString cfg_str, const QString filter, CaptureFile &cf) { if (!cfg_str_to_srt_.contains(cfg_str)) { // XXX MessageBox? return NULL; } register_srt_t *srt = cfg_str_to_srt_[cfg_str]; return new ServiceResponseTimeDialog(parent, cf, srt, filter); } void ServiceResponseTimeDialog::addSrtTable(const struct _srt_stat_table *srt_table) { new SrtTableTreeWidgetItem(statsTreeWidget(), srt_table); } void ServiceResponseTimeDialog::tapReset(void *srtd_ptr) { srt_data_t *srtd = (srt_data_t*) srtd_ptr; ServiceResponseTimeDialog *srt_dlg = static_cast(srtd->user_data); if (!srt_dlg) return; reset_srt_table(srtd->srt_array); srt_dlg->statsTreeWidget()->clear(); } void ServiceResponseTimeDialog::tapDraw(void *srtd_ptr) { srt_data_t *srtd = (srt_data_t*) srtd_ptr; ServiceResponseTimeDialog *srt_dlg = static_cast(srtd->user_data); if (!srt_dlg || !srt_dlg->statsTreeWidget()) return; QTreeWidgetItemIterator it(srt_dlg->statsTreeWidget()); while (*it) { if ((*it)->type() == srt_row_type_) { SrtRowTreeWidgetItem *srtr_ti = static_cast((*it)); srtr_ti->draw(); } ++it; } for (int i = 0; i < srt_dlg->statsTreeWidget()->columnCount() - 1; i++) { srt_dlg->statsTreeWidget()->resizeColumnToContents(i); } } void ServiceResponseTimeDialog::endRetapPackets() { for (guint i = 0; i < srt_data_.srt_array->len; i++) { srt_stat_table *srt_table = g_array_index(srt_data_.srt_array, srt_stat_table*, i); addSrtTable(srt_table); } WiresharkDialog::endRetapPackets(); } void ServiceResponseTimeDialog::fillTree() { if (srt_data_.srt_array) { free_srt_table(srt_, srt_data_.srt_array); g_array_free(srt_data_.srt_array, TRUE); } srt_data_.srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table*)); srt_data_.user_data = this; provideParameterData(); srt_table_dissector_init(srt_, srt_data_.srt_array); QString display_filter = displayFilter(); if (!registerTapListener(get_srt_tap_listener_name(srt_), &srt_data_, display_filter.toUtf8().constData(), 0, tapReset, get_srt_packet_func(srt_), tapDraw)) { reject(); // XXX Stay open instead? return; } statsTreeWidget()->setSortingEnabled(false); cap_file_.retapPackets(); // We only have one table. Move its tree items up one level. if (statsTreeWidget()->invisibleRootItem()->childCount() == 1) { statsTreeWidget()->setRootIndex(statsTreeWidget()->model()->index(0, 0)); } tapDraw(&srt_data_); statsTreeWidget()->sortItems(SRT_COLUMN_PROCEDURE, Qt::AscendingOrder); statsTreeWidget()->setSortingEnabled(true); removeTapListeners(); } QList ServiceResponseTimeDialog::treeItemData(QTreeWidgetItem *ti) const { QList tid; if (ti->type() == srt_table_type_) { SrtTableTreeWidgetItem *srtt_ti = static_cast(ti); if (srtt_ti) { tid << srtt_ti->rowData(); } } else if (ti->type() == srt_row_type_) { SrtRowTreeWidgetItem *srtr_ti = static_cast(ti); if (srtr_ti) { tid << srtr_ti->rowData(); } } return tid; } const QString ServiceResponseTimeDialog::filterExpression() { QString filter_expr; if (statsTreeWidget()->selectedItems().count() > 0) { QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; if (ti->type() == srt_row_type_) { SrtTableTreeWidgetItem *srtt_ti = static_cast(ti->parent()); g_assert(srtt_ti); QString field = srtt_ti->filterField(); QString value = ti->text(SRT_COLUMN_INDEX); if (!field.isEmpty() && !value.isEmpty()) { filter_expr = QString("%1==%2").arg(field).arg(value); } } } return filter_expr; } void ServiceResponseTimeDialog::statsTreeWidgetItemChanged() { QString procedure_title = service_response_time_get_column_name(SRT_COLUMN_PROCEDURE); if (statsTreeWidget()->selectedItems().count() > 0) { QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; SrtTableTreeWidgetItem *srtt_ti = NULL; if (ti->type() == srt_row_type_) { srtt_ti = static_cast(ti->parent()); } else { srtt_ti = static_cast(ti); } if (srtt_ti) { procedure_title = srtt_ti->columnTitle(); } } statsTreeWidget()->headerItem()->setText(SRT_COLUMN_PROCEDURE, procedure_title); } /* * Editor modelines * * Local Variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */