Add ServiceResponseTimeDialog.

Add ServiceResponseTimeDialog as a subclass of TapParameterDialog,
similar to StatsTreeDialog. Add initial plumbing for statistics menu
items and command line invocation.

Don't append "..." to menu item names. Don't add menu icons. In each
case this avoids repetitive UI clutter.

Change-Id: I463b95c93090160bb81d2e80b16aad389dc0bd6c
Reviewed-on: https://code.wireshark.org/review/8864
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Reviewed-by: Michael Mann <mmann78@netscape.net>
This commit is contained in:
Gerald Combs 2015-06-05 13:17:13 -07:00 committed by Michael Mann
parent 7fdc534cf9
commit fdb85029fd
38 changed files with 1212 additions and 96 deletions

View File

@ -860,6 +860,8 @@ 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> camel,srt
=item B<-z> compare,I<start>,I<stop>,I<ttl[0|1]>,I<order[0|1]>,I<variance>[,I<filter>]
@ -915,6 +917,10 @@ on those calls that match that 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.

View File

@ -643,6 +643,14 @@ Currently implemented statistics are:
=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
@ -687,6 +695,10 @@ on those calls that match that 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> fc,srt[,I<filter>]
Collect call/reply SRT (Service Response Time) data for FC. Data collected

View File

@ -49,12 +49,11 @@ typedef struct _srt_stat_table {
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);

View File

