aboutsummaryrefslogtreecommitdiffstats
path: root/trace-graph.c
diff options
context:
space:
mode:
Diffstat (limited to 'trace-graph.c')
-rw-r--r--trace-graph.c293
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
60struct filter_task {
61 struct filter_task *next;
62 gint pid;
63};
64
58static gint ftrace_sched_switch_id = -1; 65static gint ftrace_sched_switch_id = -1;
59static gint event_sched_switch_id = -1; 66static gint event_sched_switch_id = -1;
60 67
61static gint largest_cpu_label = 0; 68static gint largest_cpu_label = 0;
62 69
63static void redraw_pixmap_backend(struct graph_info *ginfo); 70static void redraw_pixmap_backend(struct graph_info *ginfo);
71static gint do_hash(gint val);
64 72
65static void convert_nano(unsigned long long time, unsigned long *sec, 73static 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
91static struct filter_task *
92filter_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
105static 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
121static 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
147static 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
83static void __update_with_backend(struct graph_info *ginfo, 172static 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
238static struct record *
239find_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
265static void
266filter_enable_clicked (gpointer data)
267{
268 struct graph_info *ginfo = data;
269
270 ginfo->filter_enabled ^= 1;
271}
272
273static void
274filter_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
287static void
288filter_clear_tasks_clicked (gpointer data)
289{
290 struct graph_info *ginfo = data;
291
292 filter_task_clear(ginfo);
293}
294
295static gboolean
296do_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
149static gboolean 407static gboolean
150button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) 408button_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
1015static 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
766static void set_color_by_pid(GtkWidget *widget, GdkGC *gc, gint pid) 1024static 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