diff options
author | Steven Rostedt <srostedt@redhat.com> | 2009-12-31 15:38:36 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2009-12-31 15:38:36 -0500 |
commit | e16a1edd19467530e2a3b5649b200d97a64c5675 (patch) | |
tree | 13ad71d9589d34fc70fbcea2dc98bc2c79ee269c | |
parent | 12d78ef3bbb5cce5085ade12b197c211d26531eb (diff) |
trace-graph: Add task filtering popup menus
Add popup menus to graph that allows the use to add or remove
tasks from the filter, as well as to enable or disable the filter.
This only adds the popup menus, the filtering is not actually done yet.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | trace-compat.c | 17 | ||||
-rw-r--r-- | trace-compat.h | 6 | ||||
-rw-r--r-- | trace-graph.c | 293 | ||||
-rw-r--r-- | trace-graph.h | 10 |
4 files changed, 310 insertions, 16 deletions
diff --git a/trace-compat.c b/trace-compat.c index 9c1d7e3..118a826 100644 --- a/trace-compat.c +++ b/trace-compat.c | |||
@@ -2,6 +2,9 @@ | |||
2 | 2 | ||
3 | #if GTK_VERSION < CALC_GTK_VERSION(2,18,0) | 3 | #if GTK_VERSION < CALC_GTK_VERSION(2,18,0) |
4 | 4 | ||
5 | #warning Using compat functions for older GTK library | ||
6 | #warning Please upgrade your GTK to at least 2.18 | ||
7 | |||
5 | void gtk_cell_renderer_get_padding(GtkCellRenderer *cell, | 8 | void gtk_cell_renderer_get_padding(GtkCellRenderer *cell, |
6 | gint *xpad, gint *ypad) | 9 | gint *xpad, gint *ypad) |
7 | { | 10 | { |
@@ -13,6 +16,20 @@ void gtk_cell_renderer_get_padding(GtkCellRenderer *cell, | |||
13 | 16 | ||
14 | #endif /* version < 2.18.0 */ | 17 | #endif /* version < 2.18.0 */ |
15 | 18 | ||
19 | #if GTK_VERSION < CALC_GTK_VERSION(2,16,0) | ||
20 | |||
21 | void gtk_menu_item_set_label(GtkMenuItem *menu_item, const gchar *label) | ||
22 | { | ||
23 | g_return_if_fail(GTK_IS_MENU_ITEM(menu_item)); | ||
24 | |||
25 | if (GTK_IS_LABEL(GTK_BIN(menu_item)->child)) { | ||
26 | gtk_label_set_label(GTK_LABEL(GTK_BIN(menu_item)->child), | ||
27 | label ? label : ""); | ||
28 | } | ||
29 | } | ||
30 | |||
31 | #endif /* version < 2.18.0 */ | ||
32 | |||
16 | #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) | 33 | #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) |
17 | 34 | ||
18 | gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj) | 35 | gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj) |
diff --git a/trace-compat.h b/trace-compat.h index 381fc6c..8f8ad11 100644 --- a/trace-compat.h +++ b/trace-compat.h | |||
@@ -12,6 +12,12 @@ void gtk_cell_renderer_get_padding(GtkCellRenderer *cell, | |||
12 | 12 | ||
13 | #endif /* version < 2.18.0 */ | 13 | #endif /* version < 2.18.0 */ |
14 | 14 | ||
15 | #if GTK_VERSION < CALC_GTK_VERSION(2,16,0) | ||
16 | |||
17 | void gtk_menu_item_set_label(GtkMenuItem *menu_item, const gchar *label); | ||
18 | |||
19 | #endif /* version < 2.18.0 */ | ||
20 | |||
15 | #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) | 21 | #if GTK_VERSION < CALC_GTK_VERSION(2,14,0) |
16 | 22 | ||
17 | gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj); | 23 | gdouble gtk_adjustment_get_page_size(GtkAdjustment *adj); |
diff --git a/trace-graph.c b/trace-graph.c index b70e27d..504eb36 100644 --- a/trace-graph.c +++ b/trace-graph.c | |||
@@ -55,12 +55,20 @@ | |||
55 | #define CPU_LABEL(cpu) (CPU_TOP(cpu)) | 55 | #define CPU_LABEL(cpu) (CPU_TOP(cpu)) |
56 | #define CPU_X 5 | 56 | #define CPU_X 5 |
57 | 57 | ||
58 | #define FILTER_TASK_HASH_SIZE 256 | ||
59 | |||
60 | struct filter_task { | ||
61 | struct filter_task *next; | ||
62 | gint pid; | ||
63 | }; | ||
64 | |||
58 | static gint ftrace_sched_switch_id = -1; | 65 | static gint ftrace_sched_switch_id = -1; |
59 | static gint event_sched_switch_id = -1; | 66 | static gint event_sched_switch_id = -1; |
60 | 67 | ||
61 | static gint largest_cpu_label = 0; | 68 | static gint largest_cpu_label = 0; |
62 | 69 | ||
63 | static void redraw_pixmap_backend(struct graph_info *ginfo); | 70 | static void redraw_pixmap_backend(struct graph_info *ginfo); |
71 | static gint do_hash(gint val); | ||
64 | 72 | ||
65 | static void convert_nano(unsigned long long time, unsigned long *sec, | 73 | static void convert_nano(unsigned long long time, unsigned long *sec, |
66 | unsigned long *usec) | 74 | unsigned long *usec) |
@@ -80,6 +88,87 @@ static void print_time(unsigned long long time) | |||
80 | printf("%lu.%06lu", sec, usec); | 88 | printf("%lu.%06lu", sec, usec); |
81 | } | 89 | } |
82 | 90 | ||
91 | static struct filter_task * | ||
92 | filter_task_find_pid(struct graph_info *ginfo, gint pid) | ||
93 | { | ||
94 | gint key = do_hash(pid) % FILTER_TASK_HASH_SIZE; | ||
95 | struct filter_task *task = ginfo->filter_task_hash[key]; | ||
96 | |||
97 | while (task) { | ||
98 | if (task->pid == pid) | ||
99 | break; | ||
100 | task = task->next; | ||
101 | } | ||
102 | return task; | ||
103 | } | ||
104 | |||
105 | static void filter_task_add_pid(struct graph_info *ginfo, gint pid) | ||
106 | { | ||
107 | gint key = do_hash(pid) % FILTER_TASK_HASH_SIZE; | ||
108 | struct filter_task *task; | ||
109 | |||
110 | task = g_new0(typeof(*task), 1); | ||
111 | g_assert(task); | ||
112 | |||
113 | task->pid = pid; | ||
114 | task->next = ginfo->filter_task_hash[key]; | ||
115 | ginfo->filter_task_hash[key] = task; | ||
116 | |||
117 | ginfo->filter_task_count++; | ||
118 | ginfo->filter_available = 1; | ||
119 | } | ||
120 | |||
121 | static void filter_task_remove_pid(struct graph_info *ginfo, gint pid) | ||
122 | { | ||
123 | gint key = do_hash(pid) % FILTER_TASK_HASH_SIZE; | ||
124 | struct filter_task **next = &ginfo->filter_task_hash[key]; | ||
125 | struct filter_task *task; | ||
126 | |||
127 | while (*next) { | ||
128 | if ((*next)->pid == pid) | ||
129 | break; | ||
130 | next = &(*next)->next; | ||
131 | } | ||
132 | if (!*next) | ||
133 | return; | ||
134 | |||
135 | task = *next; | ||
136 | |||
137 | *next = task->next; | ||
138 | |||
139 | g_free(task); | ||
140 | |||
141 | if (--ginfo->filter_task_count) | ||
142 | return; | ||
143 | |||
144 | ginfo->filter_available = 0; | ||
145 | } | ||
146 | |||
147 | static void filter_task_clear(struct graph_info *ginfo) | ||
148 | { | ||
149 | struct filter_task *task, *next;; | ||
150 | gint i; | ||
151 | |||
152 | if (!ginfo->filter_task_count) | ||
153 | return; | ||
154 | |||
155 | for (i = 0; i < FILTER_TASK_HASH_SIZE; i++) { | ||
156 | next = ginfo->filter_task_hash[i]; | ||
157 | if (!next) | ||
158 | continue; | ||
159 | |||
160 | ginfo->filter_task_hash[i] = NULL; | ||
161 | while (next) { | ||
162 | task = next; | ||
163 | next = task->next; | ||
164 | g_free(task); | ||
165 | } | ||
166 | } | ||
167 | |||
168 | ginfo->filter_task_count = 0; | ||
169 | ginfo->filter_available = 0; | ||
170 | } | ||
171 | |||
83 | static void __update_with_backend(struct graph_info *ginfo, | 172 | static void __update_with_backend(struct graph_info *ginfo, |
84 | gint x, gint y, | 173 | gint x, gint y, |
85 | gint width, gint height) | 174 | gint width, gint height) |
@@ -146,16 +235,188 @@ static void clear_last_line(GtkWidget *widget, struct graph_info *ginfo) | |||
146 | update_with_backend(ginfo, x, 0, x+2, widget->allocation.height); | 235 | update_with_backend(ginfo, x, 0, x+2, widget->allocation.height); |
147 | } | 236 | } |
148 | 237 | ||
238 | static struct record * | ||
239 | find_record_on_cpu(struct graph_info *ginfo, gint cpu, guint64 time) | ||
240 | { | ||
241 | struct record *record = NULL; | ||
242 | guint64 offset = 0; | ||
243 | |||
244 | tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); | ||
245 | do { | ||
246 | if (record) { | ||
247 | offset = record->offset; | ||
248 | free_record(record); | ||
249 | } | ||
250 | record = tracecmd_read_data(ginfo->handle, cpu); | ||
251 | } while (record && record->ts <= (time - 1 / ginfo->resolution)); | ||
252 | |||
253 | if (record) { | ||
254 | |||
255 | if (record->ts > (time + 1 / ginfo->resolution) && offset) { | ||
256 | dprintf(3, "old ts = %llu!\n", record->ts); | ||
257 | free_record(record); | ||
258 | record = tracecmd_read_at(ginfo->handle, offset, NULL); | ||
259 | } | ||
260 | } | ||
261 | |||
262 | return record; | ||
263 | } | ||
264 | |||
265 | static void | ||
266 | filter_enable_clicked (gpointer data) | ||
267 | { | ||
268 | struct graph_info *ginfo = data; | ||
269 | |||
270 | ginfo->filter_enabled ^= 1; | ||
271 | } | ||
272 | |||
273 | static void | ||
274 | filter_add_task_clicked (gpointer data) | ||
275 | { | ||
276 | struct graph_info *ginfo = data; | ||
277 | struct filter_task *task; | ||
278 | |||
279 | task = filter_task_find_pid(ginfo, ginfo->filter_task_selected); | ||
280 | |||
281 | if (task) | ||
282 | filter_task_remove_pid(ginfo, task->pid); | ||
283 | else | ||
284 | filter_task_add_pid(ginfo, ginfo->filter_task_selected); | ||
285 | } | ||
286 | |||
287 | static void | ||
288 | filter_clear_tasks_clicked (gpointer data) | ||
289 | { | ||
290 | struct graph_info *ginfo = data; | ||
291 | |||
292 | filter_task_clear(ginfo); | ||
293 | } | ||
294 | |||
295 | static gboolean | ||
296 | do_pop_up(GtkWidget *widget, GdkEventButton *event, gpointer data) | ||
297 | { | ||
298 | struct graph_info *ginfo = data; | ||
299 | static GtkWidget *menu; | ||
300 | static GtkWidget *menu_filter_enable; | ||
301 | static GtkWidget *menu_filter_add_task; | ||
302 | static GtkWidget *menu_filter_clear_tasks; | ||
303 | struct record *record = NULL; | ||
304 | const char *comm; | ||
305 | guint64 time; | ||
306 | gchar *text; | ||
307 | gint pid; | ||
308 | gint len; | ||
309 | gint x, y; | ||
310 | gint cpu; | ||
311 | |||
312 | x = event->x; | ||
313 | y = event->y; | ||
314 | |||
315 | if (!menu) { | ||
316 | menu = gtk_menu_new(); | ||
317 | menu_filter_enable = gtk_menu_item_new_with_label("Enable Filter"); | ||
318 | gtk_widget_show(menu_filter_enable); | ||
319 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_enable); | ||
320 | |||
321 | g_signal_connect_swapped (G_OBJECT (menu_filter_enable), "activate", | ||
322 | G_CALLBACK (filter_enable_clicked), | ||
323 | data); | ||
324 | |||
325 | menu_filter_add_task = gtk_menu_item_new_with_label("Add Task"); | ||
326 | gtk_widget_show(menu_filter_add_task); | ||
327 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_add_task); | ||
328 | |||
329 | g_signal_connect_swapped (G_OBJECT (menu_filter_add_task), "activate", | ||
330 | G_CALLBACK (filter_add_task_clicked), | ||
331 | data); | ||
332 | |||
333 | menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter"); | ||
334 | gtk_widget_show(menu_filter_clear_tasks); | ||
335 | gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks); | ||
336 | |||
337 | g_signal_connect_swapped (G_OBJECT (menu_filter_clear_tasks), "activate", | ||
338 | G_CALLBACK (filter_clear_tasks_clicked), | ||
339 | data); | ||
340 | |||
341 | } | ||
342 | |||
343 | if (ginfo->filter_enabled) | ||
344 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable), | ||
345 | "Disable Filter"); | ||
346 | else | ||
347 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_enable), | ||
348 | "Enable Filter"); | ||
349 | |||
350 | if (ginfo->filter_available) | ||
351 | gtk_widget_set_sensitive(menu_filter_enable, TRUE); | ||
352 | else | ||
353 | gtk_widget_set_sensitive(menu_filter_enable, FALSE); | ||
354 | |||
355 | if (ginfo->filter_task_count) | ||
356 | gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE); | ||
357 | else | ||
358 | gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE); | ||
359 | |||
360 | time = (x / ginfo->resolution) + ginfo->view_start_time; | ||
361 | |||
362 | for (cpu = 0; cpu < ginfo->cpus; cpu++) { | ||
363 | if (y >= (CPU_TOP(cpu) - CPU_GIVE) && | ||
364 | y <= (CPU_BOTTOM(cpu) + CPU_GIVE)) { | ||
365 | record = find_record_on_cpu(ginfo, cpu, time); | ||
366 | break; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | if (record) { | ||
371 | pid = pevent_data_pid(ginfo->pevent, record); | ||
372 | comm = pevent_data_comm_from_pid(ginfo->pevent, pid); | ||
373 | |||
374 | len = strlen(comm) + 50; | ||
375 | |||
376 | text = g_malloc(len); | ||
377 | g_assert(text); | ||
378 | |||
379 | if (filter_task_find_pid(ginfo, pid)) | ||
380 | snprintf(text, len, "Remove %s-%d to filter", comm, pid); | ||
381 | else | ||
382 | snprintf(text, len, "Add %s-%d to filter", comm, pid); | ||
383 | |||
384 | ginfo->filter_task_selected = pid; | ||
385 | |||
386 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task), | ||
387 | text); | ||
388 | g_free(text); | ||
389 | |||
390 | gtk_widget_set_sensitive(menu_filter_add_task, TRUE); | ||
391 | |||
392 | free_record(record); | ||
393 | } else { | ||
394 | gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_add_task), | ||
395 | "Add task to filter"); | ||
396 | gtk_widget_set_sensitive(menu_filter_add_task, FALSE); | ||
397 | } | ||
398 | |||
399 | |||
400 | gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3, | ||
401 | gtk_get_current_event_time()); | ||
402 | |||
403 | |||
404 | return TRUE; | ||
405 | } | ||
406 | |||
149 | static gboolean | 407 | static gboolean |
150 | button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) | 408 | button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) |
151 | { | 409 | { |
152 | struct graph_info *ginfo = data; | 410 | struct graph_info *ginfo = data; |
153 | 411 | ||
412 | if (event->button == 3) | ||
413 | return do_pop_up(widget, event, data); | ||
414 | |||
154 | if (event->button != 1) | 415 | if (event->button != 1) |
155 | return TRUE; | 416 | return TRUE; |
156 | 417 | ||
157 | /* check for double click */ | 418 | /* check for double click */ |
158 | if (event->type==GDK_2BUTTON_PRESS) { | 419 | if (event->type == GDK_2BUTTON_PRESS) { |
159 | if (ginfo->line_active) { | 420 | if (ginfo->line_active) { |
160 | ginfo->line_active = FALSE; | 421 | ginfo->line_active = FALSE; |
161 | clear_last_line(widget, ginfo); | 422 | clear_last_line(widget, ginfo); |
@@ -309,22 +570,10 @@ static void draw_cpu_info(struct graph_info *ginfo, gint cpu, gint x, gint y) | |||
309 | trace_seq_init(&s); | 570 | trace_seq_init(&s); |
310 | 571 | ||
311 | dprintf(3, "start=%zu end=%zu time=%lu\n", ginfo->start_time, ginfo->end_time, time); | 572 | dprintf(3, "start=%zu end=%zu time=%lu\n", ginfo->start_time, ginfo->end_time, time); |
312 | tracecmd_set_cpu_to_timestamp(ginfo->handle, cpu, time); | ||
313 | do { | ||
314 | if (record) { | ||
315 | offset = record->offset; | ||
316 | free_record(record); | ||
317 | } | ||
318 | record = tracecmd_read_data(ginfo->handle, cpu); | ||
319 | } while (record && record->ts <= (time - 1 / ginfo->resolution)); | ||
320 | 573 | ||
321 | if (record) { | 574 | record = find_record_on_cpu(ginfo, cpu, time); |
322 | 575 | ||
323 | if (record->ts > (time + 1 / ginfo->resolution) && offset) { | 576 | if (record) { |
324 | dprintf(3, "old ts = %llu!\n", record->ts); | ||
325 | free_record(record); | ||
326 | record = tracecmd_read_at(ginfo->handle, offset, NULL); | ||
327 | } | ||
328 | 577 | ||
329 | if (!check_sched_switch(ginfo, record, &pid, &comm)) { | 578 | if (!check_sched_switch(ginfo, record, &pid, &comm)) { |
330 | pid = pevent_data_pid(ginfo->pevent, record); | 579 | pid = pevent_data_pid(ginfo->pevent, record); |
@@ -763,10 +1012,19 @@ static gint do_hash(gint val) | |||
763 | return hash; | 1012 | return hash; |
764 | } | 1013 | } |
765 | 1014 | ||
1015 | static gint hash_pid(gint val) | ||
1016 | { | ||
1017 | /* idle always gets black */ | ||
1018 | if (!val) | ||
1019 | return 0; | ||
1020 | |||
1021 | return do_hash(val); | ||
1022 | } | ||
1023 | |||
766 | static void set_color_by_pid(GtkWidget *widget, GdkGC *gc, gint pid) | 1024 | static void set_color_by_pid(GtkWidget *widget, GdkGC *gc, gint pid) |
767 | { | 1025 | { |
768 | GdkColor color; | 1026 | GdkColor color; |
769 | gint hash = do_hash(pid); | 1027 | gint hash = hash_pid(pid); |
770 | static gint last_pid = -1; | 1028 | static gint last_pid = -1; |
771 | 1029 | ||
772 | if (!(hash & 0xffffff) && last_pid != pid) { | 1030 | if (!(hash & 0xffffff) && last_pid != pid) { |
@@ -1317,6 +1575,9 @@ trace_graph_create_with_callbacks(struct tracecmd_input *handle, | |||
1317 | ginfo->start_time = -1ULL; | 1575 | ginfo->start_time = -1ULL; |
1318 | ginfo->end_time = 0; | 1576 | ginfo->end_time = 0; |
1319 | 1577 | ||
1578 | ginfo->filter_task_hash = g_new0(typeof(*ginfo->filter_task_hash), | ||
1579 | FILTER_TASK_HASH_SIZE); | ||
1580 | |||
1320 | ginfo->widget = gtk_hbox_new(FALSE, 0); | 1581 | ginfo->widget = gtk_hbox_new(FALSE, 0); |
1321 | gtk_widget_show(ginfo->widget); | 1582 | gtk_widget_show(ginfo->widget); |
1322 | 1583 | ||
diff --git a/trace-graph.h b/trace-graph.h index a9d7293..a3d3901 100644 --- a/trace-graph.h +++ b/trace-graph.h | |||
@@ -11,6 +11,8 @@ struct graph_callbacks { | |||
11 | graph_select_cb *select; | 11 | graph_select_cb *select; |
12 | }; | 12 | }; |
13 | 13 | ||
14 | struct filter_task; | ||
15 | |||
14 | struct graph_info { | 16 | struct graph_info { |
15 | struct tracecmd_input *handle; | 17 | struct tracecmd_input *handle; |
16 | struct pevent *pevent; | 18 | struct pevent *pevent; |
@@ -47,6 +49,14 @@ struct graph_info { | |||
47 | 49 | ||
48 | struct graph_callbacks *callbacks; /* call back hooks for changes to graph */ | 50 | struct graph_callbacks *callbacks; /* call back hooks for changes to graph */ |
49 | 51 | ||
52 | int filter_enabled; | ||
53 | int filter_available; | ||
54 | |||
55 | struct filter_task **filter_task_hash; | ||
56 | gint filter_task_count; | ||
57 | gint filter_task_selected; | ||
58 | |||
59 | |||
50 | /* Box info for CPU data info window */ | 60 | /* Box info for CPU data info window */ |
51 | gint cpu_data_x; | 61 | gint cpu_data_x; |
52 | gint cpu_data_y; | 62 | gint cpu_data_y; |