aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Rostedt <srostedt@redhat.com>2010-02-18 14:59:30 -0500
committerSteven Rostedt <rostedt@goodmis.org>2010-02-18 14:59:30 -0500
commit5314054f6f5cefc637aeef711657ada14e288fae (patch)
tree4c5fe33fd930c221388d4edf59be6741f8e2c51e
parent1c9d1b374d5c592c550b064f036bcb6b32f7a762 (diff)
trace-graph: Implement task plotting menu
The task plot menu that was added with the CPU plot menu is now functional. You can add and remove task plots with using the menu. Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r--trace-filter.c373
-rw-r--r--trace-filter.h25
-rw-r--r--trace-graph-main.c11
-rw-r--r--trace-graph.h6
-rw-r--r--trace-plot-task.c88
5 files changed, 492 insertions, 11 deletions
diff --git a/trace-filter.c b/trace-filter.c
index 51e0add..e78fe4e 100644
--- a/trace-filter.c
+++ b/trace-filter.c
@@ -62,6 +62,33 @@ struct dialog_helper {
62 gpointer data; 62 gpointer data;
63}; 63};
64 64
65/**
66 * trace_array_add - allocate and add an int to an array.
67 * @array: address of array to allocate
68 * @count: address of the current count of array data
69 * @val: the value to append to the array.
70 *
71 * The first call to this, @array should be uninitialized
72 * and @count should be zero. @array will be malloced and
73 * val will be added to it. If count is greater than zero,
74 * then array will be realloced, and val will be appened
75 * to it.
76 *
77 * This also always ends the array with -1, so the values are
78 * assumed to always be positive.
79 *
80 * @array must be freed with free().
81 */
82void trace_array_add(gint **array, gint *count, gint val)
83{
84 if (*count)
85 *array = realloc(*array, sizeof(val) * (*count + 2));
86 else
87 *array = malloc(sizeof(val) * 2);
88 (*array)[(*count)++] = val;
89 (*array)[*count] = -1;
90}
91
65struct adv_event_filter_helper { 92struct adv_event_filter_helper {
66 trace_adv_filter_cb_func func; 93 trace_adv_filter_cb_func func;
67 GtkTreeView *view; 94 GtkTreeView *view;
@@ -99,15 +126,8 @@ static gint *get_event_ids(GtkTreeView *treeview)
99 ADV_COL_ID, &id, 126 ADV_COL_ID, &id,
100 -1); 127 -1);
101 128
102 if (active) { 129 if (active)
103 if (count) 130 trace_array_add(&ids, &count, id);
104 ids = realloc(ids, sizeof(*ids) * (count + 2));
105 else
106 ids = malloc(sizeof(*ids) * 2);
107 ids[count] = id;
108 count++;
109 ids[count] = -1;
110 }
111 131
112 if (!gtk_tree_model_iter_next(model, &iter)) 132 if (!gtk_tree_model_iter_next(model, &iter))
113 break; 133 break;
@@ -382,6 +402,341 @@ void trace_adv_filter_dialog(struct tracecmd_input *handle,
382 gtk_widget_show_all(dialog); 402 gtk_widget_show_all(dialog);
383} 403}
384 404
405/* --- task list dialog --- */
406
407struct task_helper {
408 trace_task_cb_func func;
409 GtkTreeView *view;
410 gboolean start;
411 gpointer data;
412};
413
414enum {
415 TASK_COL_SELECT,
416 TASK_COL_PID,
417 TASK_COL_COMM,
418 NUM_TASK_COLS,
419};
420
421static void get_tasks(GtkTreeView *treeview,
422 gint **selected,
423 gint **non_selected)
424{
425 GtkTreeModel *model;
426 GtkTreeIter iter;
427 gboolean active;
428 gint *pids = NULL;
429 gint *non_pids = NULL;
430 gint pid;
431 int pid_count = 0;
432 int non_count = 0;
433
434 model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
435 if (!model)
436 return;
437
438 if (!gtk_tree_model_iter_children(model, &iter, NULL))
439 return;
440
441 for (;;) {
442 gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
443 TASK_COL_SELECT, &active,
444 TASK_COL_PID, &pid,
445 -1);
446
447 if (active)
448 trace_array_add(&pids, &pid_count, pid);
449 else
450 trace_array_add(&non_pids, &non_count, pid);
451
452 if (!gtk_tree_model_iter_next(model, &iter))
453 break;
454 }
455
456 *selected = pids;
457 *non_selected = non_pids;
458}
459
460/* Callback for the clicked signal of the task filter button */
461static void
462task_dialog_response (gpointer data, gint response_id)
463{
464 struct dialog_helper *helper = data;
465 struct task_helper *event_helper = helper->data;
466 gint *selected;
467 gint *non_select;
468
469 switch (response_id) {
470 case GTK_RESPONSE_ACCEPT:
471 get_tasks(event_helper->view, &selected, &non_select);
472 event_helper->func(TRUE, selected, non_select, event_helper->data);
473 free(selected);
474 free(non_select);
475 break;
476 case GTK_RESPONSE_REJECT:
477 event_helper->func(FALSE, NULL, NULL, event_helper->data);
478 break;
479 default:
480 break;
481 };
482
483 gtk_widget_destroy(GTK_WIDGET(helper->dialog));
484
485 g_free(event_helper);
486 g_free(helper);
487}
488
489static GtkTreeModel *
490create_task_model(struct tracecmd_input *handle,
491 gint *tasks,
492 gint *selected)
493{
494 GtkTreeStore *treestore;
495 GtkTreeIter iter;
496 struct pevent *pevent;
497 const char *comm;
498 gboolean select;
499 gint *ret;
500 gint *list;
501 gint count;
502 gint i;
503
504 if (!tasks)
505 return NULL;
506
507 pevent = tracecmd_get_pevent(handle);
508
509 treestore = gtk_tree_store_new(NUM_TASK_COLS, G_TYPE_BOOLEAN,
510 G_TYPE_INT, G_TYPE_STRING);
511
512 /* sort our tasks */
513 for (i = 0; tasks[i] >= 0; i++)
514 ;
515 count = i;
516
517 /* Don't assume we can modify the array passed in. */
518 list = malloc_or_die(sizeof(gint) * (count + 1));
519 memcpy(list, tasks, sizeof(gint) * (count + 1));
520 qsort(list, count, sizeof(gint), id_cmp);
521
522 /* Now that we have our own copy, use it instead */
523 tasks = list;
524
525 if (selected) {
526 /* do the same for selected */
527 for (i = 0; selected[i] >= 0; i++)
528 ;
529 count = i;
530 /* count is now the size of selected */
531
532 list = malloc_or_die(sizeof(gint) * (count + 1));
533 memcpy(list, selected, sizeof(gint) * (count + 1));
534 qsort(list, count, sizeof(gint), id_cmp);
535
536 selected = list;
537 }
538
539
540 for (i = 0; tasks[i] >= 0; i++) {
541
542 comm = pevent_data_comm_from_pid(pevent, tasks[i]);
543
544 gtk_tree_store_append(treestore, &iter, NULL);
545
546 select = FALSE;
547
548 if (selected) {
549 ret = bsearch(&tasks[i], selected, count,
550 sizeof(gint), id_cmp);
551 if (ret)
552 select = TRUE;
553 }
554
555 gtk_tree_store_set(treestore, &iter,
556 TASK_COL_SELECT, select,
557 TASK_COL_PID, tasks[i],
558 TASK_COL_COMM, comm,
559 -1);
560 }
561
562 free(tasks);
563 free(selected);
564
565 return GTK_TREE_MODEL(treestore);
566}
567
568static void task_cursor_changed(gpointer data, GtkTreeView *treeview)
569{
570 struct task_helper *event_helper = data;
571 GtkTreeModel *model;
572 GtkTreePath *path;
573 GtkTreeIter iter;
574 gboolean active;
575
576 if (!event_helper->start) {
577 event_helper->start = TRUE;
578 return;
579 }
580
581 model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeview));
582 if (!model)
583 return;
584
585 gtk_tree_view_get_cursor(treeview, &path, NULL);
586 if (!path)
587 return;
588
589 if (!gtk_tree_model_get_iter(model, &iter, path))
590 goto free;
591
592 gtk_tree_model_get(model, &iter,
593 TASK_COL_SELECT, &active,
594 -1);
595
596 active = active ? FALSE : TRUE;
597
598 gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
599 TASK_COL_SELECT, active,
600 -1);
601
602 free:
603 gtk_tree_path_free(path);
604}
605
606static GtkWidget *
607create_task_view(struct tracecmd_input *handle,
608 gint *tasks, gint *selected,
609 struct task_helper *event_helper)
610{
611 GtkTreeViewColumn *col;
612 GtkCellRenderer *renderer;
613 GtkCellRenderer *togrend;
614 GtkWidget *view;
615 GtkTreeModel *model;
616
617 view = gtk_tree_view_new();
618
619 renderer = gtk_cell_renderer_text_new();
620 togrend = gtk_cell_renderer_toggle_new();
621
622
623 /* --- select column --- */
624
625 col = gtk_tree_view_column_new();
626
627 gtk_tree_view_column_set_title(col, "Plot");
628
629 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
630
631 gtk_tree_view_column_pack_start(col, togrend, FALSE);
632 gtk_tree_view_column_add_attribute(col, togrend, "active", TASK_COL_SELECT);
633
634 /* --- pid column --- */
635
636 col = gtk_tree_view_column_new();
637
638 gtk_tree_view_column_set_title(col, "PID");
639
640 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
641
642 gtk_tree_view_column_pack_start(col, renderer, FALSE);
643
644 gtk_tree_view_column_add_attribute(col, renderer, "text", TASK_COL_PID);
645
646
647 /* --- comm column --- */
648
649 col = gtk_tree_view_column_new();
650
651 gtk_tree_view_column_set_title(col, "Task");
652
653 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
654
655 gtk_tree_view_column_pack_start(col, renderer, FALSE);
656
657 gtk_tree_view_column_add_attribute(col, renderer, "text", TASK_COL_COMM);
658
659
660 model = create_task_model(handle, tasks, selected);
661
662 gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
663
664 g_object_unref(model);
665
666 gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
667 GTK_SELECTION_NONE);
668
669 g_signal_connect_swapped (view, "cursor-changed",
670 G_CALLBACK (task_cursor_changed),
671 (gpointer) event_helper);
672
673 return view;
674}
675
676/**
677 * trace_task_dialog - make dialog to select tasks
678 * @handle: the handle to the tracecmd data file
679 * @tasks: array of task pids ending with -1
680 * @selected: tasks from @tasks that should be selected (can be NULL).
681 * Also an array of pids ending with -1
682 * @func: callback function when accept or cancel is selected
683 * @data: data to pass to the function @func
684 */
685void trace_task_dialog(struct tracecmd_input *handle,
686 gint *tasks, gint *selected,
687 trace_task_cb_func func,
688 gpointer data)
689{
690 struct dialog_helper *helper;
691 struct task_helper *event_helper;
692 GtkWidget *dialog;
693 GtkWidget *scrollwin;
694 GtkWidget *view;
695
696 helper = g_malloc(sizeof(*helper));
697 g_assert(helper);
698
699 /* --- Make dialog window --- */
700
701 dialog = gtk_dialog_new_with_buttons("Select Tasks",
702 NULL,
703 GTK_DIALOG_MODAL,
704 "Apply",
705 GTK_RESPONSE_ACCEPT,
706 GTK_STOCK_CANCEL,
707 GTK_RESPONSE_REJECT,
708 NULL);
709
710 event_helper = g_new0(typeof(*event_helper), 1);
711 g_assert(event_helper);
712
713 helper->dialog = dialog;
714 helper->data = event_helper;
715
716 event_helper->func = func;
717 event_helper->data = data;
718 event_helper->start = FALSE;
719
720 /* We can attach the Quit menu item to our exit function */
721 g_signal_connect_swapped (dialog, "response",
722 G_CALLBACK (task_dialog_response),
723 (gpointer) helper);
724
725 scrollwin = gtk_scrolled_window_new(NULL, NULL);
726 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
727 GTK_POLICY_AUTOMATIC,
728 GTK_POLICY_AUTOMATIC);
729 view = create_task_view(handle, tasks, selected, event_helper);
730 event_helper->view = GTK_TREE_VIEW(view);
731 gtk_container_add(GTK_CONTAINER(scrollwin), view);
732 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), scrollwin, TRUE, TRUE, 0);
733
734 gtk_widget_set_size_request(GTK_WIDGET(dialog),
735 DIALOG_WIDTH, DIALOG_HEIGHT);
736
737 gtk_widget_show_all(dialog);
738}
739
385enum { 740enum {
386 COL_EVENT, 741 COL_EVENT,
387 COL_ACTIVE, 742 COL_ACTIVE,
diff --git a/trace-filter.h b/trace-filter.h
index 64d7b47..c68e8f0 100644
--- a/trace-filter.h
+++ b/trace-filter.h
@@ -47,6 +47,24 @@ typedef void (*trace_adv_filter_cb_func)(gboolean accept,
47 gpointer data); 47 gpointer data);
48 48
49/** 49/**
50 * trace_task_cb_func - callback type for task dialog
51 * @accept: TRUE if the accept button was pressed, otherwise FALSE
52 * @selected: list of pids of tasks selected
53 * @non_select: list of pids of tasks not selected
54 * @data: The data given passed in to the event dialog function
55 *
56 * If @accept is FALSE then @selected and @non_select
57 * should be ignored. @data is still valid.
58 *
59 * Both @selected and @non_select may be NULL, if either is not
60 * NULL they will be sorted and end with -1.
61 */
62typedef void (*trace_task_cb_func)(gboolean accept,
63 gint *selected,
64 gint *non_selected,
65 gpointer data);
66
67/**
50 * trace_filter_event_cb_func - callback type for event dialog 68 * trace_filter_event_cb_func - callback type for event dialog
51 * @accept: TRUE if the accept button was pressed, otherwise FALSE 69 * @accept: TRUE if the accept button was pressed, otherwise FALSE
52 * @all_events: TRUE if "All Events" was checked 70 * @all_events: TRUE if "All Events" was checked
@@ -70,6 +88,11 @@ void trace_adv_filter_dialog(struct tracecmd_input *handle,
70 trace_adv_filter_cb_func func, 88 trace_adv_filter_cb_func func,
71 gpointer data); 89 gpointer data);
72 90
91void trace_task_dialog(struct tracecmd_input *handle,
92 gint *tasks, gint *selected,
93 trace_task_cb_func func,
94 gpointer data);
95
73void trace_filter_event_dialog(struct tracecmd_input *handle, 96void trace_filter_event_dialog(struct tracecmd_input *handle,
74 gboolean all_events, 97 gboolean all_events,
75 gchar **systems, 98 gchar **systems,
@@ -115,4 +138,6 @@ void trace_filter_cpu_dialog(gboolean all_cpus, guint64 *cpu_mask_selected, gint
115int str_cmp(const void *a, const void *b); 138int str_cmp(const void *a, const void *b);
116int id_cmp(const void *a, const void *b); 139int id_cmp(const void *a, const void *b);
117 140
141void trace_array_add(gint **array, gint *count, gint val);
142
118#endif /* _TRACE_FILTER_H */ 143#endif /* _TRACE_FILTER_H */
diff --git a/trace-graph-main.c b/trace-graph-main.c
index adc05ca..a6507ee 100644
--- a/trace-graph-main.c
+++ b/trace-graph-main.c
@@ -143,12 +143,19 @@ static void
143plot_tasks_clicked (gpointer data) 143plot_tasks_clicked (gpointer data)
144{ 144{
145 struct graph_info *ginfo = data; 145 struct graph_info *ginfo = data;
146 gboolean all_events = TRUE; 146 gint *selected;
147 gint *tasks;
147 148
148 if (!ginfo->handle) 149 if (!ginfo->handle)
149 return; 150 return;
150 151
151 all_events = ginfo->all_events; 152 tasks = trace_graph_task_list(ginfo);
153 graph_plot_task_plotted(ginfo, &selected);
154
155 trace_task_dialog(ginfo->handle, tasks, selected,
156 graph_plot_task_update_callback, ginfo);
157 free(tasks);
158 free(selected);
152} 159}
153 160
154void trace_graph(int argc, char **argv) 161void trace_graph(int argc, char **argv)
diff --git a/trace-graph.h b/trace-graph.h
index 933bb9d..cb8286c 100644
--- a/trace-graph.h
+++ b/trace-graph.h
@@ -367,5 +367,11 @@ void graph_plot_cpus_update_callback(gboolean accept,
367 367
368/* task plot */ 368/* task plot */
369void graph_plot_task(struct graph_info *ginfo, int pid, int pos); 369void graph_plot_task(struct graph_info *ginfo, int pid, int pos);
370void graph_plot_task_update_callback(gboolean accept,
371 gint *selected,
372 gint *non_select,
373 gpointer data);
374void graph_plot_task_plotted(struct graph_info *ginfo,
375 gint **plotted);
370 376
371#endif /* _TRACE_GRAPH_H */ 377#endif /* _TRACE_GRAPH_H */
diff --git a/trace-plot-task.c b/trace-plot-task.c
index a5b8d79..2aa03d4 100644
--- a/trace-plot-task.c
+++ b/trace-plot-task.c
@@ -21,6 +21,7 @@
21#include <string.h> 21#include <string.h>
22 22
23#include "trace-graph.h" 23#include "trace-graph.h"
24#include "trace-filter.h"
24 25
25#define RED 0xff 26#define RED 0xff
26 27
@@ -611,6 +612,93 @@ static const struct plot_callbacks task_plot_cb = {
611 .destroy = task_plot_destroy 612 .destroy = task_plot_destroy
612}; 613};
613 614
615/**
616 * graph_plot_task_plotted - return what tasks are plotted
617 * @ginfo: the graph info structure
618 * @plotted: returns an allocated array of gints holding the pids.
619 * the last pid is -1, NULL, if none are.
620 *
621 * @plotted must be freed with free() after this is called.
622 */
623void graph_plot_task_plotted(struct graph_info *ginfo,
624 gint **plotted)
625{
626 struct task_plot_info *task_info;
627 struct graph_plot *plot;
628 int count = 0;
629 int i;
630
631 *plotted = NULL;
632 for (i = 0; i < ginfo->plots; i++) {
633 plot = ginfo->plot_array[i];
634 if (plot->type != PLOT_TYPE_TASK)
635 continue;
636 task_info = plot->private;
637 trace_array_add(plotted, &count, task_info->pid);
638 }
639}
640
641void graph_plot_task_update_callback(gboolean accept,
642 gint *selected,
643 gint *non_select,
644 gpointer data)
645{
646 struct graph_info *ginfo = data;
647 struct task_plot_info *task_info;
648 struct graph_plot *plot;
649 gint select_size = 0;
650 gint *ptr;
651 int i;
652
653 if (!accept)
654 return;
655
656 /* The selected and non_select are sorted */
657 if (selected) {
658 for (i = 0; selected[i] >= 0; i++)
659 ;
660 select_size = i;
661 }
662
663 /*
664 * Remove and add task plots.
665 * Go backwards, since removing a plot shifts the
666 * array from current position back.
667 */
668 for (i = ginfo->plots - 1; i >= 0; i--) {
669 plot = ginfo->plot_array[i];
670 if (plot->type != PLOT_TYPE_TASK)
671 continue;
672 /* If non are selected, then remove all */
673 if (!select_size) {
674 trace_graph_plot_remove(ginfo, plot);
675 continue;
676 }
677 task_info = plot->private;
678 ptr = bsearch(&task_info->pid, selected, select_size,
679 sizeof(gint), id_cmp);
680 if (ptr) {
681 /*
682 * This plot plot already exists, remove it
683 * from the selected array.
684 */
685 memmove(ptr, ptr + 1,
686 (unsigned long)(selected + select_size) -
687 (unsigned long)(ptr + 1));
688 select_size--;
689 continue;
690 }
691 /* Remove the plot */
692 trace_graph_plot_remove(ginfo, plot);
693 }
694
695 /* Now add any plots that need to be added */
696 for (i = 0; i < select_size; i++)
697 graph_plot_task(ginfo, selected[i], ginfo->plots);
698
699 trace_graph_refresh(ginfo);
700}
701
614void graph_plot_init_tasks(struct graph_info *ginfo) 702void graph_plot_init_tasks(struct graph_info *ginfo)
615{ 703{
616 struct task_plot_info *task_info; 704 struct task_plot_info *task_info;