From 0efdbd63ff75def9d0ad3162b83a4eb44472d341 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 7 Jan 2010 12:59:49 -0500 Subject: trace-graph: Implement event filtering for the graph Move some of the trace-view event filtering to be generic enough and have the trace-graph use it. Signed-off-by: Steven Rostedt --- Makefile | 2 +- trace-filter.c | 20 +++++++ trace-filter.h | 62 ++++++++++++++++++++++ trace-graph-main.c | 53 ++++++++++++++++++- trace-graph.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++-- trace-graph.h | 16 +++++- trace-hash.c | 21 -------- trace-hash.h | 5 -- trace-view-store.c | 1 + trace-view.h | 52 +----------------- 10 files changed, 298 insertions(+), 86 deletions(-) create mode 100644 trace-filter.h diff --git a/Makefile b/Makefile index 4826dd1..0a66cbb 100644 --- a/Makefile +++ b/Makefile @@ -52,7 +52,7 @@ trace-cmd:: trace-cmd.o trace-read.o trace-view:: trace-view-main.o $(TRACE_VIEW_OBJS) $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS) -trace-graph:: trace-graph-main.o trace-graph.o trace-compat.o trace-hash.o +trace-graph:: trace-graph-main.o trace-graph.o trace-compat.o trace-hash.o trace-filter.o $(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS) kernelshark:: kernel-shark.o trace-compat.o $(TRACE_VIEW_OBJS) trace-graph.o \ diff --git a/trace-filter.c b/trace-filter.c index 894448e..ab30b1f 100644 --- a/trace-filter.c +++ b/trace-filter.c @@ -12,6 +12,26 @@ #define DIALOG_WIDTH 400 #define DIALOG_HEIGHT 600 +int str_cmp(const void *a, const void *b) +{ + char * const * sa = a; + char * const * sb = b; + + return strcmp(*sa, *sb); +} + +int id_cmp(const void *a, const void *b) +{ + const gint *ia = a; + const gint *ib = b; + + if (*ia > *ib) + return 1; + if (*ia < *ib) + return -1; + return 0; +} + struct dialog_helper { GtkWidget *dialog; gpointer data; diff --git a/trace-filter.h b/trace-filter.h new file mode 100644 index 0000000..ced654e --- /dev/null +++ b/trace-filter.h @@ -0,0 +1,62 @@ +#ifndef _TRACE_FILTER_H +#define _TRACE_FILTER_H + +#include + +struct event_filter_list { + struct event_filter_list *next; + struct event *event; +}; + +/** + * trace_filter_event_cb_func - callback type for event dialog + * @accept: TRUE if the accept button was pressed, otherwise FALSE + * @all_events: TRUE if "All Events" was checked + * @systems: NULL or a string array of systems terminated with NULL + * @events: NULL or a int array of event ids terminated with -1 + * @data: The data given passed in to the event dialog function + * + * If @accept is FALSE then @all_events, @systems, and @events + * should be ignored. @data is still valid. + * + * If @all_events is TRUE then @systems and @events should be ignored. + */ +typedef void (*trace_filter_event_cb_func)(gboolean accept, + gboolean all_events, + char **systems, + gint *events, + gpointer data); + +void trace_filter_event_dialog(struct tracecmd_input *handle, + gboolean all_events, + gchar **systems, + gint *events, + trace_filter_event_cb_func func, + gpointer data); + +/** + * trace_filter_cpu_cb_func - callback type for CPU dialog + * @accept: TRUE if the accept button was pressed, otherwise FALSE + * @all_cpus: TRUE if "All CPUS" was checked + * @selected_cpus: NULL or a cpu_mask with the cpus that were checked set. + * @data: The data given passed in to the CPU dialog function + * + * If @accept is FALSE then @all_cpus and @selected_cpus should be ignored. + * @data is still valid. + * + * If @all_cpus is TRUE then @selected_cpus should be ignored. + */ +typedef void (*trace_filter_cpu_cb_func)(gboolean accept, + gboolean all_cpus, + guint64 *selected_cpus, + gpointer data); + +void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpu_mask_selected, gint cpus, + trace_filter_cpu_cb_func func, gpointer data); + +/* put here because there's no other place */ + +int str_cmp(const void *a, const void *b); +int id_cmp(const void *a, const void *b); + +#endif /* _TRACE_FILTER_H */ diff --git a/trace-graph-main.c b/trace-graph-main.c index de6bd12..80907d2 100644 --- a/trace-graph-main.c +++ b/trace-graph-main.c @@ -5,6 +5,7 @@ #include "trace-cmd.h" #include "trace-graph.h" +#include "trace-filter.h" #define version "0.1.1" @@ -41,6 +42,24 @@ delete_event (GtkWidget *widget, GdkEvent *event, gpointer data) return TRUE; } +/* Callback for the clicked signal of the Events filter button */ +static void +events_clicked (gpointer data) +{ + struct graph_info *ginfo = data; + gboolean all_events = TRUE; + gchar **systems = NULL; + gint *events = NULL; + + all_events = ginfo->all_events; + systems = ginfo->systems; + events = ginfo->event_ids; + + trace_filter_event_dialog(ginfo->handle, all_events, + systems, events, + trace_graph_event_filter_callback, ginfo); +} + void trace_graph(int argc, char **argv) { struct tracecmd_input *handle; @@ -81,6 +100,9 @@ void trace_graph(int argc, char **argv) gtk_init(&argc, &argv); + /* graph struct is used by handlers */ + ginfo = trace_graph_create(handle); + /* --- Main window --- */ window = gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -127,6 +149,36 @@ void trace_graph(int argc, char **argv) /* --- end File options --- */ + /* --- Filter Option --- */ + + menu_item = gtk_menu_item_new_with_label("Filter"); + gtk_widget_show(menu_item); + + gtk_menu_bar_append(GTK_MENU_BAR (menu_bar), menu_item); + + menu = gtk_menu_new(); /* Don't need to show menus */ + + + /* --- Filter - Events Option --- */ + + sub_item = gtk_menu_item_new_with_label("events"); + + /* Add them to the menu */ + gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item); + + /* We can attach the Quit menu item to our exit function */ + g_signal_connect_swapped (G_OBJECT (sub_item), "activate", + G_CALLBACK (events_clicked), + (gpointer) ginfo); + + /* We do need to show menu items */ + gtk_widget_show(sub_item); + + + /* --- End Filter Options --- */ + gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu); + + /* --- Top Level Hbox --- */ hbox = gtk_hbox_new(FALSE, 0); @@ -136,7 +188,6 @@ void trace_graph(int argc, char **argv) /* --- Set up the Graph --- */ - ginfo = trace_graph_create(handle); widget = trace_graph_get_window(ginfo); gtk_box_pack_start(GTK_BOX (hbox), widget, TRUE, TRUE, 0); gtk_widget_show(widget); diff --git a/trace-graph.c b/trace-graph.c index 5f1464c..8457edf 100644 --- a/trace-graph.c +++ b/trace-graph.c @@ -30,6 +30,7 @@ #include "trace-local.h" #include "trace-graph.h" #include "trace-hash.h" +#include "trace-filter.h" #define DEBUG_LEVEL 2 #if DEBUG_LEVEL > 0 @@ -112,12 +113,69 @@ static void graph_filter_task_clear(struct graph_info *ginfo) ginfo->filter_enabled = 0; } +gboolean graph_filter_system(struct graph_info *ginfo, const gchar *system) +{ + const gchar **sys = &system; + + if (ginfo->all_events) + return TRUE; + + if (!ginfo->systems) + return FALSE; + + sys = bsearch(sys, ginfo->systems, ginfo->systems_size, + sizeof(system), str_cmp); + + return sys != NULL; +} + +gboolean graph_filter_event(struct graph_info *ginfo, gint event_id) +{ + gint *event = &event_id; + + if (ginfo->all_events) + return TRUE; + + if (!ginfo->event_ids) + return FALSE; + + event = bsearch(event, ginfo->event_ids, ginfo->event_ids_size, + sizeof(event_id), id_cmp); + + return event != NULL; +} + +gboolean graph_filter_on_event(struct graph_info *ginfo, struct record *record) +{ + struct event_format *event; + gint event_id; + + if (!record) + return TRUE; + + if (ginfo->all_events) + return FALSE; + + event_id = pevent_data_type(ginfo->pevent, record); + event = pevent_data_event_from_type(ginfo->pevent, event_id); + if (!event) + return TRUE; + + if (graph_filter_system(ginfo, event->system)) + return FALSE; + + if (graph_filter_event(ginfo, event_id)) + return FALSE; + + return TRUE; +} + gboolean graph_filter_on_task(struct graph_info *ginfo, gint pid) { gboolean filter; filter = FALSE; - + if (ginfo->filter_enabled && filter_task_count(ginfo->task_filter) && !trace_graph_filter_task_find_pid(ginfo, pid)) @@ -1147,7 +1205,7 @@ static void draw_cpu(struct graph_info *ginfo, gint cpu, filter = graph_filter_on_task(ginfo, last_pid); if (!filter && last_pid) - + gdk_draw_rectangle(ginfo->curr_pixmap, gc, TRUE, last_x, CPU_BOX_TOP(cpu), @@ -1166,9 +1224,12 @@ static void draw_cpu(struct graph_info *ginfo, gint cpu, last_pid = pid; - if (!filter) - gdk_draw_line(ginfo->curr_pixmap, gc, // ginfo->draw->style->black_gc, - x, CPU_TOP(cpu), x, CPU_BOTTOM(cpu)); + if (!filter) { + filter = graph_filter_on_event(ginfo, record); + if (!filter) + gdk_draw_line(ginfo->curr_pixmap, gc, + x, CPU_TOP(cpu), x, CPU_BOTTOM(cpu)); + } if (!filter) { /* Figure out if we can show the text for the previous record */ @@ -1372,6 +1433,82 @@ void trace_graph_select_by_time(struct graph_info *ginfo, guint64 time) update_with_backend(ginfo, 0, 0, width, ginfo->draw_height); } +static void graph_free_systems(struct graph_info *ginfo) +{ + gint i; + + if (!ginfo->systems) + return; + + for (i = 0; ginfo->systems[i]; i++) + g_free(ginfo->systems[i]); + + g_free(ginfo->systems); + ginfo->systems = NULL; + ginfo->systems_size = 0; +} + +static void graph_free_events(struct graph_info *ginfo) +{ + g_free(ginfo->event_ids); + ginfo->event_ids = NULL; + ginfo->event_ids_size = 0; +} + +void trace_graph_event_filter_callback(gboolean accept, + gboolean all_events, + gchar **systems, + gint *events, + gpointer data) +{ + struct graph_info *ginfo = data; + gint i; + + if (!accept) + return; + + graph_free_systems(ginfo); + graph_free_events(ginfo); + + if (all_events) { + ginfo->all_events = TRUE; + redraw_graph(ginfo); + return; + } + + ginfo->all_events = FALSE; + + if (systems) { + for (ginfo->systems_size = 0; + systems[ginfo->systems_size]; + ginfo->systems_size++) + ; + + ginfo->systems = g_new(typeof(*systems), ginfo->systems_size + 1); + for (i = 0; i < ginfo->systems_size; i++) + ginfo->systems[i] = g_strdup(systems[i]); + ginfo->systems[i] = NULL; + + qsort(ginfo->systems, ginfo->systems_size, sizeof(gchar *), str_cmp); + } + + if (events) { + for (ginfo->event_ids_size = 0; + events[ginfo->event_ids_size] >= 0; + ginfo->event_ids_size++) + ; + + ginfo->event_ids = g_new(typeof(*events), ginfo->event_ids_size + 1); + for (i = 0; i < ginfo->event_ids_size; i++) + ginfo->event_ids[i] = events[i]; + ginfo->event_ids[i] = -1; + + qsort(ginfo->event_ids, ginfo->event_ids_size, sizeof(gint), id_cmp); + } + + redraw_graph(ginfo); +} + static void redraw_pixmap_backend(struct graph_info *ginfo) { GdkPixmap *old_pix; @@ -1436,6 +1573,9 @@ destroy_event(GtkWidget *widget, gpointer data) { struct graph_info *ginfo = data; + graph_free_systems(ginfo); + graph_free_events(ginfo); + if (ginfo->test) dprintf(1, "test = %s\n", ginfo->test); @@ -1556,6 +1696,8 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, ginfo->pevent = tracecmd_get_pevent(handle); ginfo->cpus = tracecmd_cpus(handle); + ginfo->all_events = TRUE; + ginfo->callbacks = cbs; ginfo->start_time = -1ULL; diff --git a/trace-graph.h b/trace-graph.h index 61d17db..db15c50 100644 --- a/trace-graph.h +++ b/trace-graph.h @@ -48,8 +48,14 @@ struct graph_info { struct graph_callbacks *callbacks; /* call back hooks for changes to graph */ - int filter_enabled; - int filter_available; + gboolean filter_enabled; + gboolean filter_available; + + gboolean all_events; /* all events enabled */ + gchar **systems; /* event systems to filter on */ + gint *event_ids; /* events to filter on */ + gint systems_size; + gint event_ids_size; struct filter_task *task_filter; gint filter_task_selected; @@ -76,6 +82,12 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, struct graph_callbacks *cbs); void trace_graph_select_by_time(struct graph_info *ginfo, guint64 time); +void trace_graph_event_filter_callback(gboolean accept, + gboolean all_events, + gchar **systems, + gint *events, + gpointer data); + static inline GtkWidget *trace_graph_get_draw(struct graph_info *ginfo) { return ginfo->draw; diff --git a/trace-hash.c b/trace-hash.c index a41b2ce..d8cd42f 100644 --- a/trace-hash.c +++ b/trace-hash.c @@ -159,24 +159,3 @@ struct filter_task *filter_task_hash_copy(struct filter_task *hash) return new_hash; } - - -int str_cmp(const void *a, const void *b) -{ - char * const * sa = a; - char * const * sb = b; - - return strcmp(*sa, *sb); -} - -int id_cmp(const void *a, const void *b) -{ - const gint *ia = a; - const gint *ib = b; - - if (*ia > *ib) - return 1; - if (*ia < *ib) - return -1; - return 0; -} diff --git a/trace-hash.h b/trace-hash.h index 04543f7..9948138 100644 --- a/trace-hash.h +++ b/trace-hash.h @@ -29,9 +29,4 @@ static inline gint filter_task_count(struct filter_task *hash) return hash->count; } -/* put here because there's no other place */ - -int str_cmp(const void *a, const void *b); -int id_cmp(const void *a, const void *b); - #endif /* _TRACE_HASH_H */ diff --git a/trace-view-store.c b/trace-view-store.c index cc6b865..3ea7e3b 100644 --- a/trace-view-store.c +++ b/trace-view-store.c @@ -3,6 +3,7 @@ #include #include "cpu.h" +#include "trace-filter.h" /* boring declarations of local functions */ diff --git a/trace-view.h b/trace-view.h index 44cf0d0..2b34f23 100644 --- a/trace-view.h +++ b/trace-view.h @@ -2,6 +2,7 @@ #define _TRACE_VIEW_H #include "trace-view-store.h" +#include "trace-filter.h" void trace_view_load(GtkWidget *view, struct tracecmd_input *handle, @@ -12,57 +13,6 @@ void trace_view(int argc, char **argv); void trace_view_update_task_filter(GtkWidget *treeview, struct filter_task *filter); void trace_view_make_selection_visible(GtkWidget *treeview); -struct event_filter_list { - struct event_filter_list *next; - struct event *event; -}; - -/** - * trace_filter_event_cb_func - callback type for event dialog - * @accept: TRUE if the accept button was pressed, otherwise FALSE - * @all_events: TRUE if "All Events" was checked - * @systems: NULL or a string array of systems terminated with NULL - * @events: NULL or a int array of event ids terminated with -1 - * @data: The data given passed in to the event dialog function - * - * If @accept is FALSE then @all_events, @systems, and @events - * should be ignored. @data is still valid. - * - * If @all_events is TRUE then @systems and @events should be ignored. - */ -typedef void (*trace_filter_event_cb_func)(gboolean accept, - gboolean all_events, - char **systems, - gint *events, - gpointer data); - -void trace_filter_event_dialog(struct tracecmd_input *handle, - gboolean all_events, - gchar **systems, - gint *events, - trace_filter_event_cb_func func, - gpointer data); - -/** - * trace_filter_cpu_cb_func - callback type for CPU dialog - * @accept: TRUE if the accept button was pressed, otherwise FALSE - * @all_cpus: TRUE if "All CPUS" was checked - * @selected_cpus: NULL or a cpu_mask with the cpus that were checked set. - * @data: The data given passed in to the CPU dialog function - * - * If @accept is FALSE then @all_cpus and @selected_cpus should be ignored. - * @data is still valid. - * - * If @all_cpus is TRUE then @selected_cpus should be ignored. - */ -typedef void (*trace_filter_cpu_cb_func)(gboolean accept, - gboolean all_cpus, - guint64 *selected_cpus, - gpointer data); - -void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpu_mask_selected, gint cpus, - trace_filter_cpu_cb_func func, gpointer data); - void trace_view_select(GtkWidget *treeview, guint64 time); void trace_view_event_filter_callback(gboolean accept, -- cgit v1.2.2