@ -32,8 +32,8 @@ extern "C" {
*/
/*
* 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?"

View File

@ -57,11 +57,12 @@ typedef struct _tap_param {
/*
* 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;

View File

@ -364,6 +364,7 @@ stats_tree_packet(void *p, packet_info *pinfo, epan_dissect_t *edt, const void *
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);
}
@ -1106,7 +1107,7 @@ stats_tree_format_as_str(const stats_tree* st, st_format_type format_type,
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\",");

View File

@ -31,6 +31,10 @@
#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 */
@ -233,6 +237,10 @@ WS_DLL_PUBLIC int stats_tree_manip_node(manip_node_mode mode,
#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 */
/*

View File

@ -21,13 +21,17 @@
* 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 */
@ -49,4 +53,8 @@ WS_DLL_PUBLIC void time_stat_update(timestat_t *stats, const nstime_t *delta, pa
WS_DLL_PUBLIC gdouble get_average(const nstime_t *sum, guint32 num);
#endif
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __TIMESTATS_H__ */

View File

@ -45,6 +45,7 @@ set(COMMON_UI_SRC
proto_hier_stats.c
recent.c
rtp_stream.c
service_response_time.c
software_update.c
ssl_key_export.c
tap_export_pdu.c

View File

@ -66,6 +66,7 @@ WIRESHARK_UI_SRC = \
proto_hier_stats.c \
recent.c \
rtp_stream.c \
service_response_time.c \
software_update.c \
ssl_key_export.c \
tap_export_pdu.c \
@ -107,6 +108,7 @@ noinst_HEADERS = \
recent_utils.h \
rtp_analysis.h \
rtp_stream.h \
service_response_time.h \
simple_dialog.h \
software_update.h \
ssl_key_export.h \

View File

@ -86,7 +86,7 @@ srt_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callba
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;
@ -471,8 +471,6 @@ init_gtk_srt_table(srt_stat_table* rst, void* gui_data)
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);
@ -493,7 +491,7 @@ init_gtk_srt_table(srt_stat_table* rst, void* gui_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 */
@ -510,38 +508,38 @@ init_gtk_srt_table(srt_stat_table* rst, void* gui_data)
/* 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);
@ -713,7 +711,7 @@ init_srt_tables(register_srt_t* srt, const char *filter)
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);
@ -737,7 +735,7 @@ init_srt_tables(register_srt_t* srt, const char *filter)
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);

View File

@ -27,7 +27,7 @@
#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
@ -39,7 +39,7 @@
#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 */

View File

@ -23,6 +23,7 @@
# 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
@ -106,6 +107,7 @@ set(WIRESHARK_QT_HEADERS
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
@ -223,6 +225,7 @@ set(WIRESHARK_QT_SRC
search_frame.cpp
sequence_diagram.cpp
sequence_dialog.cpp
service_response_time_dialog.cpp
simple_dialog.cpp
splash_overlay.cpp
sparkline_delegate.cpp

View File

@ -211,6 +211,7 @@ MOC_HDRS = \
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 \
@ -432,6 +433,7 @@ WIRESHARK_QT_SRC = \
search_frame.cpp \
sequence_diagram.cpp \
sequence_dialog.cpp \
service_response_time_dialog.cpp \
simple_dialog.cpp \
sparkline_delegate.cpp \
splash_overlay.cpp \

View File

@ -326,6 +326,7 @@ HEADERS += $$HEADERS_WS_C \
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 \
@ -612,6 +613,7 @@ HEADERS += \
simple_dialog.h \
sparkline_delegate.h \
syntax_line_edit.h \
tap_parameter_dialog.h \
time_shift_dialog.h \
wireshark_application.h \
wireshark_dialog.h \
@ -706,6 +708,7 @@ SOURCES += \
search_frame.cpp \
sequence_diagram.cpp \
sequence_dialog.cpp \
service_response_time_dialog.cpp \
simple_dialog.cpp \
sparkline_delegate.cpp \
splash_overlay.cpp \

View File

@ -24,6 +24,7 @@
#include "file.h"
#include <epan/epan_dissect.h>
#include <epan/expert.h>
#include <epan/stat_tap_ui.h>
#include <epan/tap.h>
@ -338,7 +339,7 @@ void ExpertInfoDialog::tapReset(void *eid_ptr)
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;

View File

@ -26,15 +26,15 @@
#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;
@ -83,7 +83,7 @@ private:
// 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:

View File

@ -91,7 +91,7 @@ const QString FilterAction::actionName(Action action) {
}
const QList<FilterAction::ActionType> FilterAction::actionTypes()
const QList<FilterAction::ActionType> FilterAction::actionTypes(Action filter_action)
{
static const QList<ActionType> action_types_ = QList<ActionType>()
<< ActionTypePlain
@ -100,6 +100,19 @@ const QList<FilterAction::ActionType> FilterAction::actionTypes()
<< 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_;
}

View File

@ -75,7 +75,7 @@ public:
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_; }

View File

@ -213,6 +213,8 @@ MainWindow::MainWindow(QWidget *parent) :
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
@ -235,6 +237,7 @@ MainWindow::MainWindow(QWidget *parent) :
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()));
@ -1966,6 +1969,30 @@ void MainWindow::setForCaptureInProgress(gboolean capture_in_progress)
#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;

View File

@ -199,7 +199,15 @@ signals:
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();
@ -251,6 +259,7 @@ private slots:
void showColumnEditor(int column);
void showPreferenceEditor(); // module_t *, pref *
void addStatsPluginsToMenu();
void addStatisticsMenus();
void addExternalMenus();
void startInterfaceCapture(bool valid);
@ -271,6 +280,14 @@ private slots:
*/
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
@ -472,7 +489,7 @@ private slots:
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();

View File

@ -438,14 +438,20 @@
<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"/>
@ -1675,7 +1681,7 @@
<string>HTTP load distribution</string>
</property>
</action>
<action name="actionStatisticsPacketLen">
<action name="actionStatisticsPacketLengths">
<property name="text">
<string>Packet Lengths</string>
</property>
@ -2349,6 +2355,20 @@
<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 &quot;REGISTER_STAT_GROUP_UNSORTED&quot;</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>

View File

@ -110,6 +110,7 @@
#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"
@ -131,7 +132,7 @@
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;
@ -170,12 +171,12 @@ void MainWindow::openCaptureFile(QString& cf_path, QString& read_filter, unsigne
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)) {
@ -228,7 +229,7 @@ void MainWindow::openCaptureFile(QString& cf_path, QString& read_filter, unsigne
string and return (without changing the last containing
directory). */
capture_file_.setCapFile(NULL);
return;
return false;
}
break;
}
@ -236,6 +237,8 @@ void MainWindow::openCaptureFile(QString& cf_path, QString& read_filter, unsigne
wsApp->setLastOpenDir(get_dirname(cf_path.toUtf8().data()));
main_ui_->statusBar->showExpert();
return true;
}
void MainWindow::filterPackets(QString& new_filter, bool force)
@ -1528,6 +1531,27 @@ void MainWindow::openStatCommandDialog(const QString &menu_path, const char *arg
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()
@ -2710,7 +2734,7 @@ void MainWindow::on_actionStatisticsHTTPLoadDistribution_triggered()
openStatisticsTreeDialog("http_srv");
}
void MainWindow::on_actionStatisticsPacketLen_triggered()
void MainWindow::on_actionStatisticsPacketLengths_triggered()
{
openStatisticsTreeDialog("plen");
}

View File

@ -35,6 +35,7 @@
#include <wsutil/str_util.h>
#include <QAction>
#include <QFontDatabase>
/* Make the format_size_flags_e enum usable in C++ */
@ -150,6 +151,10 @@ void smooth_font_size(QFont &font) {
}
}
bool qActionLessThan(const QAction * a1, const QAction * a2) {
return a1->text().compare(a2->text()) < 0;
}
/*
* Editor modelines
*

View File

@ -148,6 +148,16 @@ const QString file_size_to_qstring(const gint64 size);
*/
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

View File

@ -0,0 +1,411 @@
/* 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:
*/

View File

@ -0,0 +1,88 @@
/* 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:
*/

View File

@ -36,10 +36,16 @@ const int item_col_ = 0;
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 *>();
@ -56,7 +62,6 @@ public:
}
};
StatsTreeDialog::StatsTreeDialog(QWidget &parent, CaptureFile &cf, const char *cfg_abbr) :
TapParameterDialog(parent, cf),
st_(NULL),
@ -109,9 +114,7 @@ void StatsTreeDialog::fillTree()
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".
@ -126,16 +129,13 @@ void StatsTreeDialog::fillTree()
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,

View File

@ -39,7 +39,7 @@ class StatsTreeDialog : public TapParameterDialog
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);

View File

@ -26,7 +26,11 @@
* - 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"
@ -34,6 +38,8 @@
#include <errno.h>
#include "epan/stat_tap_ui.h"
#include "ui/last_open_dir.h"
#include "ui/utf8_entities.h"
@ -42,19 +48,29 @@
#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),
@ -63,11 +79,10 @@ TapParameterDialog::TapParameterDialog(QWidget &parent, CaptureFile &cf, int hel
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);
@ -86,6 +101,35 @@ TapParameterDialog::~TapParameterDialog()
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;
@ -96,10 +140,237 @@ const char *TapParameterDialog::displayFilter()
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()
{
@ -114,9 +385,24 @@ 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_) {
@ -127,6 +413,8 @@ void TapParameterDialog::updateWidgets()
void TapParameterDialog::on_applyFilterButton_clicked()
{
QString filter = ui->displayFilterLineEdit->text();
emit updateFilter(filter, true);
fillTree();
}
@ -182,13 +470,13 @@ void TapParameterDialog::on_actionSaveAs_triggered()
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) {

View File

@ -27,15 +27,23 @@
#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
@ -44,14 +52,31 @@ public:
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;
@ -59,7 +84,10 @@ private:
// 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();

View File

@ -24,7 +24,6 @@
#include <epan/addr_resolv.h>
#include <epan/prefs.h>
#include <epan/stat_tap_ui.h>
//#include <epan/dissectors/packet-tcp.h>

View File

@ -62,6 +62,7 @@
# include "ui/win32/console_win32.h"
#endif /* _WIN32 */
#include <QAction>
#include <QDesktopServices>
#include <QDir>
#include <QEvent>
@ -87,6 +88,7 @@ WiresharkApplication *wsApp = NULL;
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 " ");
@ -563,6 +565,30 @@ void WiresharkApplication::emitStatCommandSignal(const QString &menu_path, const
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

View File

@ -39,6 +39,7 @@
struct _e_prefs;
class QAction;
class QSocketNotifier;
// Recent items:
@ -72,6 +73,11 @@ public:
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);
@ -129,6 +135,7 @@ signals:
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();

View File

@ -41,6 +41,7 @@ public slots:
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_;
@ -50,7 +51,6 @@ protected slots:
virtual void captureFileClosing();
private:
const QString &windowSubtitle() { return subtitle_; }
void setWindowTitleFromSubtitle();
QString subtitle_;

View File

@ -0,0 +1,47 @@
/* 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:
*/

View File

@ -0,0 +1,58 @@
/* 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__ */

View File

@ -116,6 +116,9 @@
#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
@ -837,6 +840,7 @@ DIAG_ON(cast-qual)
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"));
@ -1324,31 +1328,30 @@ DIAG_ON(cast-qual)
////////
#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()) {
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();
/* 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(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