diff options
Diffstat (limited to 'trace-graph.c')
-rw-r--r-- | trace-graph.c | 293 |
1 files changed, 277 insertions, 16 deletions
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 | ||