1 /* protocol_hierarchy_dialog.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later*/
9 #include "protocol_hierarchy_dialog.h"
10 #include <ui_protocol_hierarchy_dialog.h>
14 #include "ui/proto_hier_stats.h"
16 #include <ui/qt/utils/variant_pointer.h>
18 #include <wsutil/utf8_entities.h>
20 #include <ui/qt/utils/qt_ui_utils.h>
21 #include "wireshark_application.h"
24 #include <QPushButton>
25 #include <QTextStream>
26 #include <QTreeWidgetItemIterator>
29 * @file Protocol Hierarchy Statistics dialog
31 * Displays tree of protocols with various statistics
32 * Allows filtering on tree items
36 // - Make "Copy as YAML" output a tree?
37 // - Add time series data to ph_stats_node_t and draw sparklines.
39 const int protocol_col_ = 0;
40 const int pct_packets_col_ = 1;
41 const int packets_col_ = 2;
42 const int pct_bytes_col_ = 3;
43 const int bytes_col_ = 4;
44 const int bandwidth_col_ = 5;
45 const int end_packets_col_ = 6;
46 const int end_bytes_col_ = 7;
47 const int end_bandwidth_col_ = 8;
49 class ProtocolHierarchyTreeWidgetItem : public QTreeWidgetItem
52 ProtocolHierarchyTreeWidgetItem(QTreeWidgetItem *parent, ph_stats_node_t& ph_stats_node) :
53 QTreeWidgetItem(parent),
54 total_packets_(ph_stats_node.num_pkts_total),
55 last_packets_(ph_stats_node.num_pkts_last),
56 total_bytes_(ph_stats_node.num_bytes_total),
57 last_bytes_(ph_stats_node.num_bytes_last),
63 filter_name_ = ph_stats_node.hfinfo->abbrev;
66 ph_stats_t *ph_stats = VariantPointer<ph_stats_t>::asPtr(parent->treeWidget()->invisibleRootItem()->data(0, Qt::UserRole));
68 if (!ph_stats || ph_stats->tot_packets < 1) return;
69 percent_packets_ = total_packets_ * 100.0 / ph_stats->tot_packets;
70 percent_bytes_ = total_bytes_ * 100.0 / ph_stats->tot_bytes;
72 double seconds = ph_stats->last_time - ph_stats->first_time;
75 bits_s_ = total_bytes_ * 8.0 / seconds;
76 end_bits_s_ = last_bytes_ * 8.0 / seconds;
79 setText(protocol_col_, ph_stats_node.hfinfo->name);
80 setData(pct_packets_col_, Qt::UserRole, percent_packets_);
81 setText(packets_col_, QString::number(total_packets_));
82 setData(pct_bytes_col_, Qt::UserRole, percent_bytes_);
83 setText(bytes_col_, QString::number(total_bytes_));
84 setText(bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(bits_s_) : UTF8_EM_DASH);
85 setText(end_packets_col_, QString::number(last_packets_));
86 setText(end_bytes_col_, QString::number(last_bytes_));
87 setText(end_bandwidth_col_, seconds > 0.0 ? bits_s_to_qstring(end_bits_s_) : UTF8_EM_DASH);
90 // Return a QString, int, double, or invalid QVariant representing the raw column data.
91 QVariant colData(int col) const {
95 case (pct_packets_col_):
96 return percent_packets_;
98 return total_packets_;
99 case (pct_bytes_col_):
100 return percent_bytes_;
103 case (bandwidth_col_):
105 case (end_packets_col_):
106 return last_packets_;
107 case (end_bytes_col_):
109 case (end_bandwidth_col_):
117 bool operator< (const QTreeWidgetItem &other) const
119 const ProtocolHierarchyTreeWidgetItem &other_phtwi = dynamic_cast<const ProtocolHierarchyTreeWidgetItem&>(other);
121 switch (treeWidget()->sortColumn()) {
122 case pct_packets_col_:
123 return percent_packets_ < other_phtwi.percent_packets_;
125 return total_packets_ < other_phtwi.total_packets_;
127 return percent_packets_ < other_phtwi.percent_packets_;
129 return total_bytes_ < other_phtwi.total_bytes_;
131 return bits_s_ < other_phtwi.bits_s_;
132 case end_packets_col_:
133 return last_packets_ < other_phtwi.last_packets_;
135 return last_bytes_ < other_phtwi.last_bytes_;
136 case end_bandwidth_col_:
137 return end_bits_s_ < other_phtwi.end_bits_s_;
142 // Fall back to string comparison
143 return QTreeWidgetItem::operator <(other);
146 const QString filterName(void) { return filter_name_; }
149 QString filter_name_;
150 unsigned total_packets_;
151 unsigned last_packets_;
152 unsigned total_bytes_;
153 unsigned last_bytes_;
155 double percent_packets_;
156 double percent_bytes_;
161 ProtocolHierarchyDialog::ProtocolHierarchyDialog(QWidget &parent, CaptureFile &cf) :
162 WiresharkDialog(parent, cf),
163 ui(new Ui::ProtocolHierarchyDialog)
166 loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5);
167 setWindowSubtitle(tr("Protocol Hierarchy Statistics"));
169 ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_packets_col_, &percent_bar_delegate_);
170 ui->hierStatsTreeWidget->setItemDelegateForColumn(pct_bytes_col_, &percent_bar_delegate_);
171 ph_stats_t *ph_stats = ph_stats_new(cap_file_.capFile());
173 ui->hierStatsTreeWidget->invisibleRootItem()->setData(0, Qt::UserRole, VariantPointer<ph_stats_t>::asQVariant(ph_stats));
174 g_node_children_foreach(ph_stats->stats_tree, G_TRAVERSE_ALL, addTreeNode, ui->hierStatsTreeWidget->invisibleRootItem());
175 ph_stats_free(ph_stats);
178 ui->hierStatsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
179 connect(ui->hierStatsTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
180 SLOT(showProtoHierMenu(QPoint)));
182 ui->hierStatsTreeWidget->setSortingEnabled(true);
183 ui->hierStatsTreeWidget->expandAll();
185 for (int i = 0; i < ui->hierStatsTreeWidget->columnCount(); i++) {
186 ui->hierStatsTreeWidget->resizeColumnToContents(i);
191 FilterAction::Action cur_action = FilterAction::ActionApply;
192 submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
193 foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
194 FilterAction *fa = new FilterAction(submenu, cur_action, at);
195 submenu->addAction(fa);
196 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
199 cur_action = FilterAction::ActionPrepare;
200 submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
201 foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
202 FilterAction *fa = new FilterAction(submenu, cur_action, at);
203 submenu->addAction(fa);
204 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
207 FilterAction *fa = new FilterAction(&ctx_menu_, FilterAction::ActionFind);
208 ctx_menu_.addAction(fa);
209 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
211 fa = new FilterAction(&ctx_menu_, FilterAction::ActionColorize);
212 ctx_menu_.addAction(fa);
213 connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
215 ctx_menu_.addSeparator();
216 ctx_menu_.addAction(ui->actionCopyAsCsv);
217 ctx_menu_.addAction(ui->actionCopyAsYaml);
219 copy_button_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole);
221 QMenu *copy_menu = new QMenu(copy_button_);
223 ca = copy_menu->addAction(tr("as CSV"));
224 ca->setToolTip(ui->actionCopyAsCsv->toolTip());
225 connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsCsv_triggered()));
226 ca = copy_menu->addAction(tr("as YAML"));
227 ca->setToolTip(ui->actionCopyAsYaml->toolTip());
228 connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsYaml_triggered()));
229 copy_button_->setMenu(copy_menu);
231 QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close);
233 close_bt->setDefault(true);
236 display_filter_ = cap_file_.capFile()->dfilter;
240 ProtocolHierarchyDialog::~ProtocolHierarchyDialog()
245 void ProtocolHierarchyDialog::showProtoHierMenu(QPoint pos)
247 bool enable = ui->hierStatsTreeWidget->currentItem() != NULL && !file_closed_ ? true : false;
249 foreach (QMenu *submenu, ctx_menu_.findChildren<QMenu*>()) {
250 submenu->setEnabled(enable);
252 foreach (QAction *action, ctx_menu_.actions()) {
253 if (action != ui->actionCopyAsCsv && action != ui->actionCopyAsYaml) {
254 action->setEnabled(enable);
258 ctx_menu_.popup(ui->hierStatsTreeWidget->viewport()->mapToGlobal(pos));
261 void ProtocolHierarchyDialog::filterActionTriggered()
263 ProtocolHierarchyTreeWidgetItem *phti = static_cast<ProtocolHierarchyTreeWidgetItem *>(ui->hierStatsTreeWidget->currentItem());
264 FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender());
269 QString filter_name(phti->filterName());
271 emit filterAction(filter_name, fa->action(), fa->actionType());
274 void ProtocolHierarchyDialog::addTreeNode(GNode *node, gpointer data)
276 ph_stats_node_t *stats = (ph_stats_node_t *)node->data;
279 QTreeWidgetItem *parent_ti = static_cast<QTreeWidgetItem *>(data);
280 if (!parent_ti) return;
282 ProtocolHierarchyTreeWidgetItem *phti = new ProtocolHierarchyTreeWidgetItem(parent_ti, *stats);
284 g_node_children_foreach(node, G_TRAVERSE_ALL, addTreeNode, phti);
288 void ProtocolHierarchyDialog::updateWidgets()
290 QString hint = "<small><i>";
291 if (display_filter_.isEmpty()) {
292 hint += tr("No display filter.");
294 hint += tr("Display filter: %1").arg(display_filter_);
296 hint += "</i></small>";
297 ui->hintLabel->setText(hint);
299 WiresharkDialog::updateWidgets();
302 QList<QVariant> ProtocolHierarchyDialog::protoHierRowData(QTreeWidgetItem *item) const
304 QList<QVariant> row_data;
306 for (int col = 0; col < ui->hierStatsTreeWidget->columnCount(); col++) {
308 row_data << ui->hierStatsTreeWidget->headerItem()->text(col);
310 ProtocolHierarchyTreeWidgetItem *phti = static_cast<ProtocolHierarchyTreeWidgetItem*>(item);
312 row_data << phti->colData(col);
319 void ProtocolHierarchyDialog::on_actionCopyAsCsv_triggered()
322 QTextStream stream(&csv, QIODevice::Text);
323 QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
327 QStringList separated_value;
328 QTreeWidgetItem *item = first ? NULL : (*iter);
330 foreach (QVariant v, protoHierRowData(item)) {
332 separated_value << "\"\"";
333 } else if (v.type() == QVariant::String) {
334 separated_value << QString("\"%1\"").arg(v.toString());
336 separated_value << v.toString();
339 stream << separated_value.join(",") << endl;
344 wsApp->clipboard()->setText(stream.readAll());
347 void ProtocolHierarchyDialog::on_actionCopyAsYaml_triggered()
350 QTextStream stream(&yaml, QIODevice::Text);
351 QTreeWidgetItemIterator iter(ui->hierStatsTreeWidget);
354 stream << "---" << endl;
356 QTreeWidgetItem *item = first ? NULL : (*iter);
358 stream << "-" << endl;
359 foreach (QVariant v, protoHierRowData(item)) {
360 stream << " - " << v.toString() << endl;
365 wsApp->clipboard()->setText(stream.readAll());
368 void ProtocolHierarchyDialog::on_buttonBox_helpRequested()
370 wsApp->helpTopicAction(HELP_STATS_PROTO_HIERARCHY_DIALOG);
379 * indent-tabs-mode: nil
382 * ex: set shiftwidth=4 tabstop=8 expandtab:
383 * :indentSize=4:tabSize=8:noTabs=true: