From fdb85029fd9b22b221f7123905f1bad66c04ce91 Mon Sep 17 00:00:00 2001 From: Gerald Combs Date: Fri, 5 Jun 2015 13:17:13 -0700 Subject: [PATCH] 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 Reviewed-by: Michael Mann --- doc/tshark.pod | 6 + doc/wireshark.pod.template | 12 + epan/srt_table.h | 7 +- epan/stat_groups.h | 4 +- epan/stat_tap_ui.h | 3 +- epan/stats_tree.c | 3 +- epan/stats_tree.h | 8 + epan/timestats.h | 14 +- ui/CMakeLists.txt | 1 + ui/Makefile.common | 2 + ui/gtk/service_response_time_table.c | 32 +- ui/gtk/service_response_time_table.h | 4 +- ui/qt/CMakeLists.txt | 3 + ui/qt/Makefile.common | 2 + ui/qt/Wireshark.pro | 3 + ui/qt/expert_info_dialog.cpp | 3 +- ui/qt/expert_info_dialog.h | 6 +- ui/qt/filter_action.cpp | 15 +- ui/qt/filter_action.h | 2 +- ui/qt/main_window.cpp | 27 ++ ui/qt/main_window.h | 21 +- ui/qt/main_window.ui | 26 +- ui/qt/main_window_slots.cpp | 34 +- ui/qt/qt_ui_utils.cpp | 5 + ui/qt/qt_ui_utils.h | 10 + ui/qt/service_response_time_dialog.cpp | 411 +++++++++++++++++++++++++ ui/qt/service_response_time_dialog.h | 88 ++++++ ui/qt/stats_tree_dialog.cpp | 24 +- ui/qt/stats_tree_dialog.h | 2 +- ui/qt/tap_parameter_dialog.cpp | 320 ++++++++++++++++++- ui/qt/tap_parameter_dialog.h | 30 +- ui/qt/traffic_table_dialog.cpp | 1 - ui/qt/wireshark_application.cpp | 26 ++ ui/qt/wireshark_application.h | 7 + ui/qt/wireshark_dialog.h | 2 +- ui/service_response_time.c | 47 +++ ui/service_response_time.h | 58 ++++ wireshark-qt.cpp | 39 +-- 38 files changed, 1212 insertions(+), 96 deletions(-) create mode 100644 ui/qt/service_response_time_dialog.cpp create mode 100644 ui/qt/service_response_time_dialog.h create mode 100644 ui/service_response_time.c create mode 100644 ui/service_response_time.h diff --git a/doc/tshark.pod b/doc/tshark.pod index f50211987e..8278425246 100644 --- a/doc/tshark.pod +++ b/doc/tshark.pod @@ -860,6 +860,8 @@ Display all possible values for B<-z>. =item B<-z> afp,srt[,I] +Show Apple Filing Protocol service response time statistics. + =item B<-z> camel,srt =item B<-z> compare,I,I,I,I,I[,I] @@ -915,6 +917,10 @@ on those calls that match that filter. Example: S> will collect SAMR SRT statistics for a specific host. +=item B<-z> bootp,stat[,I] + +Show DHCP (BOOTP) statistics. + =item B<-z> diameter,avp[,I,I,I,I<...>] This option enables extraction of most important diameter fields from large capture files. diff --git a/doc/wireshark.pod.template b/doc/wireshark.pod.template index b3ba46386c..ee398dd477 100644 --- a/doc/wireshark.pod.template +++ b/doc/wireshark.pod.template @@ -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] + +Show Apple Filing Protocol service response time statistics. + =item B<-z> conv,I[,I] 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> will collect SAMR SRT statistics for a specific host. +=item B<-z> bootp,stat[,I] + +Show DHCP (BOOTP) statistics. + =item B<-z> fc,srt[,I] Collect call/reply SRT (Service Response Time) data for FC. Data collected diff --git a/epan/srt_table.h b/epan/srt_table.h index 3b8905218b..2910e86ce3 100644 --- a/epan/srt_table.h +++ b/epan/srt_table.h @@ -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); diff --git a/epan/stat_groups.h b/epan/stat_groups.h index 0afdc57f2a..6f603325fc 100644 --- a/epan/stat_groups.h +++ b/epan/stat_groups.h @@ -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?" diff --git a/epan/stat_tap_ui.h b/epan/stat_tap_ui.h index 1bdae9d50c..3877a5aa9e 100644 --- a/epan/stat_tap_ui.h +++ b/epan/stat_tap_ui.h @@ -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; diff --git a/epan/stats_tree.c b/epan/stats_tree.c index e3600a38b5..aaad74e344 100644 --- a/epan/stats_tree.c +++ b/epan/stats_tree.c @@ -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("\n"); + s = g_string_new("\n"); break; case ST_FORMAT_CSV: s = g_string_new("\"level\",\"parent\","); diff --git a/epan/stats_tree.h b/epan/stats_tree.h index d732c390db..ed88fbaa03 100644 --- a/epan/stats_tree.h +++ b/epan/stats_tree.h @@ -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 */ /* diff --git a/epan/timestats.h b/epan/timestats.h index 884fb5b94f..37e45ebe56 100644 --- a/epan/timestats.h +++ b/epan/timestats.h @@ -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 #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__ */ diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 123d8ca241..f611c7da16 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -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 diff --git a/ui/Makefile.common b/ui/Makefile.common index 25cbdc63eb..3f215d112d 100644 --- a/ui/Makefile.common +++ b/ui/Makefile.common @@ -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 \ diff --git a/ui/gtk/service_response_time_table.c b/ui/gtk/service_response_time_table.c index c9de7b30ff..3284c06b2c 100644 --- a/ui/gtk/service_response_time_table.c +++ b/ui/gtk/service_response_time_table.c @@ -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); diff --git a/ui/gtk/service_response_time_table.h b/ui/gtk/service_response_time_table.h index 3b8e5915a9..c30ee72fec 100644 --- a/ui/gtk/service_response_time_table.h +++ b/ui/gtk/service_response_time_table.h @@ -27,7 +27,7 @@ #include #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 */ diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index b317bfb3a9..dbd57ad3c0 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -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 diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index ee46e60bfa..9536d983ca 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -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 \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index 92e3ae19eb..f9d6a2b689 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -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 \ diff --git a/ui/qt/expert_info_dialog.cpp b/ui/qt/expert_info_dialog.cpp index f3359ba133..2d7ecfc96a 100644 --- a/ui/qt/expert_info_dialog.cpp +++ b/ui/qt/expert_info_dialog.cpp @@ -24,6 +24,7 @@ #include "file.h" +#include #include #include #include @@ -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(eid_ptr); expert_info_t *expert_info = (expert_info_t *) data; diff --git a/ui/qt/expert_info_dialog.h b/ui/qt/expert_info_dialog.h index 13dcd2c084..802b52dcde 100644 --- a/ui/qt/expert_info_dialog.h +++ b/ui/qt/expert_info_dialog.h @@ -26,15 +26,15 @@ #include -#include "epan/epan_dissect.h" - #include "filter_action.h" #include "wireshark_dialog.h" #include #include +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: diff --git a/ui/qt/filter_action.cpp b/ui/qt/filter_action.cpp index e720b0e1b9..2c00a5ab7f 100644 --- a/ui/qt/filter_action.cpp +++ b/ui/qt/filter_action.cpp @@ -91,7 +91,7 @@ const QString FilterAction::actionName(Action action) { } -const QList FilterAction::actionTypes() +const QList FilterAction::actionTypes(Action filter_action) { static const QList action_types_ = QList() << ActionTypePlain @@ -100,6 +100,19 @@ const QList FilterAction::actionTypes() << ActionTypeOr << ActionTypeAndNot << ActionTypeOrNot; + + static const QList simple_action_types_ = QList() + << ActionTypePlain + << ActionTypeNot; + + switch (filter_action) { + case ActionFind: + case ActionColorize: + return simple_action_types_; + default: + break; + } + return action_types_; } diff --git a/ui/qt/filter_action.h b/ui/qt/filter_action.h index c9c2086273..5fdd09e4e9 100644 --- a/ui/qt/filter_action.h +++ b/ui/qt/filter_action.h @@ -75,7 +75,7 @@ public: static const QString actionName(Action action); ActionType actionType() { return type_; } - static const QList actionTypes(); + static const QList actionTypes(Action filter_action = ActionApply); static const QString actionTypeName(ActionType type); ActionDirection actionDirection() { return direction_; } diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index 7af4370ac1..737aadc511 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -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. + QListunsorted_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 + QListsg_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; diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index dbb85847aa..ea53ad115a 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -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__"). // // 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(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 60b53fe48d..50cef78629 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -438,14 +438,20 @@ + + + Service Response Time + + - + + - + @@ -1675,7 +1681,7 @@ HTTP load distribution - + Packet Lengths @@ -2349,6 +2355,20 @@ Add an expression to the display filter. + + + REGISTER_STAT_GROUP_UNSORTED + + + Start of "REGISTER_STAT_GROUP_UNSORTED" + + + false + + + QAction::NoRole + + diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index d2f960af6e..59f290ed48 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -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(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"); } diff --git a/ui/qt/qt_ui_utils.cpp b/ui/qt/qt_ui_utils.cpp index de59926253..c1841c9f80 100644 --- a/ui/qt/qt_ui_utils.cpp +++ b/ui/qt/qt_ui_utils.cpp @@ -35,6 +35,7 @@ #include +#include #include /* 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 * diff --git a/ui/qt/qt_ui_utils.h b/ui/qt/qt_ui_utils.h index 175f5daaef..102f599fbf 100644 --- a/ui/qt/qt_ui_utils.h +++ b/ui/qt/qt_ui_utils.h @@ -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 diff --git a/ui/qt/service_response_time_dialog.cpp b/ui/qt/service_response_time_dialog.cpp new file mode 100644 index 0000000000..33f0323f70 --- /dev/null +++ b/ui/qt/service_response_time_dialog.cpp @@ -0,0 +1,411 @@ +/* service_response_time_dialog.cpp + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 +#include + +#include + +#include "wireshark_application.h" + +#include +#include +#include + +// To do: + +static QHash 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(&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 rowData() { + double avg_time = 0.0; + if (procedure_->stats.num) { + avg_time = nstime_to_sec(&procedure_->stats.min) / procedure_->stats.num; + } + return QList() << 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 rowData() { + return QList() << srt_table_->name; + } + const QString filterField() { return srt_table_->filter_string; } + +private: + const srt_stat_table *srt_table_; +}; + + +ServiceResponseTimeDialog::ServiceResponseTimeDialog(QWidget &parent, CaptureFile &cf, register_srt *srt, const QString filter, int help_topic) : + TapParameterDialog(parent, cf, help_topic), + srt_(srt) +{ + QString subtitle = QString("%1 Service Response Time Statistics") + .arg(proto_get_protocol_short_name(find_protocol_by_id(get_srt_proto_id(srt)))); + setWindowSubtitle(subtitle); + + // 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(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(srtd->user_data); + if (!srt_dlg || !srt_dlg->statsTreeWidget()) return; + + QTreeWidgetItemIterator it(srt_dlg->statsTreeWidget()); + while (*it) { + if ((*it)->type() == srt_row_type_) { + SrtRowTreeWidgetItem *srtr_ti = static_cast((*it)); + srtr_ti->draw(); + } + ++it; + } + + for (int i = 0; i < srt_dlg->statsTreeWidget()->columnCount() - 1; i++) { + srt_dlg->statsTreeWidget()->resizeColumnToContents(i); + } +} + +void ServiceResponseTimeDialog::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 ServiceResponseTimeDialog::treeItemData(QTreeWidgetItem *ti) const +{ + QList tid; + if (ti->type() == srt_table_type_) { + SrtTableTreeWidgetItem *srtt_ti = static_cast(ti); + if (srtt_ti) { + tid << srtt_ti->rowData(); + } + } else if (ti->type() == srt_row_type_) { + SrtRowTreeWidgetItem *srtr_ti = static_cast(ti); + if (srtr_ti) { + tid << srtr_ti->rowData(); + } + } + return tid; +} + +const QString ServiceResponseTimeDialog::filterExpression() +{ + QString filter_expr; + if (statsTreeWidget()->selectedItems().count() > 0) { + QTreeWidgetItem *ti = statsTreeWidget()->selectedItems()[0]; + if (ti->type() == srt_row_type_) { + SrtTableTreeWidgetItem *srtt_ti = static_cast(ti->parent()); + 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(ti->parent()); + } else { + srtt_ti = static_cast(ti); + } + if (srtt_ti) { + procedure_title = srtt_ti->columnTitle(); + } + } + statsTreeWidget()->headerItem()->setText(SRT_COLUMN_PROCEDURE, procedure_title); +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/service_response_time_dialog.h b/ui/qt/service_response_time_dialog.h new file mode 100644 index 0000000000..b7c7569bd3 --- /dev/null +++ b/ui/qt/service_response_time_dialog.h @@ -0,0 +1,88 @@ +/* service_response_time_dialog.h + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 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: + */ diff --git a/ui/qt/stats_tree_dialog.cpp b/ui/qt/stats_tree_dialog.cpp index 2152058bd3..6a06632a41 100644 --- a/ui/qt/stats_tree_dialog.cpp +++ b/ui/qt/stats_tree_dialog.cpp @@ -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(); @@ -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; countnum_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; countnum_columns; count++) { - headerLabels.push_back(stats_tree_get_column_name(count)); - } statsTreeWidget()->setSortingEnabled(false); error_string = register_tap_listener(st_cfg_->tapname, diff --git a/ui/qt/stats_tree_dialog.h b/ui/qt/stats_tree_dialog.h index 59cf812a15..0e810eb23c 100644 --- a/ui/qt/stats_tree_dialog.h +++ b/ui/qt/stats_tree_dialog.h @@ -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); diff --git a/ui/qt/tap_parameter_dialog.cpp b/ui/qt/tap_parameter_dialog.cpp index 13027a9e67..b5038d9a13 100644 --- a/ui/qt/tap_parameter_dialog.cpp +++ b/ui/qt/tap_parameter_dialog.cpp @@ -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 +#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 +#include #include #include +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +// Qt::escape +#include +#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 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(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 TapParameterDialog::treeItemData(QTreeWidgetItem *) const +{ + return QList(); +} + +const QString plain_sep_ = " "; +QByteArray TapParameterDialog::getTreeAsString(st_format_type format) +{ + QByteArray ba; + QTreeWidgetItemIterator it(ui->statsTreeWidget, QTreeWidgetItemIterator::NotHidden); + + QList col_widths; + QByteArray footer; + + // Title + header + switch (format) { + case ST_FORMAT_PLAIN: + { + QTreeWidgetItemIterator width_it(it); + QString plain_header; + while (*width_it) { + QList 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("\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("\n%1\n").arg(title); + ba.append(xml_header.toUtf8()); + ba.append("\n\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(" %1\n").arg(title); + ba.append(title.toUtf8()); + } + ba.append("\n\n"); + ba.append("\n"); + footer = "\n
\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 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 = "\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(" %1\n").arg(entry)); + } + line.append("\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) { diff --git a/ui/qt/tap_parameter_dialog.h b/ui/qt/tap_parameter_dialog.h index 857c3f1006..8203fcbd7f 100644 --- a/ui/qt/tap_parameter_dialog.h +++ b/ui/qt/tap_parameter_dialog.h @@ -27,15 +27,23 @@ #include #include +#include +#include + +#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 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 treeItemData(QTreeWidgetItem *) const; + virtual QByteArray getTreeAsString(st_format_type format); private slots: void updateWidgets(); diff --git a/ui/qt/traffic_table_dialog.cpp b/ui/qt/traffic_table_dialog.cpp index 2d58a14db4..908765df14 100644 --- a/ui/qt/traffic_table_dialog.cpp +++ b/ui/qt/traffic_table_dialog.cpp @@ -24,7 +24,6 @@ #include #include -#include //#include diff --git a/ui/qt/wireshark_application.cpp b/ui/qt/wireshark_application.cpp index 51627c24eb..b822cd3fc4 100644 --- a/ui/qt/wireshark_application.cpp +++ b/ui/qt/wireshark_application.cpp @@ -62,6 +62,7 @@ # include "ui/win32/console_win32.h" #endif /* _WIN32 */ +#include #include #include #include @@ -87,6 +88,7 @@ WiresharkApplication *wsApp = NULL; static char *last_open_dir = NULL; static bool updated_last_open_dir = FALSE; static QList recent_items_; +static QHash > 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(); + } + statistics_groups_[group] << sg_action; +} + +QList WiresharkApplication::statisticsGroupItems(int group) +{ + if (!statistics_groups_.contains(group)) { + return QList(); + } + + QList sgi_list = statistics_groups_[group]; + std::sort(sgi_list.begin(), sgi_list.end(), qActionLessThan); + return sgi_list; +} + #ifdef HAVE_LIBPCAP static void diff --git a/ui/qt/wireshark_application.h b/ui/qt/wireshark_application.h index 2255fd5ace..5cfd5ee032 100644 --- a/ui/qt/wireshark_application.h +++ b/ui/qt/wireshark_application.h @@ -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); + QListstatisticsGroupItems(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(); diff --git a/ui/qt/wireshark_dialog.h b/ui/qt/wireshark_dialog.h index 9c5ed7d4f5..3e7e1e4334 100644 --- a/ui/qt/wireshark_dialog.h +++ b/ui/qt/wireshark_dialog.h @@ -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_; diff --git a/ui/service_response_time.c b/ui/service_response_time.c new file mode 100644 index 0000000000..528675bb75 --- /dev/null +++ b/ui/service_response_time.c @@ -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 + * 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: + */ diff --git a/ui/service_response_time.h b/ui/service_response_time.h new file mode 100644 index 0000000000..dcad54539e --- /dev/null +++ b/ui/service_response_time.h @@ -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 + * 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 +#include + +#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__ */ diff --git a/wireshark-qt.cpp b/wireshark-qt.cpp index 4640944a8c..2abae5dc95 100644 --- a/wireshark-qt.cpp +++ b/wireshark-qt.cpp @@ -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