diff options
author | Anders Broman <anders.broman@ericsson.com> | 2009-10-06 07:44:48 +0000 |
---|---|---|
committer | Anders Broman <anders.broman@ericsson.com> | 2009-10-06 07:44:48 +0000 |
commit | b42fda0fd262a61b615a2171b67a818814bbc040 (patch) | |
tree | 8827fec7615073685f7310599e6b5f669eb427a9 /gtk/conversations_table.c | |
parent | 999ab129452a3126cd1f26d3afbd2e81ec11ce9d (diff) |
From : Didier Gautheron
gtktreeview for conversations:
- use a garray rather than g_realloc memory for conversations list.
- use a hashtable for finding conversation rather than a linear search.
Notes:
- sorting is too slow, I disabled it.
- the dialog box is really a big problem because it's also empty the idle event queue, slow down a lot.
- Test on a 1.5M packets with hundred thousands conversations capture, slow but bearable.
svn path=/trunk/; revision=30353
Diffstat (limited to 'gtk/conversations_table.c')
-rw-r--r-- | gtk/conversations_table.c | 1022 |
1 files changed, 605 insertions, 417 deletions
diff --git a/gtk/conversations_table.c b/gtk/conversations_table.c index 265f6237c9..d40f71247a 100644 --- a/gtk/conversations_table.c +++ b/gtk/conversations_table.c @@ -1,5 +1,3 @@ -/* mem leak should free the column_arrows when the table is destroyed */ - /* conversations_table.c * conversations_table 2003 Ronnie Sahlberg * Helper routines common to all endpoint conversations tap. @@ -34,6 +32,7 @@ #include <stdlib.h> #include <stdio.h> #include <math.h> +#include <locale.h> #include <gtk/gtk.h> @@ -46,7 +45,6 @@ #include "../simple_dialog.h" #include "../globals.h" -#include "../color.h" #include "gtk/sat.h" #include "gtk/conversations_table.h" @@ -57,11 +55,6 @@ #include "gtk/help_dlg.h" #include "gtk/main.h" -#include "image/clist_ascend.xpm" -#include "image/clist_descend.xpm" - - -#define NUM_COLS 14 #define COL_STR_LEN 16 #define CONV_PTR_KEY "conversations-pointer" #define NB_PAGES_KEY "notebook-pages" @@ -80,7 +73,10 @@ static char * ct_port_to_str(int port_type, guint32 port) { static int i=0; - static gchar str[4][12]; + static gchar *strp, str[4][12]; + gchar *bp; + + strp=str[i]; switch(port_type){ case PT_TCP: @@ -88,13 +84,17 @@ ct_port_to_str(int port_type, guint32 port) case PT_SCTP: case PT_NCP: i = (i+1)%4; - g_snprintf(str[i], sizeof(str[0]), "%d", port); - return str[i]; + bp = &strp[11]; + + *bp = 0; + do { + *--bp = (port % 10) +'0'; + } while ((port /= 10) != 0 && bp > strp); + return bp; } return NULL; } - #define FN_SRC_ADDRESS 0 #define FN_DST_ADDRESS 1 #define FN_ANY_ADDRESS 2 @@ -275,15 +275,6 @@ ct_get_filter_name(address *addr, int specific_addr_type, int port_type, int nam return NULL; } - -typedef struct column_arrows { - GtkWidget *table; - GtkWidget *ascend_pm; - GtkWidget *descend_pm; -} column_arrows; - - - static void reset_ct_table_data(conversations_table *ct) { @@ -291,6 +282,7 @@ reset_ct_table_data(conversations_table *ct) char title[256]; GString *error_string; const char *filter; + GtkListStore *store; if (ct->use_dfilter) { filter = gtk_entry_get_text(GTK_ENTRY(main_display_filter_widget)); @@ -305,9 +297,6 @@ reset_ct_table_data(conversations_table *ct) return; } - /* Allow clist to update */ - gtk_clist_thaw(ct->table); - if(ct->page_lb) { g_snprintf(title, sizeof(title), "Conversations: %s", cf_get_display_name(&cfile)); gtk_window_set_title(GTK_WINDOW(ct->win), title); @@ -330,16 +319,24 @@ reset_ct_table_data(conversations_table *ct) gtk_window_set_title(GTK_WINDOW(ct->win), title); } - /* remove all entries from the clist */ - gtk_clist_clear(ct->table); + /* remove all entries from the list */ + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(ct->table))); + gtk_list_store_clear(store); /* delete all conversations */ for(i=0;i<ct->num_conversations;i++){ - g_free((gpointer)ct->conversations[i].src_address.data); - g_free((gpointer)ct->conversations[i].dst_address.data); + conv_t *conv = &g_array_index(ct->conversations, conv_t, i); + g_free((gpointer)conv->src_address.data); + g_free((gpointer)conv->dst_address.data); } - g_free(ct->conversations); + if (ct->conversations) + g_array_free(ct->conversations, TRUE); + + if (ct->hashtable != NULL) + g_hash_table_destroy(ct->hashtable); + ct->conversations=NULL; + ct->hashtable=NULL; ct->num_conversations=0; } @@ -362,66 +359,77 @@ ct_win_destroy_cb(GtkWindow *win _U_, gpointer data) g_free(conversations); } - - +enum +{ + SRC_ADR_COLUMN, + SRC_PORT_COLUMN, + DST_ADR_COLUMN, + DST_PORT_COLUMN, + PACKETS_COLUMN, + BYTES_COLUMN, + PKT_AB_COLUMN, + BYTES_AB_COLUMN, + PKT_BA_COLUMN, + BYTES_BA_COLUMN, + START_COLUMN, + DURATION_COLUMN, + BPS_AB_COLUMN, + BPS_BA_COLUMN, + INDEX_COLUMN, + N_COLUMNS +}; + static gint -ct_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) +ct_sort_func(GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer user_data) { guint32 idx1, idx2; - conversations_table *ct = g_object_get_data(G_OBJECT(clist), CONV_PTR_KEY); + /* The col to get data from is in userdata */ + gint data_column = GPOINTER_TO_INT(user_data); + + conversations_table *ct = g_object_get_data(G_OBJECT(model), CONV_PTR_KEY); conv_t *conv1 = NULL; conv_t *conv2 = NULL; double duration1, duration2; - const GtkCListRow *row1 = ptr1; - const GtkCListRow *row2 = ptr2; - - idx1 = GPOINTER_TO_INT(row1->data); - idx2 = GPOINTER_TO_INT(row2->data); + gtk_tree_model_get(model, a, INDEX_COLUMN, &idx1, -1); + gtk_tree_model_get(model, b, INDEX_COLUMN, &idx2, -1); if (!ct || idx1 >= ct->num_conversations || idx2 >= ct->num_conversations) return 0; - conv1 = &ct->conversations[idx1]; - conv2 = &ct->conversations[idx2]; + conv1 = &g_array_index(ct->conversations, conv_t, idx1); + conv2 = &g_array_index(ct->conversations, conv_t, idx2); - duration1 = nstime_to_sec(&conv1->stop_time) - nstime_to_sec(&conv1->start_time); - duration2 = nstime_to_sec(&conv2->stop_time) - nstime_to_sec(&conv2->start_time); - switch(clist->sort_column){ - case 0: /* Source address */ + switch(data_column){ + case SRC_ADR_COLUMN: /* Source address */ return(CMP_ADDRESS(&conv1->src_address, &conv2->src_address)); - case 2: /* Destination address */ + case DST_ADR_COLUMN: /* Destination address */ return(CMP_ADDRESS(&conv1->dst_address, &conv2->dst_address)); - case 1: /* Source port */ + case SRC_PORT_COLUMN: /* Source port */ CMP_NUM(conv1->src_port, conv2->src_port); - case 3: /* Destination port */ + case DST_PORT_COLUMN: /* Destination port */ CMP_NUM(conv1->dst_port, conv2->dst_port); - case 4: /* Packets */ - CMP_NUM(conv1->tx_frames+conv1->rx_frames, - conv2->tx_frames+conv2->rx_frames); - case 5: /* Bytes */ - CMP_NUM(conv1->tx_bytes+conv1->rx_bytes, - conv2->tx_bytes+conv2->rx_bytes); - case 6: /* Packets A->B */ - CMP_NUM(conv1->tx_frames, conv2->tx_frames); - case 7: /* Bytes A->B */ - CMP_NUM(conv1->tx_bytes, conv2->tx_bytes); - case 8: /* Packets A<-B */ - CMP_NUM(conv1->rx_frames, conv2->rx_frames); - case 9: /* Bytes A<-B */ - CMP_NUM(conv1->rx_bytes, conv2->rx_bytes); - case 10: /* Start time */ + case START_COLUMN: /* Start time */ return nstime_cmp(&conv1->start_time, &conv2->start_time); - case 11: /* Duration */ + } + + duration1 = nstime_to_sec(&conv1->stop_time) - nstime_to_sec(&conv1->start_time); + duration2 = nstime_to_sec(&conv2->stop_time) - nstime_to_sec(&conv2->start_time); + + switch(data_column){ + case DURATION_COLUMN: /* Duration */ CMP_NUM(duration1, duration2); - case 12: /* bps A->B */ + case BPS_AB_COLUMN: /* bps A->B */ if (duration1 > 0 && conv1->tx_frames > 1 && duration2 > 0 && conv2->tx_frames > 1) { CMP_NUM((gint64) conv1->tx_bytes / duration1, (gint64) conv2->tx_bytes / duration2); } else { CMP_NUM(conv1->tx_bytes, conv2->tx_bytes); } - case 13: /* bps A<-B */ + case BPS_BA_COLUMN: /* bps A<-B */ if (duration1 > 0 && conv1->rx_frames > 1 && duration2 > 0 && conv2->rx_frames > 1) { CMP_NUM((gint64) conv1->rx_bytes / duration1, (gint64) conv2->rx_bytes / duration2); } else { @@ -434,41 +442,6 @@ ct_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) return 0; } - -static void -ct_click_column_cb(GtkCList *clist, gint column, gpointer data) -{ - column_arrows *col_arrows = (column_arrows *) data; - int i; - - for (i = 0; i < NUM_COLS; i++) { - gtk_widget_hide(col_arrows[i].ascend_pm); - gtk_widget_hide(col_arrows[i].descend_pm); - } - - if (column == clist->sort_column) { - if (clist->sort_type == GTK_SORT_ASCENDING) { - clist->sort_type = GTK_SORT_DESCENDING; - gtk_widget_show(col_arrows[column].descend_pm); - } else { - clist->sort_type = GTK_SORT_ASCENDING; - gtk_widget_show(col_arrows[column].ascend_pm); - } - } else { - clist->sort_type = GTK_SORT_ASCENDING; - gtk_widget_show(col_arrows[column].ascend_pm); - gtk_clist_set_sort_column(clist, column); - } - - gtk_clist_sort(clist); - - /* Allow update of clist */ - gtk_clist_thaw(clist); - gtk_clist_freeze(clist); - -} - - /* Filter direction */ #define DIR_A_TO_FROM_B 0 #define DIR_A_TO_B 1 @@ -484,38 +457,47 @@ static void ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callback_action) { int direction; - int selection; + guint32 index = 0; conversations_table *ct = (conversations_table *)callback_data; + GtkTreeIter iter; + GtkTreeModel *model; + GtkTreeSelection *sel; char *str = NULL; char *sport, *dport; + conv_t *conv; direction=FILTER_EXTRA(callback_action); - selection=GPOINTER_TO_INT(g_list_nth_data(GTK_CLIST(ct->table)->selection, 0)); - if(selection>=(int)ct->num_conversations){ + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW(ct->table)); + if (!gtk_tree_selection_get_selected(sel, &model, &iter)) + return; + + gtk_tree_model_get (model, &iter, + INDEX_COLUMN, &index, + -1); + + if(index>= ct->num_conversations){ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "No conversation selected"); return; } - /* translate it back from row index to index in enndpoint array */ - selection=GPOINTER_TO_INT(gtk_clist_get_row_data(ct->table, selection)); - - sport=ct_port_to_str(ct->conversations[selection].port_type, ct->conversations[selection].src_port); - dport=ct_port_to_str(ct->conversations[selection].port_type, ct->conversations[selection].dst_port); + conv = &g_array_index(ct->conversations, conv_t, index); + sport=ct_port_to_str(conv->port_type, conv->src_port); + dport=ct_port_to_str(conv->port_type, conv->dst_port); switch(direction){ case DIR_A_TO_FROM_B: /* A <-> B */ str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_ADDRESS), - ep_address_to_str(&ct->conversations[selection].src_address), + ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_ADDRESS), + ep_address_to_str(&conv->src_address), sport?" && ":"", - sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_PORT):"", + sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_PORT):"", sport?"==":"", sport?sport:"", - ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_ADDRESS), - ep_address_to_str(&ct->conversations[selection].dst_address), + ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_ADDRESS), + ep_address_to_str(&conv->dst_address), dport?" && ":"", - dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_PORT):"", + dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_PORT):"", dport?"==":"", dport?dport:"" ); @@ -523,16 +505,16 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_A_TO_B: /* A --> B */ str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_ADDRESS), - ep_address_to_str(&ct->conversations[selection].src_address), + ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_ADDRESS), + ep_address_to_str(&conv->src_address), sport?" && ":"", - sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_PORT):"", + sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_PORT):"", sport?"==":"", sport?sport:"", - ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_ADDRESS), - ep_address_to_str(&ct->conversations[selection].dst_address), + ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_ADDRESS), + ep_address_to_str(&conv->dst_address), dport?" && ":"", - dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_PORT):"", + dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_PORT):"", dport?"==":"", dport?dport:"" ); @@ -540,16 +522,16 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_A_FROM_B: /* A <-- B */ str = g_strdup_printf("%s==%s%s%s%s%s && %s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_ADDRESS), - ep_address_to_str(&ct->conversations[selection].src_address), + ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_ADDRESS), + ep_address_to_str(&conv->src_address), sport?" && ":"", - sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_PORT):"", + sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_PORT):"", sport?"==":"", sport?sport:"", - ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_ADDRESS), - ep_address_to_str(&ct->conversations[selection].dst_address), + ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_ADDRESS), + ep_address_to_str(&conv->dst_address), dport?" && ":"", - dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_PORT):"", + dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_PORT):"", dport?"==":"", dport?dport:"" ); @@ -557,10 +539,10 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_A_TO_FROM_ANY: /* A <-> ANY */ str = g_strdup_printf("%s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_ADDRESS), - ep_address_to_str(&ct->conversations[selection].src_address), + ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_ADDRESS), + ep_address_to_str(&conv->src_address), sport?" && ":"", - sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_PORT):"", + sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_ANY_PORT):"", sport?"==":"", sport?sport:"" ); @@ -568,10 +550,10 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_A_TO_ANY: /* A --> ANY */ str = g_strdup_printf("%s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_ADDRESS), - ep_address_to_str(&ct->conversations[selection].src_address), + ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_ADDRESS), + ep_address_to_str(&conv->src_address), sport?" && ":"", - sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_PORT):"", + sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_SRC_PORT):"", sport?"==":"", sport?sport:"" ); @@ -579,10 +561,10 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_A_FROM_ANY: /* A <-- ANY */ str = g_strdup_printf("%s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_ADDRESS), - ep_address_to_str(&ct->conversations[selection].src_address), + ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_ADDRESS), + ep_address_to_str(&conv->src_address), sport?" && ":"", - sport?ct_get_filter_name(&ct->conversations[selection].src_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_PORT):"", + sport?ct_get_filter_name(&conv->src_address, conv->sat, conv->port_type, FN_DST_PORT):"", sport?"==":"", sport?sport:"" ); @@ -590,10 +572,10 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_ANY_TO_FROM_B: /* ANY <-> B */ str = g_strdup_printf("%s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_ADDRESS), - ep_address_to_str(&ct->conversations[selection].dst_address), + ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_ADDRESS), + ep_address_to_str(&conv->dst_address), dport?" && ":"", - dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_ANY_PORT):"", + dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_ANY_PORT):"", dport?"==":"", dport?dport:"" ); @@ -601,10 +583,10 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_ANY_FROM_B: /* ANY <-- B */ str = g_strdup_printf("%s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_ADDRESS), - ep_address_to_str(&ct->conversations[selection].dst_address), + ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_ADDRESS), + ep_address_to_str(&conv->dst_address), dport?" && ":"", - dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_SRC_PORT):"", + dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_SRC_PORT):"", dport?"==":"", dport?dport:"" ); @@ -612,10 +594,10 @@ ct_select_filter_cb(GtkWidget *widget _U_, gpointer callback_data, guint callbac case DIR_ANY_TO_B: /* ANY --> B */ str = g_strdup_printf("%s==%s%s%s%s%s", - ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_ADDRESS), - ep_address_to_str(&ct->conversations[selection].dst_address), + ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_ADDRESS), + ep_address_to_str(&conv->dst_address), dport?" && ":"", - dport?ct_get_filter_name(&ct->conversations[selection].dst_address, ct->conversations[selection].sat, ct->conversations[selection].port_type, FN_DST_PORT):"", + dport?ct_get_filter_name(&conv->dst_address, conv->sat, conv->port_type, FN_DST_PORT):"", dport?"==":"", dport?dport:"" ); @@ -633,24 +615,10 @@ static gint ct_show_popup_menu_cb(void *widg _U_, GdkEvent *event, conversations_table *ct) { GdkEventButton *bevent = (GdkEventButton *)event; - gint row; - gint column; - - /* To quote the "Gdk Event Structures" doc: - * "Normally button 1 is the left mouse button, 2 is the middle button, and 3 is the right button" */ if(event->type==GDK_BUTTON_PRESS && bevent->button==3){ - /* if this is a right click on one of our columns, select it and popup the context menu */ - if(gtk_clist_get_selection_info(ct->table, - (gint) (((GdkEventButton *)event)->x), - (gint) (((GdkEventButton *)event)->y), - &row, &column)) { - gtk_clist_unselect_all(ct->table); - gtk_clist_select_row(ct->table, row, -1); - gtk_menu_popup(GTK_MENU(ct->menu), NULL, NULL, NULL, NULL, bevent->button, bevent->time); - } } return FALSE; @@ -1002,62 +970,54 @@ ct_create_popup_menu(conversations_table *ct) /* Draw/refresh the address fields of a single entry at the specified index */ static void -draw_ct_table_address(conversations_table *ct, int conversation_idx) +get_ct_table_address(conversations_table *ct, conv_t *conv, char **entries) { - const char *entry; char *port; guint32 pt; - int rownum; - - rownum=gtk_clist_find_row_from_data(ct->table, (gpointer)(long)conversation_idx); if(!ct->resolve_names) - entry=ep_address_to_str(&ct->conversations[conversation_idx].src_address); + entries[0] = ep_address_to_str(&conv->src_address); else { - entry=get_addr_name(&ct->conversations[conversation_idx].src_address); + entries[0] = (char *)get_addr_name(&conv->src_address); } - gtk_clist_set_text(ct->table, rownum, 0, entry); - pt = ct->conversations[conversation_idx].port_type; + pt = conv->port_type; if(!ct->resolve_names) pt = PT_NONE; switch(pt) { case(PT_TCP): - entry=get_tcp_port(ct->conversations[conversation_idx].src_port); + entries[1] = get_tcp_port(conv->src_port); break; case(PT_UDP): - entry=get_udp_port(ct->conversations[conversation_idx].src_port); + entries[1] = get_udp_port(conv->src_port); break; case(PT_SCTP): - entry=get_sctp_port(ct->conversations[conversation_idx].src_port); + entries[1] = get_sctp_port(conv->src_port); break; default: - port=ct_port_to_str(ct->conversations[conversation_idx].port_type, ct->conversations[conversation_idx].src_port); - entry=port?port:""; + port=ct_port_to_str(conv->port_type, conv->src_port); + entries[1] = port?port:""; } - gtk_clist_set_text(ct->table, rownum, 1, entry); if(!ct->resolve_names) - entry=ep_address_to_str(&ct->conversations[conversation_idx].dst_address); + entries[2]=ep_address_to_str(&conv->dst_address); else { - entry=get_addr_name(&ct->conversations[conversation_idx].dst_address); + entries[2]=(char *)get_addr_name(&conv->dst_address); } - gtk_clist_set_text(ct->table, rownum, 2, entry); switch(pt) { case(PT_TCP): - entry=get_tcp_port(ct->conversations[conversation_idx].dst_port); + entries[3]=get_tcp_port(conv->dst_port); break; case(PT_UDP): - entry=get_udp_port(ct->conversations[conversation_idx].dst_port); + entries[3]=get_udp_port(conv->dst_port); break; case(PT_SCTP): - entry=get_sctp_port(ct->conversations[conversation_idx].dst_port); + entries[3]=get_sctp_port(conv->dst_port); break; default: - port=ct_port_to_str(ct->conversations[conversation_idx].port_type, ct->conversations[conversation_idx].dst_port); - entry=port?port:""; + port=ct_port_to_str(conv->port_type, conv->dst_port); + entries[3]=port?port:""; } - gtk_clist_set_text(ct->table, rownum, 3, entry); } /* Refresh the address fields of all entries in the list */ @@ -1065,20 +1025,64 @@ static void draw_ct_table_addresses(conversations_table *ct) { guint32 i; + char *entries[4]; + GtkListStore *store; + + if (!ct->num_conversations) + return; + + store = GTK_LIST_STORE(gtk_tree_view_get_model(ct->table)); + g_object_ref(store); + gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), NULL); for(i=0;i<ct->num_conversations;i++){ - draw_ct_table_address(ct, i); + conv_t *conv = &g_array_index(ct->conversations, conv_t, i); + if (!conv->iter_valid) + continue; + get_ct_table_address(ct, conv, entries); + gtk_list_store_set (store, &conv->iter, + SRC_ADR_COLUMN, entries[0], + SRC_PORT_COLUMN, entries[1], + DST_ADR_COLUMN, entries[2], + DST_PORT_COLUMN, entries[3], + -1); } + + gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), GTK_TREE_MODEL(store)); + g_object_unref(store); } +static void +switch_to_fixed_col(conversations_table *ct) +{ + gint size; + GtkTreeViewColumn *column; + GList *columns; + + ct->fixed_col = TRUE; + columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(ct->table)); + while(columns) { + column = columns->data; + size = gtk_tree_view_column_get_width (column); + gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_FIXED); + if (size > gtk_tree_view_column_get_fixed_width(column)) + gtk_tree_view_column_set_fixed_width(column, size); + columns = g_list_next(columns); + } + g_list_free(columns); + +#if GTK_CHECK_VERSION(2,6,0) + gtk_tree_view_set_fixed_height_mode(ct->table, TRUE); +#endif +} static void draw_ct_table_data(conversations_table *ct) { guint32 i; - int j; char title[256]; - double duration_s; + GtkListStore *store; + gboolean first = TRUE; if (ct->page_lb) { if(ct->num_conversations) { @@ -1096,57 +1100,97 @@ draw_ct_table_data(conversations_table *ct) } gtk_label_set_text(GTK_LABEL(ct->name_lb), title); } + + store = GTK_LIST_STORE(gtk_tree_view_get_model(ct->table)); for(i=0;i<ct->num_conversations;i++){ - char str[COL_STR_LEN]; - - j=gtk_clist_find_row_from_data(ct->table, (gpointer)(unsigned long)i); - - g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_frames+ct->conversations[i].rx_frames); - gtk_clist_set_text(ct->table, j, 4, str); - g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_bytes+ct->conversations[i].rx_bytes); - gtk_clist_set_text(ct->table, j, 5, str); - - - g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_frames); - gtk_clist_set_text(ct->table, j, 6, str); - g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].tx_bytes); - gtk_clist_set_text(ct->table, j, 7, str); + char start_time[COL_STR_LEN], duration[COL_STR_LEN], + txbps[COL_STR_LEN], rxbps[COL_STR_LEN]; + char *tx_ptr, *rx_ptr; + double duration_s; + conv_t *conversation = &g_array_index(ct->conversations, conv_t, i); + if (!conversation->modified) + continue; + + if (first) { + g_object_ref(store); + gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), NULL); - g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].rx_frames); - gtk_clist_set_text(ct->table, j, 8, str); - g_snprintf(str, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", ct->conversations[i].rx_bytes); - gtk_clist_set_text(ct->table, j, 9, str); + first = FALSE; + } + duration_s = nstime_to_sec(&conversation->stop_time) - nstime_to_sec(&conversation->start_time); + g_snprintf(start_time, COL_STR_LEN, "%s", rel_time_to_secs_str(&conversation->start_time)); + g_snprintf(duration, COL_STR_LEN, "%.4f", duration_s); - duration_s = nstime_to_sec(&ct->conversations[i].stop_time) - nstime_to_sec(&ct->conversations[i].start_time); - g_snprintf(str, COL_STR_LEN, "%s", rel_time_to_secs_str(&ct->conversations[i].start_time)); - gtk_clist_set_text(ct->table, j, 10, str); - g_snprintf(str, COL_STR_LEN, "%.4f", duration_s); - gtk_clist_set_text(ct->table, j, 11, str); - if (duration_s > 0 && ct->conversations[i].tx_frames > 1) { - /* XXX - The gint64 casts below are needed for MSVC++ 6.0 */ - g_snprintf(str, COL_STR_LEN, "%.2f", (gint64) ct->conversations[i].tx_bytes * 8 / duration_s); - gtk_clist_set_text(ct->table, j, 12, str); + if (duration_s > 0 && conversation->tx_frames > 1) { + g_snprintf(txbps, COL_STR_LEN, "%.2f", (gint64) conversation->tx_bytes * 8 / duration_s); + tx_ptr = txbps; } else { - gtk_clist_set_text(ct->table, j, 12, NO_BPS_STR); + tx_ptr = NO_BPS_STR; } - if (duration_s > 0 && ct->conversations[i].rx_frames > 1) { - /* XXX - The gint64 casts below are needed for MSVC++ 6.0 */ - g_snprintf(str, COL_STR_LEN, "%.2f", (gint64) ct->conversations[i].rx_bytes * 8 / duration_s); - gtk_clist_set_text(ct->table, j, 13, str); + if (duration_s > 0 && conversation->rx_frames > 1) { + g_snprintf(rxbps, COL_STR_LEN, "%.2f", (gint64) conversation->rx_bytes * 8 / duration_s); + rx_ptr = rxbps; } else { - gtk_clist_set_text(ct->table, j, 13, NO_BPS_STR); + rx_ptr = NO_BPS_STR; + } + conversation->modified = FALSE; + if (!conversation->iter_valid) { + char *entries[4]; + + get_ct_table_address(ct, conversation, entries); + conversation->iter_valid = TRUE; +#if GTK_CHECK_VERSION(2,6,0) + gtk_list_store_insert_with_values( store , &conversation->iter, G_MAXINT, +#else + gtk_list_store_append(store, &conversation->iter); + gtk_list_store_set (store, &conversation->iter, +#endif + SRC_ADR_COLUMN, entries[0], + SRC_PORT_COLUMN, entries[1], + DST_ADR_COLUMN, entries[2], + DST_PORT_COLUMN, entries[3], + PACKETS_COLUMN, conversation->tx_frames+conversation->rx_frames, + BYTES_COLUMN, conversation->tx_bytes+conversation->rx_bytes, + PKT_AB_COLUMN, conversation->tx_frames, + BYTES_AB_COLUMN, conversation->tx_bytes, + PKT_BA_COLUMN, conversation->rx_frames, + BYTES_BA_COLUMN, conversation->rx_bytes, + START_COLUMN, start_time, + DURATION_COLUMN, duration, + BPS_AB_COLUMN, tx_ptr, + BPS_BA_COLUMN, rx_ptr, + INDEX_COLUMN, i, + -1); + } + else { + gtk_list_store_set (store, &conversation->iter, + PACKETS_COLUMN, conversation->tx_frames+conversation->rx_frames, + BYTES_COLUMN, conversation->tx_bytes+conversation->rx_bytes, + PKT_AB_COLUMN, conversation->tx_frames, + BYTES_AB_COLUMN, conversation->tx_bytes, + PKT_BA_COLUMN, conversation->rx_frames, + BYTES_BA_COLUMN, conversation->rx_bytes, + START_COLUMN, start_time, + DURATION_COLUMN, duration, + BPS_AB_COLUMN, tx_ptr, + BPS_BA_COLUMN, rx_ptr, + -1); } } + if (!first) { + if (!ct->fixed_col && ct->num_conversations >= 1000) { + /* finding the right size for a column isn't easy + * let it run in autosize a little (1000 is arbitrary) + * and then switch to fixed width. + */ + switch_to_fixed_col(ct); + } - draw_ct_table_addresses(ct); - - gtk_clist_sort(ct->table); - - /* Allow table to redraw */ - gtk_clist_thaw(ct->table); - gtk_clist_freeze(ct->table); + gtk_tree_view_set_model(GTK_TREE_VIEW(ct->table), GTK_TREE_MODEL(store)); + g_object_unref(store); + } } static void @@ -1155,62 +1199,185 @@ draw_ct_table_data_cb(void *arg) draw_ct_table_data(arg); } +typedef struct { + int nb_cols; + gint columns_order[N_COLUMNS]; + GString *CSV_str; + conversations_table *talkers; +} csv_t; + +/* output in C locale */ +static gboolean +csv_handle(GtkTreeModel *model, GtkTreePath *path _U_, GtkTreeIter *iter, + gpointer data) +{ + csv_t *csv = (csv_t *)data; + gchar *table_text; + int i; + unsigned index; + conv_t *conv; + double duration_s; + guint64 value; + + gtk_tree_model_get(model, iter, INDEX_COLUMN, &index, -1); + conv=&g_array_index(csv->talkers->conversations, conv_t, index); + duration_s = nstime_to_sec(&conv->stop_time) - nstime_to_sec(&conv->start_time); + + for (i=0; i< csv->nb_cols; i++) { + if (i) + g_string_append(csv->CSV_str, ","); + + switch(csv->columns_order[i]) { + case SRC_ADR_COLUMN: + case SRC_PORT_COLUMN: + case DST_ADR_COLUMN: + case DST_PORT_COLUMN: + gtk_tree_model_get(model, iter, csv->columns_order[i], &table_text, -1); + if (table_text) { + g_string_append(csv->CSV_str, table_text); + g_free(table_text); + } + break; + case PACKETS_COLUMN: + case BYTES_COLUMN: + case PKT_AB_COLUMN: + case BYTES_AB_COLUMN: + case PKT_BA_COLUMN: + case BYTES_BA_COLUMN: + gtk_tree_model_get(model, iter, csv->columns_order[i], &value, -1); + g_string_append_printf(csv->CSV_str, "%" G_GINT64_MODIFIER "u", value); + break; + case START_COLUMN: + g_string_append_printf(csv->CSV_str, "%s", rel_time_to_secs_str(&conv->start_time)); + break; + case DURATION_COLUMN: + g_string_append_printf(csv->CSV_str, "%.4f", duration_s); + break; + case BPS_AB_COLUMN: + if (duration_s > 0 && conv->tx_frames > 1) { + g_string_append_printf(csv->CSV_str, "%.2f", (gint64) conv->tx_bytes * 8 / duration_s); + } else { + g_string_append(csv->CSV_str, NO_BPS_STR); + } + break; + case BPS_BA_COLUMN: + if (duration_s > 0 && conv->rx_frames > 1) { + g_string_append_printf(csv->CSV_str, "%.2f", (gint64) conv->rx_bytes * 8 / duration_s); + } else { + g_string_append(csv->CSV_str, NO_BPS_STR); + } + break; + default: + break; + } + } + g_string_append(csv->CSV_str,"\n"); + + return FALSE; +} + + static void copy_as_csv_cb(GtkWindow *copy_bt, gpointer data _U_) { - guint32 i,j; - gchar *table_entry; GtkClipboard *cb; - GString *CSV_str = g_string_new(""); - - conversations_table *talkers=g_object_get_data(G_OBJECT(copy_bt), CONV_PTR_KEY); - if (!talkers) + char *savelocale; + GList *columns; + GtkTreeViewColumn *column; + GtkListStore *store; + csv_t csv; + + csv.talkers=g_object_get_data(G_OBJECT(copy_bt), CONV_PTR_KEY); + if (!csv.talkers) return; - /* Add the column headers to the CSV data */ - for(i=0;i<talkers->num_columns;i++){ /* all columns */ - if((i==1 || i==3) && !talkers->has_ports) continue; /* Don't add the port column if it's empty */ - g_string_append(CSV_str,talkers->default_titles[i]);/* add the column heading to the CSV string */ - if(i!=talkers->num_columns-1) - g_string_append(CSV_str,","); - } - g_string_append(CSV_str,"\n"); /* new row */ - - /* Add the column values to the CSV data */ - for(i=0;i<talkers->num_conversations;i++){ /* all rows */ - for(j=0;j<talkers->num_columns;j++){ /* all columns */ - if((j==1 || j==3) && !talkers->has_ports) continue; /* Don't add the port column if it's empty */ - gtk_clist_get_text(talkers->table,i,j,&table_entry);/* copy table item into string */ - g_string_append(CSV_str,table_entry); /* add the table entry to the CSV string */ - if(j!=talkers->num_columns-1) - g_string_append(CSV_str,","); + savelocale = setlocale(LC_NUMERIC, NULL); + setlocale(LC_NUMERIC, "C"); + csv.CSV_str = g_string_new(""); + + columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(csv.talkers->table)); + csv.nb_cols = 0; + while(columns) { + column = columns->data; + if (gtk_tree_view_column_get_visible(column)) { + csv.columns_order[csv.nb_cols] = gtk_tree_view_column_get_sort_column_id(column); + if (csv.nb_cols) + g_string_append(csv.CSV_str, ","); + g_string_append(csv.CSV_str, gtk_tree_view_column_get_title(column)); + csv.nb_cols++; } - g_string_append(CSV_str,"\n"); /* new row */ + columns = g_list_next(columns); } + g_list_free(columns); + + g_string_append(csv.CSV_str,"\n"); + store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(csv.talkers->table))); + gtk_tree_model_foreach(GTK_TREE_MODEL(store), csv_handle, &csv); /* Now that we have the CSV data, copy it into the default clipboard */ cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); /* Get the default clipboard */ - gtk_clipboard_set_text(cb, CSV_str->str, -1); /* Copy the CSV data into the clipboard */ - g_string_free(CSV_str, TRUE); /* Free the memory */ + gtk_clipboard_set_text(cb, csv.CSV_str->str, -1); /* Copy the CSV data into the clipboard */ + setlocale(LC_NUMERIC, savelocale); + g_string_free(csv.CSV_str, TRUE); /* Free the memory */ +} + +static gint get_default_col_size(GtkWidget *view, const gchar *str) +{ + PangoLayout *layout; + gint col_width; + + layout = gtk_widget_create_pango_layout(view, str); + pango_layout_get_pixel_size(layout, + &col_width, /* width */ + NULL); /* height */ + g_object_unref(G_OBJECT(layout)); + return col_width; } +static gint default_col_size[N_COLUMNS]; + +static void +init_default_col_size(GtkWidget *view) +{ + + default_col_size[SRC_ADR_COLUMN] = get_default_col_size(view, "00000000.000000000000"); + default_col_size[DST_ADR_COLUMN] = default_col_size[SRC_ADR_COLUMN]; + default_col_size[SRC_PORT_COLUMN] = get_default_col_size(view, "000000"); + default_col_size[DST_PORT_COLUMN] = default_col_size[SRC_PORT_COLUMN]; + default_col_size[PACKETS_COLUMN] = get_default_col_size(view, "00000000"); + default_col_size[BYTES_COLUMN] = get_default_col_size(view, "0000000000"); + default_col_size[PKT_AB_COLUMN] = default_col_size[PACKETS_COLUMN]; + default_col_size[PKT_BA_COLUMN] = default_col_size[PACKETS_COLUMN]; + default_col_size[BYTES_AB_COLUMN] = default_col_size[BYTES_COLUMN]; + default_col_size[BYTES_BA_COLUMN] = default_col_size[BYTES_COLUMN]; + default_col_size[START_COLUMN] = get_default_col_size(view, "000000.000000000"); + default_col_size[DURATION_COLUMN] = get_default_col_size(view, "000000.0000"); + default_col_size[BPS_AB_COLUMN] = get_default_col_size(view, "000000000.00"); + default_col_size[BPS_BA_COLUMN] = default_col_size[BPS_AB_COLUMN]; +} static gboolean -init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, tap_packet_cb packet_func) +init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean hide_ports, const char *table_name, const char *tap_name, const char *filter, + tap_packet_cb packet_func) { int i; - column_arrows *col_arrows; - GtkStyle *win_style; - GtkWidget *column_lb; GString *error_string; char title[256]; + GtkListStore *store; + GtkWidget *tree; + GtkTreeViewColumn *column; + GtkCellRenderer *renderer; + GtkTreeSortable *sortable; + GtkTreeSelection *sel; + static gboolean col_size = FALSE; + conversations->page_lb=NULL; conversations->resolve_names=TRUE; conversations->has_ports=!hide_ports; - conversations->num_columns=NUM_COLS; + conversations->fixed_col = FALSE; conversations->default_titles[0]="Address A", - conversations->default_titles[1]="Port A"; + conversations->default_titles[1]="Port A"; conversations->default_titles[2]="Address B"; conversations->default_titles[3]="Port B"; conversations->default_titles[4]="Packets"; @@ -1223,6 +1390,7 @@ init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean conversations->default_titles[11]="Duration"; conversations->default_titles[12]="bps A->B"; conversations->default_titles[13]="bps A<-B"; + if (strcmp(table_name, "NCP")==0) { conversations->default_titles[1]="Connection A"; conversations->default_titles[3]="Connection B"; @@ -1230,66 +1398,98 @@ init_ct_table_page(conversations_table *conversations, GtkWidget *vbox, gboolean g_snprintf(title, sizeof(title), "%s Conversations", table_name); conversations->name_lb=gtk_label_new(title); - gtk_box_pack_start(GTK_BOX(vbox), conversations->name_lb, FALSE, FALSE, 0); + + /* Create the store */ + store = gtk_list_store_new (N_COLUMNS, /* Total number of columns */ + G_TYPE_STRING, /* Address A */ + G_TYPE_STRING, /* Port A */ + G_TYPE_STRING, /* Address B */ + G_TYPE_STRING, /* Port B */ + G_TYPE_UINT64, /* Packets */ + G_TYPE_UINT64, /* Bytes */ + G_TYPE_UINT64, /* Packets A->B */ + G_TYPE_UINT64, /* Bytes A->B */ + G_TYPE_UINT64, /* Packets A<-B */ + G_TYPE_UINT64, /* Bytes A<-B */ + G_TYPE_STRING, /* Start */ + G_TYPE_STRING, /* Duration */ + G_TYPE_STRING, /* bps A->B */ + G_TYPE_STRING, /* bps A<-B */ + G_TYPE_UINT); /* Index */ + + gtk_box_pack_start(GTK_BOX(vbox), conversations->name_lb, FALSE, FALSE, 0); conversations->scrolled_window=scrolled_window_new(NULL, NULL); gtk_box_pack_start(GTK_BOX(vbox), conversations->scrolled_window, TRUE, TRUE, 0); - conversations->table=(GtkCList *)gtk_clist_new(NUM_COLS); - g_object_set_data(G_OBJECT(conversations->table), CONV_PTR_KEY, conversations); + tree = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); + conversations->table = GTK_TREE_VIEW(tree); + sortable = GTK_TREE_SORTABLE(store); - col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS); - win_style = gtk_widget_get_style(conversations->scrolled_window); - for (i = 0; i < NUM_COLS; i++) { - col_arrows[i].table = gtk_table_new(2, 2, FALSE); - gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5); - column_lb = gtk_label_new(conversations->default_titles[i]); - gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0); - gtk_widget_show(column_lb); - - col_arrows[i].ascend_pm = xpm_to_widget((const char **) clist_ascend_xpm); - gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0); - col_arrows[i].descend_pm = xpm_to_widget((const char **) clist_descend_xpm); - gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0); - /* make total frames be the default sort order */ - if (i == 4) { - gtk_widget_show(col_arrows[i].ascend_pm); - } - gtk_clist_set_column_widget(GTK_CLIST(conversations->table), i, col_arrows[i].table); - gtk_widget_show(col_arrows[i].table); + if (!col_size) { + col_size = TRUE; + init_default_col_size(GTK_WIDGET(conversations->table)); } - gtk_clist_column_titles_show(GTK_CLIST(conversations->table)); - gtk_clist_set_compare_func(conversations->table, ct_sort_column); - gtk_clist_set_sort_column(conversations->table, 4); - gtk_clist_set_sort_type(conversations->table, GTK_SORT_ASCENDING); + /* The view now holds a reference. We can get rid of our own reference */ + g_object_unref (G_OBJECT (store)); + g_object_set_data(G_OBJECT(store), CONV_PTR_KEY, conversations); + g_object_set_data(G_OBJECT(conversations->table), CONV_PTR_KEY, conversations); - for (i = 0; i < NUM_COLS; i++) { - gtk_clist_set_column_auto_resize(conversations->table, i, TRUE); + for (i = 0; i < N_COLUMNS -1; i++) { + renderer = gtk_cell_renderer_text_new (); + g_object_set(renderer, "ypad", 0, NULL); + if (i >= 4) { + /* right align numbers */ + g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL); + column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, "text", + i, NULL); + if (i >= 10) + gtk_tree_sortable_set_sort_func(sortable, i, ct_sort_func, GINT_TO_POINTER(i), NULL); + } + else { + column = gtk_tree_view_column_new_with_attributes (conversations->default_titles[i], renderer, "text", + i, NULL); + if(hide_ports && (i == 1 || i == 3)){ + /* hide srcport and dstport if we don't use ports */ + gtk_tree_view_column_set_visible(column, FALSE); + } + gtk_tree_sortable_set_sort_func(sortable, i, ct_sort_func, GINT_TO_POINTER(i), NULL); + } + gtk_tree_view_column_set_sort_column_id(column, i); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_reorderable(column, TRUE); + gtk_tree_view_column_set_min_width(column, 40); + gtk_tree_view_column_set_fixed_width(column, default_col_size[i]); + gtk_tree_view_append_column (conversations->table, column); +#if 0 + /* for capture with ten thousands conversations it's too slow */ + if (i == PACKETS_COLUMN) { + gtk_tree_view_column_clicked(column); + gtk_tree_view_column_clicked(column); + } +#endif } - - gtk_clist_set_shadow_type(conversations->table, GTK_SHADOW_IN); - gtk_clist_column_titles_show(conversations->table); gtk_container_add(GTK_CONTAINER(conversations->scrolled_window), (GtkWidget *)conversations->table); - - g_signal_connect(conversations->table, "click-column", G_CALLBACK(ct_click_column_cb), col_arrows); + gtk_tree_view_set_rules_hint(conversations->table, TRUE); + gtk_tree_view_set_headers_clickable(conversations->table, TRUE); + gtk_tree_view_set_reorderable (conversations->table, TRUE); conversations->num_conversations=0; conversations->conversations=NULL; + conversations->hashtable=NULL; - /* hide srcport and dstport if we don't use ports */ - if(hide_ports){ - gtk_clist_set_column_visibility(conversations->table, 1, FALSE); - gtk_clist_set_column_visibility(conversations->table, 3, FALSE); - } + sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(conversations->table)); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); /* create popup menu for this table */ ct_create_popup_menu(conversations); /* register the tap and rerun the taps on the packet list */ - error_string=register_tap_listener(tap_name, conversations, filter, 0, reset_ct_table_data_cb, packet_func, draw_ct_table_data_cb); + error_string=register_tap_listener(tap_name, conversations, filter, 0, reset_ct_table_data_cb, packet_func, + draw_ct_table_data_cb); if(error_string){ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str); g_string_free(error_string, TRUE); @@ -1312,7 +1512,7 @@ init_conversation_table(gboolean hide_ports, const char *table_name, const char GtkWidget *copy_bt; GtkTooltips *tooltips = gtk_tooltips_new(); - conversations=g_malloc(sizeof(conversations_table)); + conversations=g_malloc0(sizeof(conversations_table)); conversations->name=table_name; conversations->filter=filter; @@ -1360,12 +1560,6 @@ init_conversation_table(gboolean hide_ports, const char *table_name, const char cf_retap_packets(&cfile); gdk_window_raise(conversations->win->window); - /* Keep clist frozen to cause modifications to the clist (inserts, appends, others that are extremely slow - in GTK2) to not be drawn, allow refreshes to occur at strategic points for performance */ - gtk_clist_freeze(conversations->table); - - /* after retapping, redraw table */ - draw_ct_table_data(conversations); } @@ -1403,7 +1597,7 @@ init_ct_notebook_page_cb(gboolean hide_ports, const char *table_name, const char GtkWidget *page_vbox; conversations_table *conversations; - conversations=g_malloc(sizeof(conversations_table)); + conversations=g_malloc0(sizeof(conversations_table)); conversations->name=table_name; conversations->filter=filter; conversations->resolve_names=TRUE; @@ -1468,9 +1662,6 @@ ct_resolve_toggle_dest(GtkWidget *widget, gpointer data) draw_ct_table_addresses(conversations); - /* Allow table to redraw */ - gtk_clist_thaw(conversations->table); - gtk_clist_freeze(conversations->table); } } @@ -1495,11 +1686,6 @@ ct_filter_toggle_dest(GtkWidget *widget, gpointer data) if (conversations) { gdk_window_raise(conversations->win->window); } - - /* after retapping, redraw table */ - for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) { - draw_ct_table_data(pages[page]); - } } @@ -1522,6 +1708,7 @@ init_conversation_notebook_cb(GtkWidget *w _U_, gpointer d _U_) GSList *current_table; register_ct_t *registered; GtkTooltips *tooltips = gtk_tooltips_new(); + GtkWidget *copy_bt; pages = g_malloc(sizeof(void *) * (g_slist_length(registered_ct_tables) + 1)); @@ -1607,10 +1794,62 @@ init_conversation_notebook_cb(GtkWidget *w _U_, gpointer d _U_) cf_retap_packets(&cfile); gdk_window_raise(win->window); - /* after retapping, redraw table */ - for (page=1; page<=GPOINTER_TO_INT(pages[0]); page++) { - draw_ct_table_data(pages[page]); - } +} + +typedef struct _key { + address addr1; + address addr2; + guint32 port1; + guint32 port2; +} conv_key_t; + + +/* + * Compute the hash value for two given address/port pairs if the match + * is to be exact. + */ +static guint +conversation_hash(gconstpointer v) +{ + const conv_key_t *key = (const conv_key_t *)v; + guint hash_val; + + hash_val = 0; + ADD_ADDRESS_TO_HASH(hash_val, &key->addr1); + hash_val += key->port1; + ADD_ADDRESS_TO_HASH(hash_val, &key->addr2); + hash_val += key->port2; + + return hash_val; +} + +/* + * Compare two conversation keys for an exact match. + */ +static gint +conversation_match(gconstpointer v, gconstpointer w) +{ + const conv_key_t *v1 = (const conv_key_t *)v; + const conv_key_t *v2 = (const conv_key_t *)w; + + if (v1->port1 == v2->port1 && + v1->port2 == v2->port2 && + ADDRESSES_EQUAL(&v1->addr1, &v2->addr1) && + ADDRESSES_EQUAL(&v1->addr2, &v2->addr2)) { + return 1; + } + + if (v1->port2 == v2->port1 && + v1->port1 == v2->port2 && + ADDRESSES_EQUAL(&v1->addr2, &v2->addr1) && + ADDRESSES_EQUAL(&v1->addr1, &v2->addr2)) { + return 1; + } + + /* + * The addresses or the ports don't match. + */ + return 0; } @@ -1620,7 +1859,7 @@ add_conversation_table_data(conversations_table *ct, const address *src, const a const address *addr1, *addr2; guint32 port1, port2; conv_t *conversation=NULL; - int conversation_idx=0; + unsigned int conversation_idx=0; gboolean new_conversation; if(src_port>dst_port){ @@ -1645,68 +1884,72 @@ add_conversation_table_data(conversations_table *ct, const address *src, const a port1=dst_port; } - new_conversation=FALSE; - /* XXX should be optimized to allocate n extra entries at a time - instead of just one */ /* if we dont have any entries at all yet */ if(ct->conversations==NULL){ - ct->conversations=g_malloc(sizeof(conv_t)); - ct->num_conversations=1; - conversation=&ct->conversations[0]; + ct->conversations= g_array_sized_new(FALSE, FALSE, sizeof(conv_t), 10000); conversation_idx=0; - new_conversation=TRUE; - } - /* try to find it among the existing known conversations */ - if(conversation==NULL){ - guint32 i; - for(i=0;i<ct->num_conversations;i++){ - if( (!CMP_ADDRESS(&ct->conversations[i].src_address, addr1))&&(!CMP_ADDRESS(&ct->conversations[i].dst_address, addr2))&&(ct->conversations[i].src_port==port1)&&(ct->conversations[i].dst_port==port2) ){ - conversation=&ct->conversations[i]; - conversation_idx=i; - break; - } - if( (!CMP_ADDRESS(&ct->conversations[i].src_address, addr2))&&(!CMP_ADDRESS(&ct->conversations[i].dst_address, addr1))&&(ct->conversations[i].src_port==port2)&&(ct->conversations[i].dst_port==port1) ){ - conversation=&ct->conversations[i]; - conversation_idx=i; - break; - } - } + ct->hashtable = g_hash_table_new(conversation_hash,conversation_match); + + } + else { + /* try to find it among the existing known conversations */ + conv_key_t existing_key; + + existing_key.addr1 = *addr1; + existing_key.addr2 = *addr2; + existing_key.port1 = port1; + existing_key.port2 = port2; + conversation_idx = GPOINTER_TO_UINT(g_hash_table_lookup(ct->hashtable, &existing_key)); + if (conversation_idx) { + conversation_idx--; + conversation=&g_array_index(ct->conversations, conv_t, conversation_idx); + } } /* if we still dont know what conversation this is it has to be a new one and we have to allocate it and append it to the end of the list */ if(conversation==NULL){ + conv_key_t *new_key; + conv_t conv; new_conversation=TRUE; - ct->num_conversations++; - ct->conversations=g_realloc(ct->conversations, ct->num_conversations*sizeof(conv_t)); - conversation=&ct->conversations[ct->num_conversations-1]; - conversation_idx=ct->num_conversations-1; - } - - /* if this is a new conversation we need to initialize the struct */ - if(new_conversation){ - COPY_ADDRESS(&conversation->src_address, addr1); - COPY_ADDRESS(&conversation->dst_address, addr2); - conversation->sat=sat; - conversation->port_type=port_type; - conversation->src_port=port1; - conversation->dst_port=port2; - conversation->rx_frames=0; - conversation->tx_frames=0; - conversation->rx_bytes=0; - conversation->tx_bytes=0; + + COPY_ADDRESS(&conv.src_address, addr1); + COPY_ADDRESS(&conv.dst_address, addr2); + conv.sat=sat; + conv.port_type=port_type; + conv.src_port=port1; + conv.dst_port=port2; + conv.rx_frames=0; + conv.tx_frames=0; + conv.rx_bytes=0; + conv.tx_bytes=0; + conv.iter_valid = FALSE; + conv.modified = TRUE; + if (ts) { - memcpy(&conversation->start_time, ts, sizeof(conversation->start_time)); - memcpy(&conversation->stop_time, ts, sizeof(conversation->stop_time)); + memcpy(&conv.start_time, ts, sizeof(conv.start_time)); + memcpy(&conv.stop_time, ts, sizeof(conv.stop_time)); } else { - nstime_set_unset(&conversation->start_time); - nstime_set_unset(&conversation->stop_time); + nstime_set_unset(&conv.start_time); + nstime_set_unset(&conv.stop_time); } + g_array_append_val(ct->conversations, conv); + conversation_idx=ct->num_conversations; + conversation=&g_array_index(ct->conversations, conv_t, conversation_idx); + new_key = g_malloc(sizeof (conv_key_t)); + COPY_ADDRESS(&new_key->addr1,addr1); + COPY_ADDRESS(&new_key->addr2,addr2); + new_key->port1 = port1; + new_key->port2 = port2; + g_hash_table_insert(ct->hashtable, new_key, GUINT_TO_POINTER(conversation_idx +1)); + + ct->num_conversations++; } /* update the conversation struct */ + conversation->modified = TRUE; if( (!CMP_ADDRESS(src, addr1))&&(!CMP_ADDRESS(dst, addr2))&&(src_port==port1)&&(dst_port==port2) ){ conversation->tx_frames+=num_frames; conversation->tx_bytes+=num_bytes; @@ -1722,61 +1965,6 @@ add_conversation_table_data(conversations_table *ct, const address *src, const a memcpy(&conversation->start_time, ts, sizeof(conversation->start_time)); } } - - /* if this was a new conversation we have to create a clist row for it */ - if(new_conversation){ - char *entries[NUM_COLS]; - char frames[COL_STR_LEN], bytes[COL_STR_LEN], - txframes[COL_STR_LEN], txbytes[COL_STR_LEN], - rxframes[COL_STR_LEN], rxbytes[COL_STR_LEN], - start_time[COL_STR_LEN], duration[COL_STR_LEN], - txbps[COL_STR_LEN], rxbps[COL_STR_LEN]; - double duration_s; - - /* these values will be filled by call to draw_ct_table_addresses() below */ - entries[0] = ""; - entries[1] = ""; - entries[2] = ""; - entries[3] = ""; - - g_snprintf(frames, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_frames+conversation->rx_frames); - entries[4]=frames; - g_snprintf(bytes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_bytes+conversation->rx_bytes); - entries[5]=bytes; - - g_snprintf(txframes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_frames); - entries[6]=txframes; - g_snprintf(txbytes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->tx_bytes); - entries[7]=txbytes; - - g_snprintf(rxframes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->rx_frames); - entries[8]=rxframes; - g_snprintf(rxbytes, COL_STR_LEN, "%" G_GINT64_MODIFIER "u", conversation->rx_bytes); - entries[9]=rxbytes; - - duration_s = nstime_to_sec(&conversation->start_time) - nstime_to_sec(&conversation->stop_time); - g_snprintf(start_time, COL_STR_LEN, "%s", rel_time_to_secs_str(&conversation->start_time)); - g_snprintf(duration, COL_STR_LEN, "%.4f", duration_s); - entries[10]=start_time; - entries[11]=duration; - if (duration_s > 0 && conversation->tx_frames > 1) { - g_snprintf(txbps, COL_STR_LEN, "%.2f", (gint64) conversation->tx_bytes * 8 / duration_s); - entries[12]=txbps; - } else { - entries[12] = NO_BPS_STR; - } - if (duration_s > 0 && conversation->rx_frames > 1) { - g_snprintf(rxbps, COL_STR_LEN, "%.2f", (gint64) conversation->rx_bytes * 8 / duration_s); - entries[13]=rxbps; - } else { - entries[13] = NO_BPS_STR; - } - - gtk_clist_insert(ct->table, conversation_idx, entries); - gtk_clist_set_row_data(ct->table, conversation_idx, (gpointer)(long) conversation_idx); - - draw_ct_table_address(ct, conversation_idx); - } } /* |