=item B<-z> afp,srt[,I<filter>]
+Show Apple Filing Protocol service response time statistics.
+
=item B<-z> camel,srt
=item B<-z> compare,I<start>,I<stop>,I<ttl[0|1]>,I<order[0|1]>,I<variance>[,I<filter>]
Example: S<B<-z dcerpc,srt,12345778-1234-abcd-ef00-0123456789ac,1.0,ip.addr==1.2.3.4>> will collect SAMR
SRT statistics for a specific host.
+=item B<-z> bootp,stat[,I<filter>]
+
+Show DHCP (BOOTP) statistics.
+
=item B<-z> diameter,avp[,I<cmd.code>,I<field>,I<field>,I<...>]
This option enables extraction of most important diameter fields from large capture files.
=over 4
+=item B<-z help>
+
+Display all possible values for B<-z>.
+
+=item B<-z> afp,srt[,I<filter>]
+
+Show Apple Filing Protocol service response time statistics.
+
=item B<-z> conv,I<type>[,I<filter>]
Create a table that lists all conversations that could be seen in the
Example: S<B<-z dcerpc,srt,12345778-1234-abcd-ef00-0123456789ac,1.0,ip.addr==1.2.3.4>> will collect SAMR
SRT statistics for a specific host.
+=item B<-z> bootp,stat[,I<filter>]
+
+Show DHCP (BOOTP) statistics.
+
=item B<-z> fc,srt[,I<filter>]
Collect call/reply SRT (Service Response Time) data for FC. Data collected
void* table_specific_data; /** any dissector/table specific data needed for packet filtering */
} srt_stat_table;
-
struct register_srt;
struct _srt_data_t;
-typedef void (*srt_gui_init_cb)(srt_stat_table* rst, void* gui_data);
-typedef void (*srt_gui_reset_cb)(srt_stat_table* rst, void* gui_data);
-typedef void (*srt_gui_free_cb)(srt_stat_table* rst, void* gui_data);
+typedef void (*srt_gui_init_cb)(srt_stat_table* rst, void* gui_data); /* GTK+ only? */
+typedef void (*srt_gui_reset_cb)(srt_stat_table* rst, void* gui_data); /* GTK+ only? */
+typedef void (*srt_gui_free_cb)(srt_stat_table* rst, void* gui_data); /* GTK+ only? */
typedef void (*srt_proc_table_cb)(srt_stat_table* rst, int indx, struct _srt_data_t* gui_data);
typedef void (*srt_init_cb)(struct register_srt* srt, GArray* srt_array, srt_gui_init_cb gui_callback, void* gui_data);
typedef guint (*srt_param_handler_cb)(struct register_srt* srt, const char* opt_arg, char** err);
*/
/*
- * XXX - defines stuff usable regardless of the GUI toolkit. Right now,
- * that's only the menu group, which is used by tap_param_dlg.h.
+ * Menu statistics group definitions. Used by ui/qt/tap_parameter_dialog.h
+ * and ui/gtk/tap_param_dlg.h.
*
* XXX - stats should be able to register additional menu groups, although
* the question then would be "in what order should they appear in the menu?"
/*
* UI information for a tap.
*/
+typedef void (* stat_tap_init_cb)(const char *, void*);
typedef struct _stat_tap_ui {
register_stat_group_t group; /* group to which statistic belongs */
const char *title; /* title of statistic */
const char *cli_string; /* initial part of the "-z" argument for statistic */
- void (* tap_init_cb)(const char *, void*); /* callback to init function of the tap */
+ stat_tap_init_cb tap_init_cb; /* callback to init function of the tap */
size_t nparams; /* number of parameters */
tap_param *params; /* pointer to table of parameter info */
} stat_tap_ui;
extern stats_tree_cfg*
stats_tree_get_cfg_by_abbr(const char *abbr)
{
+ if (!abbr) return NULL;
return (stats_tree_cfg *)g_hash_table_lookup(registry,abbr);
}
s = g_string_new("---\n");
break;
case ST_FORMAT_XML:
- s = g_string_new("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n");
+ s = g_string_new("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
break;
case ST_FORMAT_CSV:
s = g_string_new("\"level\",\"parent\",");
#include "../register.h"
#include "ws_symbol_export.h"
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
#define STAT_TREE_ROOT "root"
#define ST_FLG_AVERAGE 0x10000000 /* Calculate averages for nodes, rather than totals */
#define stat_node_clear_flags(st,name,parent_id,with_children,flags) \
(stats_tree_manip_node(MN_CLEAR_FLAGS,(st),(name),(parent_id),(with_children),flags))
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
#endif /* __STATS_TREE_H */
/*
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef _time_stat
-#define _time_stat
+#ifndef __TIMESTATS_H__
+#define __TIMESTATS_H__
#include <glib.h>
#include "epan/packet_info.h"
#include "wsutil/nstime.h"
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
/* Summary of time statistics*/
typedef struct _timestat_t {
guint32 num; /* number of samples */
WS_DLL_PUBLIC gdouble get_average(const nstime_t *sum, guint32 num);
-#endif
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __TIMESTATS_H__ */
proto_hier_stats.c
recent.c
rtp_stream.c
+ service_response_time.c
software_update.c
ssl_key_export.c
tap_export_pdu.c
proto_hier_stats.c \
recent.c \
rtp_stream.c \
+ service_response_time.c \
software_update.c \
ssl_key_export.c \
tap_export_pdu.c \
recent_utils.h \
rtp_analysis.h \
rtp_stream.h \
+ service_response_time.h \
simple_dialog.h \
software_update.h \
ssl_key_export.h \
if (!gtk_tree_selection_get_selected(sel, &model, &iter))
return;
- gtk_tree_model_get (model, &iter, INDEX_COLUMN, &selection, -1);
+ gtk_tree_model_get (model, &iter, SRT_COLUMN_INDEX, &selection, -1);
if(selection>=(int)rst->num_procs){
simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No procedure selected");
return;
GtkTreeSelection *sel;
gtk_srt_table_t *gtk_table_data = g_new0(gtk_srt_table_t, 1);
- static const char *default_titles[] = { "Index", "Procedure", "Calls", "Min SRT (s)", "Max SRT (s)", "Avg SRT (s)", "Sum SRT (s)" };
-
/* Create GTK data for the table here */
gtk_table_data->rst = rst;
g_array_insert_val(ss->gtk_srt_array, ss->gtk_srt_array->len, gtk_table_data);
}
/* Create the store */
- store = gtk_list_store_new (N_COLUMNS, /* Total number of columns */
+ store = gtk_list_store_new (NUM_SRT_COLUMNS, /* Total number of columns */
G_TYPE_INT, /* Index */
G_TYPE_STRING, /* Procedure */
G_TYPE_UINT, /* Calls */
/* The view now holds a reference. We can get rid of our own reference */
g_object_unref (G_OBJECT (store));
- for (i = 0; i < N_COLUMNS; i++) {
+ for (i = 0; i < NUM_SRT_COLUMNS; i++) {
renderer = gtk_cell_renderer_text_new ();
- if (i != PROCEDURE_COLUMN) {
+ if (i != SRT_COLUMN_PROCEDURE) {
/* right align numbers */
g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
}
g_object_set(renderer, "ypad", 0, NULL);
switch (i) {
- case MIN_SRT_COLUMN:
- case MAX_SRT_COLUMN:
- column = gtk_tree_view_column_new_with_attributes (default_titles[i], renderer, NULL);
+ case SRT_COLUMN_MIN:
+ case SRT_COLUMN_MAX:
+ column = gtk_tree_view_column_new_with_attributes (service_response_time_get_column_name(i), renderer, NULL);
gtk_tree_view_column_set_cell_data_func(column, renderer, srt_time_func, GINT_TO_POINTER(i), NULL);
gtk_tree_sortable_set_sort_func(sortable, i, srt_time_sort_func, GINT_TO_POINTER(i), NULL);
break;
- case AVG_SRT_COLUMN:
- case SUM_SRT_COLUMN:
- column = gtk_tree_view_column_new_with_attributes (default_titles[i], renderer, NULL);
+ case SRT_COLUMN_AVG:
+ case SRT_COLUMN_SUM:
+ column = gtk_tree_view_column_new_with_attributes (service_response_time_get_column_name(i), renderer, NULL);
gtk_tree_view_column_set_cell_data_func(column, renderer, srt_avg_func, GINT_TO_POINTER(i), NULL);
break;
case PROCEDURE_COLUMN:
- column = gtk_tree_view_column_new_with_attributes (((rst->proc_column_name != NULL) ? rst->proc_column_name : default_titles[i]), renderer, "text",
+ column = gtk_tree_view_column_new_with_attributes ((rst->proc_column_name != NULL) ? rst->proc_column_name : service_response_time_get_column_name(i), renderer, "text",
i, NULL);
break;
default:
- column = gtk_tree_view_column_new_with_attributes (default_titles[i], renderer, "text", i, NULL);
+ column = gtk_tree_view_column_new_with_attributes (service_response_time_get_column_name(i), renderer, "text", i, NULL);
break;
}
gtk_tree_view_column_set_sort_column_id(column, i);
gtk_tree_view_column_set_resizable(column, TRUE);
gtk_tree_view_append_column (gtk_table_data->table, column);
- if (i == CALLS_COLUMN) {
+ if (i == SRT_COLUMN_CALLS) {
/* XXX revert order sort */
gtk_tree_view_column_clicked(column);
gtk_tree_view_column_clicked(column);
gtk_window_set_destroy_with_parent (GTK_WINDOW(ss->gtk_data.win), TRUE);
gtk_window_set_default_size(GTK_WINDOW(ss->gtk_data.win), SRT_PREFERRED_WIDTH, 600);
- str = g_strdup_printf("%s Service Response Time statistics", proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt))));
+ str = g_strdup_printf("%s Service Response Time Statistics", proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt))));
set_window_title(ss->gtk_data.win, str);
ss->gtk_data.vbox=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 3, FALSE);
label=gtk_label_new(filter_string);
gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
- gtk_widget_set_tooltip_text (label, filter ? filter : "");
+ gtk_widget_set_tooltip_text (label, filter ? filter : "");
g_free(filter_string);
gtk_box_pack_start(GTK_BOX(ss->gtk_data.vbox), label, FALSE, FALSE, 0);
#include <gtk/gtk.h>
#include "wsutil/nstime.h"
-#include "epan/srt_table.h"
+#include "ui/service_response_time.h"
/** Suggested width of SRT window */
#define SRT_PREFERRED_WIDTH 650
#define MAX_FILTER_STRING_LENGTH 1000
/** @file
- * Helper routines common to all service response time statistics tap.
+ * Helper routines common to all service response time statistics taps.
*/
/** Statistics table */
# need to go here.
set(WIRESHARK_QT_HEADERS
about_dialog.h
+ accordion_frame.h
bluetooth_att_server_attributes_dialog.h
bluetooth_devices_dialog.h
accordion_frame.h
simple_dialog.h
splash_overlay.h
stats_tree_dialog.h
+ service_response_time_dialog.h
syntax_line_edit.h
tap_parameter_dialog.h
tcp_stream_dialog.h
search_frame.cpp
sequence_diagram.cpp
sequence_dialog.cpp
+ service_response_time_dialog.cpp
simple_dialog.cpp
splash_overlay.cpp
sparkline_delegate.cpp
sctp_graph_byte_dialog.h \
sequence_diagram.h \
sequence_dialog.h \
+ service_response_time_dialog.h \
simple_dialog.h \
sparkline_delegate.h \
splash_overlay.h \
search_frame.cpp \
sequence_diagram.cpp \
sequence_dialog.cpp \
+ service_response_time_dialog.cpp \
simple_dialog.cpp \
sparkline_delegate.cpp \
splash_overlay.cpp \
sctp_graph_arwnd_dialog.h \
sctp_graph_byte_dialog.h \
search_frame.h \
+ service_response_time_dialog.h \
splash_overlay.h \
stats_tree_dialog.h \
tango_colors.h \
simple_dialog.h \
sparkline_delegate.h \
syntax_line_edit.h \
+ tap_parameter_dialog.h \
time_shift_dialog.h \
wireshark_application.h \
wireshark_dialog.h \
search_frame.cpp \
sequence_diagram.cpp \
sequence_dialog.cpp \
+ service_response_time_dialog.cpp \
simple_dialog.cpp \
sparkline_delegate.cpp \
splash_overlay.cpp \
#include "file.h"
+#include <epan/epan_dissect.h>
#include <epan/expert.h>
#include <epan/stat_tap_ui.h>
#include <epan/tap.h>
eid->clearAllData();
}
-gboolean ExpertInfoDialog::tapPacket(void *eid_ptr, packet_info *pinfo, epan_dissect_t *, const void *data)
+gboolean ExpertInfoDialog::tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data)
{
ExpertInfoDialog *eid = static_cast<ExpertInfoDialog *>(eid_ptr);
expert_info_t *expert_info = (expert_info_t *) data;
#include <glib.h>
-#include "epan/epan_dissect.h"
-
#include "filter_action.h"
#include "wireshark_dialog.h"
#include <QMenu>
#include <QTreeWidgetItem>
+struct epan_dissect;
struct expert_info_s;
+struct _packet_info;
namespace Ui {
class ExpertInfoDialog;
// Callbacks for register_tap_listener
static void tapReset(void *eid_ptr);
- static gboolean tapPacket(void *eid_ptr, packet_info *pinfo, epan_dissect_t *, const void *data);
+ static gboolean tapPacket(void *eid_ptr, struct _packet_info *pinfo, struct epan_dissect *, const void *data);
static void tapDraw(void *eid_ptr);
private slots:
}
-const QList<FilterAction::ActionType> FilterAction::actionTypes()
+const QList<FilterAction::ActionType> FilterAction::actionTypes(Action filter_action)
{
static const QList<ActionType> action_types_ = QList<ActionType>()
<< ActionTypePlain
<< ActionTypeOr
<< ActionTypeAndNot
<< ActionTypeOrNot;
+
+ static const QList<ActionType> simple_action_types_ = QList<ActionType>()
+ << ActionTypePlain
+ << ActionTypeNot;
+
+ switch (filter_action) {
+ case ActionFind:
+ case ActionColorize:
+ return simple_action_types_;
+ default:
+ break;
+ }
+
return action_types_;
}
static const QString actionName(Action action);
ActionType actionType() { return type_; }
- static const QList<ActionType> actionTypes();
+ static const QList<ActionType> actionTypes(Action filter_action = ActionApply);
static const QString actionTypeName(ActionType type);
ActionDirection actionDirection() { return direction_; }
if (!gbl_cur_main_window_) {
connect(wsApp, SIGNAL(openStatCommandDialog(QString,const char*,void*)),
this, SLOT(openStatCommandDialog(QString,const char*,void*)));
+ connect(wsApp, SIGNAL(openTapParameterDialog(QString,const QString,void*)),
+ this, SLOT(openTapParameterDialog(QString,const QString,void*)));
}
gbl_cur_main_window_ = this;
#ifdef HAVE_LIBPCAP
connect(wsApp, SIGNAL(appInitialized()), this, SLOT(setFeaturesEnabled()));
connect(wsApp, SIGNAL(appInitialized()), this, SLOT(zoomText()));
connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatsPluginsToMenu()));
+ connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addStatisticsMenus()));
connect(wsApp, SIGNAL(appInitialized()), this, SLOT(addExternalMenus()));
connect(wsApp, SIGNAL(profileChanging()), this, SLOT(saveWindowGeometry()));
#endif
}
+void MainWindow::addStatisticsMenus()
+{
+
+ // Unsorted
+ // actionStatistics_REGISTER_STAT_GROUP_UNSORTED should exist and be.
+ // invisible.
+ QList<QAction *>unsorted_actions = wsApp->statisticsGroupItems(REGISTER_STAT_GROUP_UNSORTED);
+
+ foreach (QAction *unsorted_action, unsorted_actions) {
+ main_ui_->menuStatistics->insertAction(
+ main_ui_->actionStatistics_REGISTER_STAT_GROUP_UNSORTED,
+ unsorted_action);
+ connect(unsorted_action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
+ }
+
+ // Response time
+ QList<QAction *>sg_actions = wsApp->statisticsGroupItems(REGISTER_STAT_GROUP_RESPONSE_TIME);
+
+ foreach (QAction *sg_action, sg_actions) {
+ main_ui_->menuServiceResponseTime->addAction(sg_action);
+ connect(sg_action, SIGNAL(triggered(bool)), this, SLOT(openTapParameterDialog()));
+ }
+}
+
void MainWindow::externalMenuHelper(ext_menu_t * menu, QMenu * subMenu, gint depth)
{
QAction * itemAction = NULL;
public slots:
// in main_window_slots.cpp
- void openCaptureFile(QString& cf_path = *new QString(), QString& display_filter = *new QString(), unsigned int type = WTAP_TYPE_AUTO);
+ /**
+ * Open a capture file.
+ * @param cf_path Path to the file.
+ * @param display_filter Display filter to apply. May be empty.
+ * @param type File type.
+ * @return True on success, false on failure.
+ */
+ // XXX We might want to return a cf_read_status_t or a CaptureFile.
+ bool openCaptureFile(QString& cf_path = *new QString(), QString& display_filter = *new QString(), unsigned int type = WTAP_TYPE_AUTO);
void filterPackets(QString& new_filter = *new QString(), bool force = false);
void updateForUnsavedChanges();
void layoutPanes();
void showColumnEditor(int column);
void showPreferenceEditor(); // module_t *, pref *
void addStatsPluginsToMenu();
+ void addStatisticsMenus();
void addExternalMenus();
void startInterfaceCapture(bool valid);
*/
void openStatCommandDialog(const QString &menu_path, const char *arg, void *userdata);
+ /** Pass tap parameter arguments to a slot.
+ * @param menu_path slot Partial slot name, e.g. "StatisticsAFPSrt".
+ * @param arg "-z" argument, e.g. "afp,srt".
+ * @param userdata Optional user data.
+ */
+ void openTapParameterDialog(const QString cfg_str, const QString arg, void *userdata);
+ void openTapParameterDialog();
+
// Automatically connected slots ("on_<object>_<signal>").
//
// The slots below follow the naming conventaion described in
void on_actionStatisticsHTTPPacketCounter_triggered();
void on_actionStatisticsHTTPRequests_triggered();
void on_actionStatisticsHTTPLoadDistribution_triggered();
- void on_actionStatisticsPacketLen_triggered();
+ void on_actionStatisticsPacketLengths_triggered();
void statCommandIOGraph(const char *, void *);
void on_actionStatisticsIOGraph_triggered();
void on_actionStatisticsSametime_triggered();
<addaction name="actionStatistics29WestLBTRM"/>
<addaction name="actionStatistics29WestLBTRU"/>
</widget>
+ <widget class="QMenu" name="menuServiceResponseTime">
+ <property name="title">
+ <string>Service Response Time</string>
+ </property>
+ </widget>
<addaction name="actionStatisticsCaptureFileProperties"/>
<addaction name="actionStatisticsProtocolHierarchy"/>
<addaction name="actionStatisticsConversations"/>
<addaction name="actionStatisticsEndpoints"/>
- <addaction name="actionStatisticsPacketLen"/>
+ <addaction name="actionStatisticsPacketLengths"/>
<addaction name="actionStatisticsIOGraph"/>
+ <addaction name="menuServiceResponseTime"/>
<addaction name="separator"/>
- <addaction name="separator"/>
+ <addaction name="actionStatistics_REGISTER_STAT_GROUP_UNSORTED"/>
<addaction name="menu29West"/>
<addaction name="actionStatisticsANCP"/>
<addaction name="menuBACnet"/>
<string>HTTP load distribution</string>
</property>
</action>
- <action name="actionStatisticsPacketLen">
+ <action name="actionStatisticsPacketLengths">
<property name="text">
<string>Packet Lengths</string>
</property>
<string>Add an expression to the display filter.</string>
</property>
</action>
+ <action name="actionStatistics_REGISTER_STAT_GROUP_UNSORTED">
+ <property name="text">
+ <string>REGISTER_STAT_GROUP_UNSORTED</string>
+ </property>
+ <property name="toolTip">
+ <string>Start of "REGISTER_STAT_GROUP_UNSORTED"</string>
+ </property>
+ <property name="visible">
+ <bool>false</bool>
+ </property>
+ <property name="menuRole">
+ <enum>QAction::NoRole</enum>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
#include "sctp_graph_dialog.h"
#include "sequence_dialog.h"
#include "stats_tree_dialog.h"
+#include "tap_parameter_dialog.h"
#include "tcp_stream_dialog.h"
#include "time_shift_dialog.h"
#include "voip_calls_dialog.h"
const char *dfe_property_ = "display filter expression"; //TODO : Fix Translate
-void MainWindow::openCaptureFile(QString& cf_path, QString& read_filter, unsigned int type)
+bool MainWindow::openCaptureFile(QString& cf_path, QString& read_filter, unsigned int type)
{
QString file_name = "";
dfilter_t *rfcode = NULL;
if (open_dlg.open(file_name, type)) {
cf_path = file_name;
} else {
- return;
+ return false;
}
}
if (!testCaptureFileClose(false)) {
- return;
+ return false;
}
if (dfilter_compile(read_filter.toUtf8().constData(), &rfcode, &err_msg)) {
string and return (without changing the last containing
directory). */
capture_file_.setCapFile(NULL);
- return;
+ return false;
}
break;
}
wsApp->setLastOpenDir(get_dirname(cf_path.toUtf8().data()));
main_ui_->statusBar->showExpert();
+
+ return true;
}
void MainWindow::filterPackets(QString& new_filter, bool force)
QMetaObject::invokeMethod(this, slot.toLatin1().constData(), Q_ARG(const char *, arg), Q_ARG(void *, userdata));
}
+void MainWindow::openTapParameterDialog(const QString cfg_str, const QString arg, void *userdata)
+{
+ TapParameterDialog *tp_dialog = TapParameterDialog::showTapParameterStatistics(*this, capture_file_, cfg_str, arg, userdata);
+ if (!tp_dialog) return;
+
+ connect(tp_dialog, SIGNAL(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)),
+ this, SLOT(filterAction(QString&,FilterAction::Action,FilterAction::ActionType)));
+ connect(tp_dialog, SIGNAL(updateFilter(QString&, bool)),
+ this, SLOT(filterPackets(QString&, bool)));
+ tp_dialog->show();
+}
+
+void MainWindow::openTapParameterDialog()
+{
+ QAction *tpa = qobject_cast<QAction *>(QObject::sender());
+ if (!tpa) return;
+
+ const QString cfg_str = tpa->data().toString();
+ openTapParameterDialog(cfg_str, NULL, NULL);
+}
+
// File Menu
void MainWindow::on_actionFileOpen_triggered()
openStatisticsTreeDialog("http_srv");
}
-void MainWindow::on_actionStatisticsPacketLen_triggered()
+void MainWindow::on_actionStatisticsPacketLengths_triggered()
{
openStatisticsTreeDialog("plen");
}
#include <wsutil/str_util.h>
+#include <QAction>
#include <QFontDatabase>
/* Make the format_size_flags_e enum usable in C++ */
}
}
+bool qActionLessThan(const QAction * a1, const QAction * a2) {
+ return a1->text().compare(a2->text()) < 0;
+}
+
/*
* Editor modelines
*
*/
void smooth_font_size(QFont &font);
+/**
+ * Compare the text of two QActions. Useful for passing to std::sort.
+ *
+ * @param a1 First action
+ * @param a2 Second action
+ * @return
+ */
+class QAction;
+bool qActionLessThan(const QAction * a1, const QAction * a2);
+
#endif /* __QT_UI_UTILS__H__ */
// XXX Add a routine to fetch the HWND corresponding to a widget using QPlatformIntegration
--- /dev/null
+/* service_response_time_dialog.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "service_response_time_dialog.h"
+
+#include "file.h"
+
+#include <epan/timestats.h>
+#include <epan/tap.h>
+
+#include <ui/service_response_time.h>
+
+#include "wireshark_application.h"
+
+#include <QMessageBox>
+#include <QTreeWidget>
+#include <QTreeWidgetItemIterator>
+
+// To do:
+
+static QHash<const QString, register_srt_t *> cfg_str_to_srt_;
+
+extern "C" {
+// XXX Need to handle filters.
+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);
+ }
+}
+}
+
+void register_service_response_tables(gpointer data, gpointer)
+{
+ register_srt_t *srt = (register_srt_t*)data;
+ const char* short_name = proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)));
+ const char *cfg_abbr = srt_table_get_tap_string(srt);
+
+ /* 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, "RPC") == 0) ||
+ (strcmp(short_name, "DCERPC") == 0))
+ return;
+
+ cfg_str_to_srt_[cfg_abbr] = srt;
+ TapParameterDialog::registerDialog(
+ short_name,
+ cfg_abbr,
+ REGISTER_STAT_GROUP_RESPONSE_TIME,
+ srt_init,
+ ServiceResponseTimeDialog::createSrtDialog);
+}
+
+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_->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));
+ double avg_time = 0.0;
+ if (procedure_->stats.num) {
+ avg_time = nstime_to_sec(&procedure_->stats.min) / procedure_->stats.num;
+ }
+ setText(SRT_COLUMN_AVG, QString::number(avg_time, '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<const SrtRowTreeWidgetItem *>(&other);
+
+ switch (treeWidget()->sortColumn()) {
+ case SRT_COLUMN_INDEX:
+ return procedure_->index < other_row->procedure_->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 = nstime_to_msec(&procedure_->stats.tot) / procedure_->stats.num;
+ double other_avg = nstime_to_msec(&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<QVariant> rowData() {
+ double avg_time = 0.0;
+ if (procedure_->stats.num) {
+ avg_time = nstime_to_sec(&procedure_->stats.min) / procedure_->stats.num;
+ }
+ return QList<QVariant>() << QString(procedure_->procedure) << procedure_->index << procedure_->stats.num
+ << nstime_to_sec(&procedure_->stats.min) << nstime_to_sec(&procedure_->stats.max)
+ << avg_time << 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<QVariant> rowData() {
+ return QList<QVariant>() << 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);
+
+ // 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);
+ }
+
+ QMenu *submenu;
+ QAction *insert_action = ctx_menu_.actions().first();
+
+ FilterAction::Action cur_action = FilterAction::ActionApply;
+ submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
+ foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
+ FilterAction *fa = new FilterAction(submenu, cur_action, at);
+ submenu->addAction(fa);
+ connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
+ filter_actions_ << fa;
+ }
+ ctx_menu_.insertMenu(insert_action, submenu);
+
+ cur_action = FilterAction::ActionPrepare;
+ submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
+ foreach (FilterAction::ActionType at, FilterAction::actionTypes()) {
+ FilterAction *fa = new FilterAction(submenu, cur_action, at);
+ submenu->addAction(fa);
+ connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
+ filter_actions_ << fa;
+ }
+ ctx_menu_.insertMenu(insert_action, submenu);
+
+ cur_action = FilterAction::ActionFind;
+ submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
+ foreach (FilterAction::ActionType at, FilterAction::actionTypes(cur_action)) {
+ FilterAction *fa = new FilterAction(submenu, cur_action, at);
+ submenu->addAction(fa);
+ connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
+ filter_actions_ << fa;
+ }
+ ctx_menu_.insertMenu(insert_action, submenu);
+
+ cur_action = FilterAction::ActionColorize;
+ submenu = ctx_menu_.addMenu(FilterAction::actionName(cur_action));
+ foreach (FilterAction::ActionType at, FilterAction::actionTypes(cur_action)) {
+ FilterAction *fa = new FilterAction(submenu, cur_action, at);
+ submenu->addAction(fa);
+ connect(fa, SIGNAL(triggered()), this, SLOT(filterActionTriggered()));
+ filter_actions_ << fa;
+ }
+ ctx_menu_.insertMenu(insert_action, submenu);
+ ctx_menu_.insertSeparator(insert_action);
+
+ if (!filter.isEmpty()) {
+ setDisplayFilter(filter);
+ }
+
+ connect(statsTreeWidget(), SIGNAL(itemChanged(QTreeWidgetItem*,int)),
+ this, SLOT(statsTreeWidgetItemChanged()));
+}
+
+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);
+}
+
+QTreeWidgetItem *ServiceResponseTimeDialog::addSrtTable(const struct _srt_stat_table *srt_table)
+{
+ SrtTableTreeWidgetItem *srtt_ti = new SrtTableTreeWidgetItem(statsTreeWidget(), srt_table);
+ return srtt_ti;
+}
+
+void ServiceResponseTimeDialog::tapReset(void *srtd_ptr)
+{
+ srt_data_t *srtd = (srt_data_t*) srtd_ptr;
+ ServiceResponseTimeDialog *srt_dlg = static_cast<ServiceResponseTimeDialog *>(srtd->user_data);
+ if (!srt_dlg) return;
+
+ reset_srt_table(srtd->srt_array, NULL, NULL);
+
+ srt_dlg->statsTreeWidget()->clear();
+ for (guint i = 0; i < srtd->srt_array->len; i++) {
+ srt_stat_table *srt_table = g_array_index(srtd->srt_array, srt_stat_table*, i);
+ srt_dlg->addSrtTable(srt_table);
+ }
+}
+
+void ServiceResponseTimeDialog::tapDraw(void *srtd_ptr)
+{
+ srt_data_t *srtd = (srt_data_t*) srtd_ptr;
+ ServiceResponseTimeDialog *srt_dlg = static_cast<ServiceResponseTimeDialog *>(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<SrtRowTreeWidgetItem *>((*it));
+ srtr_ti->draw();
+ }
+ ++it;
+ }
+
+ for (int i = 0; i < srt_dlg->statsTreeWidget()->columnCount() - 1; i++) {
+ srt_dlg->statsTreeWidget()->resizeColumnToContents(i);
+ }
+}
+
+void ServiceResponseTimeDialog::fillTree()
+{
+ srt_data_t srt_data;
+ srt_data.srt_array = g_array_new(FALSE, TRUE, sizeof(srt_stat_table*));
+ srt_data.user_data = this;
+
+ srt_table_dissector_init(srt_, srt_data.srt_array, NULL, NULL);
+
+ GString *error_string = register_tap_listener(get_srt_tap_listener_name(srt_),
+ &srt_data,
+ displayFilter(),
+ 0,
+ tapReset,
+ get_srt_packet_func(srt_),
+ tapDraw);
+ if (error_string) {
+ QMessageBox::critical(this, tr("Failed to attach to tap \"%1\"").arg(get_srt_tap_listener_name(srt_)),
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ reject();
+ }
+
+ statsTreeWidget()->setSortingEnabled(false);
+
+ cf_retap_packets(cap_file_.capFile());
+
+ // 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);
+
+ remove_tap_listener(&srt_data);
+}
+
+QList<QVariant> ServiceResponseTimeDialog::treeItemData(QTreeWidgetItem *ti) const
+{
+ QList<QVariant> tid;
+ if (ti->type() == srt_table_type_) {
+ SrtTableTreeWidgetItem *srtt_ti = static_cast<SrtTableTreeWidgetItem *>(ti);
+ if (srtt_ti) {
+ tid << srtt_ti->rowData();
+ }
+ } else if (ti->type() == srt_row_type_) {
+ SrtRowTreeWidgetItem *srtr_ti = static_cast<SrtRowTreeWidgetItem *>(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<SrtTableTreeWidgetItem *>(ti->parent());
+ QString field = srtt_ti->filterField();
+ QString value = ti->text(SRT_COLUMN_INDEX);
+ if (srtt_ti && !field.isEmpty() && !value.isEmpty()) {
+ filter_expr = QString("%1==%2").arg(srtt_ti->filterField()).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<SrtTableTreeWidgetItem *>(ti->parent());
+ } else {
+ srtt_ti = static_cast<SrtTableTreeWidgetItem *>(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:
+ */
--- /dev/null
+/* service_response_time_dialog.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __SERVICE_RESPONSE_TIME_DIALOG_H__
+#define __SERVICE_RESPONSE_TIME_DIALOG_H__
+
+#include "tap_parameter_dialog.h"
+
+struct register_srt;
+struct _srt_stat_table;
+
+class QTreeWidgetItem;
+
+class ServiceResponseTimeDialog : public TapParameterDialog
+{
+ Q_OBJECT
+
+public:
+ ServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, struct register_srt *srt, const QString filter, int help_topic = 0);
+ static TapParameterDialog *createSrtDialog(QWidget &parent, const QString cfg_str, const QString arg, CaptureFile &cf);
+
+protected:
+ /** Add service response time table.
+ *
+ * In the GTK+ UI "tables" are separate, tabbed widgets. In the Qt UI they are
+ * separate groups of QTreeWidgetItems.
+ *
+ * @param title The table title (not shown if only one table).
+ * @param num_procs Number of procedures.
+ * @param filter_string filter string or QString().
+ */
+ // gtk:service_response_table.h:init_srt_table
+ QTreeWidgetItem *addSrtTable(const struct _srt_stat_table *srt_table);
+
+private:
+ struct register_srt *srt_;
+
+ // Callbacks for register_tap_listener
+ static void tapReset(void *srtd_ptr);
+ static void tapDraw(void *srtd_ptr);
+
+ virtual void fillTree();
+ virtual QList<QVariant> treeItemData(QTreeWidgetItem *ti) const;
+ virtual const QString filterExpression();
+
+private slots:
+ void statsTreeWidgetItemChanged();
+};
+
+/** Register function to register dissectors that support SRT for GTK.
+ *
+ * @param data register_srt_t* representing dissetor SRT table
+ * @param user_data is unused
+ */
+void register_service_response_tables(gpointer data, gpointer user_data);
+
+#endif // __SERVICE_RESPONSE_TIME_DIALOG_H__
+
+/*
+ * 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:
+ */
Q_DECLARE_METATYPE(stat_node *)
+const int sn_type_ = 1000;
class StatsTreeWidgetItem : public QTreeWidgetItem
{
public:
- StatsTreeWidgetItem(int type = Type) : QTreeWidgetItem (type) {}
+ StatsTreeWidgetItem(int type = sn_type_) : QTreeWidgetItem (type)
+ {
+ for (int col = 1; col < columnCount(); col++) {
+ setTextAlignment(col, Qt::AlignRight);
+ }
+ }
bool operator< (const QTreeWidgetItem &other) const
{
stat_node *thisnode = data(item_col_, Qt::UserRole).value<stat_node *>();
}
};
-
StatsTreeDialog::StatsTreeDialog(QWidget &parent, CaptureFile &cf, const char *cfg_abbr) :
TapParameterDialog(parent, cf),
st_(NULL),
GString *error_string;
if (!st_cfg_ || file_closed_) return;
- gchar* display_name_temp = stats_tree_get_displayname(st_cfg_->name);
- QString display_name(display_name_temp);
- g_free(display_name_temp);
+ QString display_name = gchar_free_to_qstring(stats_tree_get_displayname(st_cfg_->name));
// The GTK+ UI appends "Stats Tree" to the window title. If we do the same
// here we should expand the name completely, e.g. to "Statistics Tree".
st_ = stats_tree_new(st_cfg_, NULL, displayFilter());
// Add number of columns for this stats_tree
- QStringList headerLabels;
+ QStringList header_labels;
for (int count = 0; count<st_->num_columns; count++) {
- headerLabels.push_back(stats_tree_get_column_name(count));
+ header_labels.push_back(stats_tree_get_column_name(count));
}
- statsTreeWidget()->setColumnCount(headerLabels.count());
- statsTreeWidget()->setHeaderLabels(headerLabels);
+ statsTreeWidget()->setColumnCount(header_labels.count());
+ statsTreeWidget()->setHeaderLabels(header_labels);
resize(st_->num_columns*80+80, height());
- for (int count = 0; count<st_->num_columns; count++) {
- headerLabels.push_back(stats_tree_get_column_name(count));
- }
statsTreeWidget()->setSortingEnabled(false);
error_string = register_tap_listener(st_cfg_->tapname,
Q_OBJECT
public:
- explicit StatsTreeDialog(QWidget &parent, CaptureFile &cf, const char *cfg_abbr = NULL);
+ explicit StatsTreeDialog(QWidget &parent, CaptureFile &cf, const char *cfg_abbr);
~StatsTreeDialog();
static void setupNode(stat_node* node);
* - fillTree. Called when the dialog is first displayed and when a display
* filter is applied. In most cases the subclass should clear the tree and
* retap packets here.
- * - getTreeAsString.
+ * - filterExpression. If the subclass supports filtering context menu items
+ * ("Apply As Filter", etc.) it should fill in ctx_menu_ and implement
+ * filterExpression.
+ * - getTreeAsString or treeItemData. Used for "Copy" and "Save As...".
+ * -
*/
#include "tap_parameter_dialog.h"
#include <errno.h>
+#include "epan/stat_tap_ui.h"
+
#include "ui/last_open_dir.h"
#include "ui/utf8_entities.h"
#include "wireshark_application.h"
#include <QClipboard>
+#include <QContextMenuEvent>
#include <QMessageBox>
#include <QFileDialog>
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+// Qt::escape
+#include <QTextDocument>
+#endif
// The GTK+ counterpart uses tap_param_dlg, which we don't use. If we
// need tap parameters we should probably create a TapParameterDialog
// class based on WiresharkDialog and subclass it here.
// To do:
-// - Add help
-// - Update to match bug 9452 / r53657
+// - Add tap parameters? SCSI SRT uses PARAM_ENUM. Everything appears to use
+// PARAM_FILTER. Nothing uses _UINT, _STRING, or _UUID.
+// - Update to match bug 9452 / r53657.
+// - Create a TapParameterTreeWidgetItem class?
+// - Better / more usable XML output.
const int expand_all_threshold_ = 100; // Arbitrary
+static QHash<const QString, tpdCreator> cfg_str_to_creator_;
+
TapParameterDialog::TapParameterDialog(QWidget &parent, CaptureFile &cf, int help_topic) :
WiresharkDialog(parent, cf),
ui(new Ui::TapParameterDialog),
ui->setupUi(this);
// XXX Use recent settings instead
- resize(parent.width(), parent.height() * 3 / 4);
+ resize(parent.width() * 2 / 3, parent.height() * 3 / 4);
- ui->statsTreeWidget->addAction(ui->actionCopyToClipboard);
- ui->statsTreeWidget->addAction(ui->actionSaveAs);
- ui->statsTreeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
+ ctx_menu_.addAction(ui->actionCopyToClipboard);
+ ctx_menu_.addAction(ui->actionSaveAs);
QPushButton *button;
button = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ActionRole);
delete ui;
}
+void TapParameterDialog::registerDialog(const QString title, const char *cfg_abbr, register_stat_group_t group, stat_tap_init_cb tap_init_cb, tpdCreator creator)
+{
+ stat_tap_ui ui_info;
+
+ ui_info.group = group;
+ ui_info.title = title.toUtf8().constData();
+ ui_info.cli_string = cfg_abbr;
+ ui_info.tap_init_cb = tap_init_cb;
+ ui_info.nparams = 0; // We'll need this for SCSI SRT
+ ui_info.params = NULL;
+ register_stat_tap_ui(&ui_info, NULL);
+
+ QString cfg_str = cfg_abbr;
+ cfg_str_to_creator_[cfg_str] = creator;
+
+ QAction *tpd_action = new QAction(title, NULL);
+ tpd_action->setData(cfg_str);
+ wsApp->addStatisticsGroupItem(group, tpd_action);
+}
+
+TapParameterDialog *TapParameterDialog::showTapParameterStatistics(QWidget &parent, CaptureFile &cf, const QString cfg_str, const QString arg, void *)
+{
+ if (cfg_str_to_creator_.contains(cfg_str)) {
+ TapParameterDialog *tpd = cfg_str_to_creator_[cfg_str](parent, cfg_str, arg, cf);
+ return tpd;
+ }
+ return NULL;
+}
+
QTreeWidget *TapParameterDialog::statsTreeWidget()
{
return ui->statsTreeWidget;
return ui->displayFilterLineEdit->text().toUtf8().constData();
}
-//QByteArray TapParameterDialog::getTreeAsString(st_format_type format)
-//{
-// // XXX Iterate over the tree and build a QByteArray
-//}
+// This assumes that we're called before signals are connected or show()
+// is called.
+void TapParameterDialog::setDisplayFilter(const QString &filter)
+{
+ ui->displayFilterLineEdit->setText(filter);
+}
+
+void TapParameterDialog::filterActionTriggered()
+{
+ FilterAction *fa = qobject_cast<FilterAction *>(QObject::sender());
+ QString filter_expr = filterExpression();
+
+ if (!fa || filter_expr.isEmpty()) {
+ return;
+ }
+
+ emit filterAction(filter_expr, fa->action(), fa->actionType());
+}
+
+QString TapParameterDialog::itemDataToPlain(QVariant var, int width)
+{
+ QString plain_str;
+ int align_mul = 1;
+
+ switch (var.type()) {
+ case QVariant::String:
+ align_mul = -1;
+ // Fall through
+ case QVariant::Int:
+ case QVariant::UInt:
+ plain_str = var.toString();
+ break;
+ case QVariant::Double:
+ plain_str = QString::number(var.toDouble(), 'f', 6);
+ break;
+ default:
+ break;
+ }
+
+ if (plain_str.length() < width) {
+ plain_str = QString("%1").arg(plain_str, width * align_mul);
+ }
+ return plain_str;
+}
+
+QList<QVariant> TapParameterDialog::treeItemData(QTreeWidgetItem *) const
+{
+ return QList<QVariant>();
+}
+
+const QString plain_sep_ = " ";
+QByteArray TapParameterDialog::getTreeAsString(st_format_type format)
+{
+ QByteArray ba;
+ QTreeWidgetItemIterator it(ui->statsTreeWidget, QTreeWidgetItemIterator::NotHidden);
+
+ QList<int> col_widths;
+ QByteArray footer;
+
+ // Title + header
+ switch (format) {
+ case ST_FORMAT_PLAIN:
+ {
+ QTreeWidgetItemIterator width_it(it);
+ QString plain_header;
+ while (*width_it) {
+ QList<QVariant> tid = treeItemData((*width_it));
+ int col = 0;
+ foreach (QVariant var, tid) {
+ if (col_widths.size() <= col) {
+ col_widths.append(ui->statsTreeWidget->headerItem()->text(col).length());
+ }
+ if (var.type() == QVariant::String) {
+ col_widths[col] = qMax(col_widths[col], itemDataToPlain(var).length());
+ }
+ col++;
+ }
+ ++width_it;
+ }
+ QStringList ph_parts;
+ for (int col = 0; col < ui->statsTreeWidget->columnCount() && col < col_widths.length(); col++) {
+ ph_parts << ui->statsTreeWidget->headerItem()->text(col);
+ }
+ plain_header = ph_parts.join(plain_sep_);
+
+ QByteArray top_separator;
+ top_separator.fill('=', plain_header.length());
+ top_separator.append('\n');
+ QString file_header = QString("%1 - %2:\n").arg(windowSubtitle(), cap_file_.fileName());
+ footer.fill('-', plain_header.length());
+ footer.append('\n');
+ plain_header.append('\n');
+
+ ba.append(top_separator);
+ ba.append(file_header);
+ ba.append(plain_header);
+ ba.append(footer);
+ break;
+ }
+ case ST_FORMAT_CSV:
+ {
+ QString csv_header;
+ QStringList ch_parts;
+ for (int col = 0; col < ui->statsTreeWidget->columnCount(); col++) {
+ ch_parts << QString("\"%1\"").arg(ui->statsTreeWidget->headerItem()->text(col));
+ }
+ csv_header = ch_parts.join(",");
+ csv_header.append('\n');
+ ba.append(csv_header.toUtf8().constData());
+ break;
+ }
+ case ST_FORMAT_XML:
+ {
+ // XXX What's a useful format? This mostly conforms to DocBook.
+ ba.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+ QString title;
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ title = Qt::escape(windowSubtitle());
+#else
+ title = QString(windowSubtitle()).toHtmlEscaped();
+#endif
+ QString xml_header = QString("<table>\n<title>%1</title>\n").arg(title);
+ ba.append(xml_header.toUtf8());
+ ba.append("<thead>\n<row>\n");
+ for (int col = 0; col < ui->statsTreeWidget->columnCount(); col++) {
+ title = ui->statsTreeWidget->headerItem()->text(col);
+#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ title = Qt::escape(title);
+#else
+ title = title.toHtmlEscaped();
+#endif
+ title = QString(" <entry>%1</entry>\n").arg(title);
+ ba.append(title.toUtf8());
+ }
+ ba.append("</row>\n</thead>\n");
+ ba.append("<tbody>\n");
+ footer = "</tbody>\n</table>\n";
+ break;
+ }
+ case ST_FORMAT_YAML:
+ {
+ QString yaml_header;
+ ba.append("---\n");
+ yaml_header = QString("Description: \"%1\"\nFile: \"%2\"\nItems:\n").arg(windowSubtitle()).arg(cap_file_.fileName());
+ ba.append(yaml_header.toUtf8());
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Data
+ while (*it) {
+ QList<QVariant> tid = treeItemData((*it));
+ if (tid.length() < 1) continue;
+
+ if (tid.length() < ui->statsTreeWidget->columnCount()) {
+ // Assume we have a header
+ }
+
+ // Assume var length == columnCount
+ QString line;
+ QStringList parts;
+
+ switch (format) {
+ case ST_FORMAT_PLAIN:
+ {
+ int i = 0;
+ foreach (QVariant var, tid) {
+ parts << itemDataToPlain(var, col_widths[i]);
+ i++;
+ }
+ line = parts.join(plain_sep_);
+ line.append('\n');
+ break;
+ }
+ case ST_FORMAT_CSV:
+ foreach (QVariant var, tid) {
+ if (var.type() == QVariant::String) {
+ parts << QString("\"%1\"").arg(var.toString());
+ } else {
+ parts << var.toString();
+ }
+ }
+ line = parts.join(",");
+ line.append('\n');
+ break;
+ case ST_FORMAT_XML:
+ {
+ line = "<row>\n";
+ foreach (QVariant var, tid) {
+ QString entry;
+ #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
+ entry = Qt::escape(var.toString());
+ #else
+ entry = var.toString().toHtmlEscaped();
+ #endif
+ line.append(QString(" <entry>%1</entry>\n").arg(entry));
+ }
+ line.append("</row>\n");
+ break;
+ }
+ case ST_FORMAT_YAML:
+ {
+ int col = 0;
+ QString indent = "-";
+ foreach (QVariant var, tid) {
+ QString entry;
+ if (var.type() == QVariant::String) {
+ entry = QString("\"%1\"").arg(var.toString());
+ } else {
+ entry = var.toString();
+ }
+ line.append(QString(" %1 %2: %3\n").arg(indent).arg(ui->statsTreeWidget->headerItem()->text(col), entry));
+ indent = " ";
+ col++;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ ba.append(line.toUtf8());
+ ++it;
+ }
+
+ // Footer
+ ba.append(footer); // plain only?
+ return ba;
+}
void TapParameterDialog::drawTreeItems()
{
void TapParameterDialog::showEvent(QShowEvent *)
{
+ if (!ui->displayFilterLineEdit->text().isEmpty()) {
+ QString filter = ui->displayFilterLineEdit->text();
+ emit updateFilter(filter, true);
+ }
fillTree();
}
+void TapParameterDialog::contextMenuEvent(QContextMenuEvent *event)
+{
+ bool enable = filterExpression().length() > 0 ? true : false;
+
+ foreach (QAction *fa, filter_actions_) {
+ fa->setEnabled(enable);
+ }
+
+ ctx_menu_.exec(event->globalPos());
+}
+
void TapParameterDialog::updateWidgets()
{
if (file_closed_) {
void TapParameterDialog::on_applyFilterButton_clicked()
{
+ QString filter = ui->displayFilterLineEdit->text();
+ emit updateFilter(filter, true);
fillTree();
}
QByteArray tree_as_ba = getTreeAsString(format);
// actually save the file
- f = ws_fopen (file_name.toUtf8().constData(),"w");
- last_errno= errno;
+ f = ws_fopen (file_name.toUtf8().constData(), "w");
+ last_errno = errno;
if (f) {
- if (fputs(tree_as_ba.data(), f)!=EOF) {
- success= true;
+ if (fputs(tree_as_ba.data(), f) != EOF) {
+ success = true;
}
- last_errno= errno;
+ last_errno = errno;
fclose(f);
}
if (!success) {
#include <glib.h>
#include <epan/stat_groups.h>
+#include <epan/stat_tap_ui.h>
+#include <QMenu>
+
+#include "filter_action.h"
#include "wireshark_dialog.h"
class QTreeWidget;
+class QTreeWidgetItem;
namespace Ui {
class TapParameterDialog;
}
+class TapParameterDialog;
+typedef TapParameterDialog* (*tpdCreator)(QWidget &parent, const QString cfg_str, const QString arg, CaptureFile &cf);
+
class TapParameterDialog : public WiresharkDialog
{
Q_OBJECT
explicit TapParameterDialog(QWidget &parent, CaptureFile &cf, int help_topic = 0);
~TapParameterDialog();
+ static void registerDialog(const QString title, const char *cfg_abbr, register_stat_group_t group, stat_tap_init_cb tap_init_cb, tpdCreator creator);
+
+ static TapParameterDialog *showTapParameterStatistics(QWidget &parent, CaptureFile &cf, const QString cfg_str, const QString arg, void *);
+ // Needed by static member functions in subclasses. Should we just make
+ // "ui" available instead?
QTreeWidget *statsTreeWidget();
void drawTreeItems();
+signals:
+ void filterAction(QString& filter, FilterAction::Action action, FilterAction::ActionType type);
+ void updateFilter(QString &filter, bool force = false);
+
public slots:
protected:
+ QMenu ctx_menu_;
+ QList<QAction *> filter_actions_;
+
void showEvent(QShowEvent *);
+ void contextMenuEvent(QContextMenuEvent *event);
const char *displayFilter();
+ void setDisplayFilter(const QString &filter);
+
+protected slots:
+ void filterActionTriggered();
private:
Ui::TapParameterDialog *ui;
// Called by the constructor. The subclass should tap packets here.
virtual void fillTree() = 0;
- virtual QByteArray getTreeAsString(st_format_type format) = 0;
+ virtual const QString filterExpression() { return QString(); }
+ QString itemDataToPlain(QVariant var, int width = 0);
+ virtual QList<QVariant> treeItemData(QTreeWidgetItem *) const;
+ virtual QByteArray getTreeAsString(st_format_type format);
private slots:
void updateWidgets();
#include <epan/addr_resolv.h>
#include <epan/prefs.h>
-#include <epan/stat_tap_ui.h>
//#include <epan/dissectors/packet-tcp.h>
# include "ui/win32/console_win32.h"
#endif /* _WIN32 */
+#include <QAction>
#include <QDesktopServices>
#include <QDir>
#include <QEvent>
static char *last_open_dir = NULL;
static bool updated_last_open_dir = FALSE;
static QList<recent_item_status *> recent_items_;
+static QHash<int, QList<QAction *> > statistics_groups_;
QString WiresharkApplication::window_title_separator_ = QString::fromUtf8(" " UTF8_MIDDLE_DOT " ");
emit openStatCommandDialog(menu_path, arg, userdata);
}
+void WiresharkApplication::emitTapParameterSignal(const QString cfg_abbr, const QString arg, void *userdata)
+{
+ emit openTapParameterDialog(cfg_abbr, arg, userdata);
+}
+
+void WiresharkApplication::addStatisticsGroupItem(int group, QAction *sg_action)
+{
+ if (!statistics_groups_.contains(group)) {
+ statistics_groups_[group] = QList<QAction *>();
+ }
+ statistics_groups_[group] << sg_action;
+}
+
+QList<QAction *> WiresharkApplication::statisticsGroupItems(int group)
+{
+ if (!statistics_groups_.contains(group)) {
+ return QList<QAction *>();
+ }
+
+ QList<QAction *> sgi_list = statistics_groups_[group];
+ std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan);
+ return sgi_list;
+}
+
#ifdef HAVE_LIBPCAP
static void
struct _e_prefs;
+class QAction;
class QSocketNotifier;
// Recent items:
void registerUpdate(register_action_e action, const char *message);
void emitAppSignal(AppSignal signal);
void emitStatCommandSignal(const QString &menu_path, const char *arg, void *userdata);
+ void emitTapParameterSignal(const QString cfg_abbr, const QString arg, void *userdata);
+ // Map a register_stat_group_t to a list of stat_tap_ui.title
+ void addStatisticsGroupItem(int group, QAction *sg_action);
+ QList<QAction *>statisticsGroupItems(int group);
+
void allSystemsGo();
void refreshLocalInterfaces();
struct _e_prefs * readConfigurationFiles(char **gdp_path, char **dp_path);
void fieldsChanged();
void openStatCommandDialog(const QString &menu_path, const char *arg, void *userdata);
+ void openTapParameterDialog(const QString cfg_str, const QString arg, void *userdata);
public slots:
void clearRecentItems();
protected:
virtual void keyPressEvent(QKeyEvent *event) { QDialog::keyPressEvent(event); }
void setWindowSubtitle(const QString &subtitle);
+ const QString &windowSubtitle() { return subtitle_; }
virtual void updateWidgets();
CaptureFile &cap_file_;
virtual void captureFileClosing();
private:
- const QString &windowSubtitle() { return subtitle_; }
void setWindowTitleFromSubtitle();
QString subtitle_;
--- /dev/null
+/* service_response_time.c
+ * Copied from ui/gtk/service_response_time_table.h, 2003 Ronnie Sahlberg
+ * Helper routines and structs common to all service response time statistics
+ * taps.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "service_response_time.h"
+
+extern const char*
+service_response_time_get_column_name (int index)
+{
+ static const char *default_titles[] = { "Index", "Procedure", "Calls", "Min SRT (s)", "Max SRT (s)", "Avg SRT (s)", "Sum SRT (s)" };
+
+ if (index < 0 || index >= NUM_SRT_COLUMNS) return "(Unknown)";
+ return default_titles[index];
+}
+
+/*
+ * 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:
+ */
--- /dev/null
+/* service_response_time.h
+ * Copied from ui/gtk/service_response_time_table.h, 2003 Ronnie Sahlberg
+ * Helper routines and structs common to all service response time statistics
+ * taps.
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/** @file
+ * Helper routines common to all service response time statistics taps.
+ */
+
+#ifndef __SRT_STATS_H__
+#define __SRT_STATS_H__
+
+#include <epan/timestats.h>
+#include <epan/srt_table.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+enum
+{
+ SRT_COLUMN_INDEX,
+ SRT_COLUMN_PROCEDURE,
+ SRT_COLUMN_CALLS,
+ SRT_COLUMN_MIN,
+ SRT_COLUMN_MAX,
+ SRT_COLUMN_AVG,
+ SRT_COLUMN_SUM,
+ NUM_SRT_COLUMNS
+};
+
+/** returns the column name for a given column index */
+extern const char* service_response_time_get_column_name(int index);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __SRT_STATS_H__ */
#include "ui/qt/conversation_dialog.h"
#include "ui/qt/endpoint_dialog.h"
+#include "epan/srt_table.h"
+#include "ui/qt/service_response_time_dialog.h"
+
#if defined(HAVE_LIBPCAP) || defined(HAVE_EXTCAP)
capture_options global_capture_opts;
#endif
register_all_tap_listeners();
conversation_table_set_gui_info(init_conversation_table);
hostlist_table_set_gui_info(init_endpoint_table);
+ srt_table_iterate_tables(register_service_response_tables, NULL);
if (ex_opt_count("read_format") > 0) {
in_file_type = open_info_name_to_type(ex_opt_get_next("read_format"));
////////
#endif /* HAVE_LIBPCAP */
-// w->setEnabled(true);
wsApp->allSystemsGo();
g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_INFO, "Wireshark is up and ready to go");
SimpleDialog::displayQueuedMessages(main_w);
- /* user could specify filename, or display filter, or both */
+ /* User could specify filename, or display filter, or both */
if (!cf_name.isEmpty()) {
-
- /* Open stat windows; we do so after creating the main window,
- to avoid Qt warnings, and after successfully opening the
- capture file, so we know we have something to compute stats
- on, and after registering all dissectors, so that MATE will
- have registered its field array and we can have a tap filter
- with one of MATE's late-registered fields as part of the
- filter. */
- start_requested_stats();
-
- // XXX The GTK+ UI does error checking here.
- main_w->openCaptureFile(cf_name, read_filter, in_file_type);
- if (!dfilter.isEmpty())
- main_w->filterPackets(dfilter, false);
- if(go_to_packet != 0) {
- /* Jump to the specified frame number, kept for backward
- compatibility. */
- cf_goto_frame(CaptureFile::globalCapFile(), go_to_packet);
+ if (main_w->openCaptureFile(cf_name, read_filter, in_file_type)) {
+ if (!dfilter.isEmpty())
+ main_w->filterPackets(dfilter, false);
+
+ /* Open stat windows; we do so after creating the main window,
+ to avoid Qt warnings, and after successfully opening the
+ capture file, so we know we have something to compute stats
+ on, and after registering all dissectors, so that MATE will
+ have registered its field array and we can have a tap filter
+ with one of MATE's late-registered fields as part of the
+ filter. */
+ start_requested_stats();
+
+ if(go_to_packet != 0) {
+ /* Jump to the specified frame number, kept for backward
+ compatibility. */
+ cf_goto_frame(CaptureFile::globalCapFile(), go_to_packet);
+ }
}
}
#ifdef HAVE_LIBPCAP