blob: c9523026aa5939bfc1a8acc7d8592f7d88df4466 [file] [log] [blame]
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <gtk/gtk.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <dlfcn.h>
#include "trace-compat.h"
#include "trace-capture.h"
#include "trace-cmd.h"
#include "trace-local.h"
#include "trace-gui.h"
#include "kernel-shark.h"
#include "kshark-plugin.h"
#include "event-utils.h"
#include "version.h"
#define ___stringify(X) #X
#define __stringify(X) ___stringify(X)
#define DEBUG_LEVEL 0
#if DEBUG_LEVEL > 0
# define dprintf(l, x...) \
do { \
if (l <= DEBUG_LEVEL) \
printf(x); \
} while (0)
#else
# define dprintf(l, x...) do { if (0) printf(x); } while (0)
#endif
#define TRACE_WIDTH 800
#define TRACE_HEIGHT 600
#define default_input_file "trace.dat"
static char *input_file;
void usage(char **argv)
{
char *prog = basename(argv[0]);
printf("Usage: %s\n", prog);
printf(" -h Display this help message\n");
printf(" -v Display version and exit\n");
printf(" -i input_file, default is %s\n", default_input_file);
}
static gboolean display_warnings;
/*
* trace_sync_select_menu - helper function to the syncing of list and graph filters
*
* Creates a pop up dialog with the selections given. The selections will be
* radio buttons to the user. The keep is a value that will be set to the check
* box (default on) if the user wants to keep the selection persistant.
*/
static int trace_sync_select_menu(const gchar *title,
gchar **selections, gboolean *keep)
{
GtkWidget *dialog;
GtkWidget *radio;
GtkWidget *check;
GSList *group;
int result;
int i;
dialog = gtk_dialog_new_with_buttons(title,
NULL,
GTK_DIALOG_MODAL,
"OK", GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
NULL);
radio = gtk_radio_button_new_with_label(NULL, selections[0]);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), radio, TRUE, TRUE, 0);
gtk_widget_show(radio);
group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radio));
for (i = 1; selections[i]; i++) {
radio = gtk_radio_button_new_with_label(group, selections[i]);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), radio, TRUE, TRUE, 0);
gtk_widget_show(radio);
}
check = gtk_check_button_new_with_label("Keep the filters in sync?");
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), check, TRUE, TRUE, 0);
gtk_widget_show(check);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), TRUE);
result = gtk_dialog_run(GTK_DIALOG(dialog));
switch (result) {
case GTK_RESPONSE_ACCEPT:
i = 0;
for (i = 0; group; i++, group = g_slist_next(group)) {
radio = group->data;
if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio)))
break;
}
result = i;
*keep = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(check));
break;
default:
result = -1;
}
gtk_widget_destroy(dialog);
return result;
}
static void update_tree_view_filters(struct shark_info *info,
struct filter_task *task_filter,
struct filter_task *hide_tasks)
{
if (info->list_filter_enabled)
trace_view_update_filters(info->treeview,
task_filter, hide_tasks);
if (filter_task_count(task_filter) ||
filter_task_count(hide_tasks))
info->list_filter_available = 1;
else {
info->list_filter_enabled = 0;
info->list_filter_available = 0;
}
}
/* graph callbacks */
/* convert_nano() and print_time() are copied from trace-graph.c for debugging
purposes, and should be deleted when this is complete (or merged with
trace-graph.c */
static void convert_nano(unsigned long long time, unsigned long *sec,
unsigned long *usec)
{
*sec = time / 1000000000ULL;
*usec = (time / 1000) % 1000000;
}
static void print_time(unsigned long long time)
{
unsigned long sec, usec;
if (!DEBUG_LEVEL)
return;
convert_nano(time, &sec, &usec);
printf("%lu.%06lu", sec, usec);
}
static void ks_graph_select(struct graph_info *ginfo, guint64 cursor)
{
struct graph_callbacks *cbs;
struct shark_info *info;
dprintf(1, "Cursor: ");
print_time(cursor);
dprintf(1, " selected\n");
cbs = trace_graph_get_callbacks(ginfo);
info = container_of(cbs, struct shark_info, graph_cbs);
trace_view_select(info->treeview, cursor);
}
static void ks_graph_filter(struct graph_info *ginfo,
struct filter_task *task_filter,
struct filter_task *hide_tasks)
{
struct graph_callbacks *cbs;
struct shark_info *info;
cbs = trace_graph_get_callbacks(ginfo);
info = container_of(cbs, struct shark_info, graph_cbs);
if (!info->sync_task_filters)
return;
update_tree_view_filters(info, task_filter, hide_tasks);
}
static void free_info(struct shark_info *info)
{
tracecmd_close(info->handle);
trace_graph_free_info(info->ginfo);
filter_task_hash_free(info->list_task_filter);
filter_task_hash_free(info->list_hide_tasks);
kernel_shark_clear_capture(info);
free(info->current_filter);
free(info->ginfo);
free(info);
}
static void update_title(GtkWidget *window, const gchar *file)
{
GString *gstr;
gchar *str;
gchar *gfile;
gfile = g_strdup(file);
gstr = g_string_new("kernelshark");
g_string_append_printf(gstr, "(%s)", basename(gfile));
str = g_string_free(gstr, FALSE);
g_free(gfile);
gtk_window_set_title(GTK_WINDOW(window), str);
g_free(str);
}
static void unsync_task_filters(struct shark_info *info)
{
info->sync_task_filters = 0;
gtk_menu_item_set_label(GTK_MENU_ITEM(info->task_sync_menu),
"Sync Graph and List Task Filters");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_task_menu),
"graph tasks");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_hide_task_menu),
"graph hide tasks");
gtk_widget_show(info->list_task_menu);
gtk_widget_show(info->list_hide_task_menu);
/* The list now uses its own hash */
info->list_task_filter = filter_task_hash_copy(info->ginfo->task_filter);
info->list_hide_tasks = filter_task_hash_copy(info->ginfo->hide_tasks);
}
static void sync_task_filters(struct shark_info *info)
{
info->sync_task_filters = 1;
gtk_menu_item_set_label(GTK_MENU_ITEM(info->task_sync_menu),
"Unsync Graph and List Task Filters");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_task_menu),
"tasks");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_hide_task_menu),
"hide tasks");
gtk_widget_hide(info->list_task_menu);
gtk_widget_hide(info->list_hide_task_menu);
}
static void unsync_event_filters(struct shark_info *info)
{
info->sync_event_filters = 0;
gtk_menu_item_set_label(GTK_MENU_ITEM(info->events_sync_menu),
"Sync Graph and List Event Filters");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_events_menu),
"graph events");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_adv_events_menu),
"graph advanced events");
gtk_widget_show(info->list_events_menu);
gtk_widget_show(info->list_adv_events_menu);
}
static void sync_event_filters(struct shark_info *info)
{
info->sync_event_filters = 1;
gtk_menu_item_set_label(GTK_MENU_ITEM(info->events_sync_menu),
"Unsync Graph and List Event Filters");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_events_menu),
"events");
gtk_menu_item_set_label(GTK_MENU_ITEM(info->graph_adv_events_menu),
"advanced events");
gtk_widget_hide(info->list_events_menu);
gtk_widget_hide(info->list_adv_events_menu);
}
static void alt_warn(const char *fmt, va_list ap)
{
display_warnings = TRUE;
vpr_stat(fmt, ap);
}
/**
* kernelshark_load_file - load a new file into kernelshark
* @info: the kernelshark descriptor
* @file: the file to load
*
* Returns: 0 on success, -1 on error
*/
int kernelshark_load_file(struct shark_info *info, const char *file)
{
struct tracecmd_input *handle;
/*
* Have warnings go into the status bar. If we had any
* warnings, pop up a message at the end.
*/
display_warnings = 0;
pr_stat("\nLoading file %s", file);
trace_dialog_register_alt_warning(alt_warn);
handle = tracecmd_open(file);
trace_dialog_register_alt_warning(NULL);
if (display_warnings) {
errno = 0;
warning("Warnings occurred on loading.\n"
"See display status for details");
}
if (!handle)
return -1;
tracecmd_close(info->handle);
info->handle = handle;
trace_graph_load_handle(info->ginfo, handle);
trace_view_reload(info->treeview, handle, info->spin);
update_title(info->window, file);
return 0;
}
static void
/* Callback for the clicked signal of the Load button */
load_clicked (gpointer data)
{
struct shark_info *info = data;
gchar *filename;
filename = trace_get_file_dialog_filter("Load File", NULL,
TRACE_DIALOG_FILTER_DATA, FALSE);
if (!filename)
return;
kernelshark_load_file(info, filename);
g_free(filename);
}
static GString *get_home_filters_new(void)
{
char *path = getenv("HOME");
GString *str;
str = g_string_new(path);
g_string_append(str, "/.trace-cmd/filters/");
return str;
}
static int create_home_filters(void)
{
char *path = getenv("HOME");
GString *str;
struct stat st;
int ret;
str = g_string_new(path);
g_string_append(str, "/.trace-cmd");
ret = stat(str->str, &st);
if (ret < 0) {
ret = mkdir(str->str, 0755);
if (ret < 0) {
warning("Can not create %s", str->str);
goto out;
}
}
g_string_append(str, "/filters");
ret = stat(str->str, &st);
if (ret < 0) {
ret = mkdir(str->str, 0755);
if (ret < 0) {
warning("Can not create %s", str->str);
goto out;
}
}
ret = 0;
out:
g_string_free(str, TRUE);
return ret;
}
static void load_filter(struct shark_info *info, const char *filename)
{
struct graph_info *ginfo = info->ginfo;
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
GtkTreeModel *model;
TraceViewStore *store;
struct tracecmd_xml_handle *handle;
struct filter_task *task_filter;
struct filter_task *hide_tasks;
struct event_filter *event_filter;
int ret;
handle = tracecmd_xml_open(filename);
if (!handle) {
warning("Could not open %s", filename);
return;
}
/* Unsync the list and graph filters */
if (info->sync_task_filters)
unsync_task_filters(info);
if (info->sync_event_filters)
unsync_event_filters(info);
ret = tracecmd_xml_system_exists(handle,
"GraphTaskFilter");
if (ret) {
filter_task_clear(ginfo->task_filter);
filter_task_clear(ginfo->hide_tasks);
trace_filter_load_filters(handle,
"GraphTaskFilter",
ginfo->task_filter,
ginfo->hide_tasks);
trace_graph_refresh_filters(ginfo);
}
ret = tracecmd_xml_system_exists(handle,
"ListTaskFilter");
if (ret) {
task_filter = info->list_task_filter;
hide_tasks = info->list_hide_tasks;
filter_task_clear(task_filter);
filter_task_clear(hide_tasks);
trace_filter_load_filters(handle,
"ListTaskFilter",
task_filter,
hide_tasks);
update_tree_view_filters(info, task_filter, hide_tasks);
}
trace_graph_load_filters(ginfo, handle);
ret = trace_view_load_filters(handle, trace_tree);
tracecmd_xml_close(handle);
/*
* If the events or tasks filters are the same for both
* the list and graph, then sync them back.
*/
if (filter_task_compare(ginfo->task_filter,
info->list_task_filter) &&
filter_task_compare(ginfo->hide_tasks,
info->list_hide_tasks))
sync_task_filters(info);
model = gtk_tree_view_get_model(trace_tree);
if (!model)
return;
store = TRACE_VIEW_STORE(model);
event_filter = trace_view_store_get_event_filter(store);
if (pevent_filter_compare(event_filter, ginfo->event_filter))
sync_event_filters(info);
}
static void load_filter_clicked(GtkMenuItem *item, gpointer data)
{
struct shark_info *info = data;
const char *name;
GString *path;
name = gtk_menu_item_get_label(item);
path = get_home_filters_new();
g_string_append_printf(path, "/%s.ksf", name);
load_filter(info, path->str);
g_string_free(path, TRUE);
free(info->current_filter);
info->current_filter = strdup(name);
}
/* Callback for the clicked signal of the Load Filters button */
static void
import_filters_clicked (gpointer data)
{
struct shark_info *info = data;
gchar *filename;
filename = trace_get_file_dialog_filter("Load Filters", NULL,
TRACE_DIALOG_FILTER_FILTER, FALSE);
if (!filename)
return;
load_filter(info, filename);
}
static void save_filters(struct shark_info *info, const char *filename)
{
struct graph_info *ginfo = info->ginfo;
struct tracecmd_xml_handle *handle;
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
struct filter_task *task_filter;
struct filter_task *hide_tasks;
handle = tracecmd_xml_create(filename, VERSION_STRING);
if (!handle) {
warning("Could not create %s", filename);
return;
}
trace_view_save_filters(handle, trace_tree);
trace_graph_save_filters(ginfo, handle);
trace_filter_save_filters(handle,
"GraphTaskFilter",
ginfo->task_filter,
ginfo->hide_tasks);
if (info->sync_task_filters) {
task_filter = ginfo->task_filter;
hide_tasks = ginfo->hide_tasks;
} else {
task_filter = info->list_task_filter;
hide_tasks = info->list_hide_tasks;
}
trace_filter_save_filters(handle,
"ListTaskFilter",
task_filter, hide_tasks);
tracecmd_xml_close(handle);
}
/* Callback for the clicked signal of the Save Filters button */
static void
export_filters_clicked (gpointer data)
{
struct shark_info *info = data;
gchar *filename;
filename = trace_get_file_dialog_filter("Save Filters", "Save",
TRACE_DIALOG_FILTER_FILTER, TRUE);
if (!filename)
return;
save_filters(info, filename);
g_free(filename);
}
static void update_load_filter(struct shark_info *info)
{
struct dirent *dent;
struct stat st;
DIR *dir;
GtkWidget *menu;
GtkWidget *sub_item;
GString *path;
int ret;
path = get_home_filters_new();
menu = gtk_menu_new();
ret = stat(path->str, &st);
if (ret < 0 || !S_ISDIR(st.st_mode))
goto update_rest;
dir = opendir(path->str);
if (!dir)
goto update_rest;
while ((dent = readdir(dir))) {
const char *name = dent->d_name;
GString *file;
gchar *item;
if (strcmp(name, ".") == 0 ||
strcmp(name, "..") == 0)
continue;
if (strcmp(name + strlen(name) - 4, ".ksf") != 0)
continue;
file = g_string_new(path->str);
g_string_append_printf(file, "/%s", name);
/* Save the file name but remove the .kss extention */
item = g_strdup(name);
item[strlen(name) - 4] = 0;
sub_item = gtk_menu_item_new_with_label(item);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item);
gtk_widget_show(sub_item);
g_signal_connect(G_OBJECT (sub_item), "activate",
G_CALLBACK (load_filter_clicked),
(gpointer) info);
g_free(item);
g_string_free(file, TRUE);
}
update_rest:
g_string_free(path, TRUE);
/* --- File - Load Filter - Filters --- */
sub_item = gtk_menu_item_new_with_label("Import Filter");
g_signal_connect_swapped (G_OBJECT (sub_item), "activate",
G_CALLBACK (import_filters_clicked),
(gpointer) info);
/* Add them to the menu */
gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item);
/* We do need to show menu items */
gtk_widget_show(sub_item);
gtk_menu_item_set_submenu(GTK_MENU_ITEM (info->load_filter_menu), menu);
}
/* Callback for the clicked signal of the Save Filters button */
static void
save_filter_clicked (gpointer data)
{
struct shark_info *info = data;
struct stat st;
GtkWidget *dialog;
GtkWidget *hbox;
GtkWidget *label;
GtkWidget *entry;
GString *file;
const char *name;
gint result;
int ret;
dialog = gtk_dialog_new_with_buttons("Save Filter",
NULL,
GTK_DIALOG_MODAL,
GTK_STOCK_OK,
GTK_RESPONSE_ACCEPT,
GTK_STOCK_CANCEL,
GTK_RESPONSE_REJECT,
NULL);
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
label = gtk_label_new("Filter Name: ");
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
gtk_widget_show(label);
entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox), entry, FALSE, FALSE, 0);
gtk_widget_show(entry);
if (info->current_filter)
gtk_entry_set_text(GTK_ENTRY(entry), info->current_filter);
again:
result = gtk_dialog_run(GTK_DIALOG(dialog));
switch (result) {
case GTK_RESPONSE_ACCEPT:
name = gtk_entry_get_text(GTK_ENTRY(entry));
if (!name || !has_text(name)) {
warning("Must enter a name");
goto again;
}
/* Make sure home settings exists */
if (create_home_filters() < 0)
break;
file = get_home_filters_new();
g_string_append_printf(file, "/%s.ksf", name);
ret = stat(file->str, &st);
if (ret >= 0) {
ret = trace_dialog(GTK_WINDOW(dialog), TRACE_GUI_ASK,
"The Filter '%s' already exists.\n"
"Are you sure you want to replace it",
name);
if (ret == GTK_RESPONSE_NO) {
g_string_free(file, TRUE);
goto again;
}
}
save_filters(info, file->str);
free(info->current_filter);
info->current_filter = strdup(name);
update_load_filter(info);
g_string_free(file, TRUE);
break;
case GTK_RESPONSE_REJECT:
break;
default:
break;
};
gtk_widget_destroy(dialog);
}
/* Callback for the clicked signal of the Exit button */
static void
exit_clicked (gpointer data)
{
struct shark_info *info = data;
gtk_widget_destroy (info->window); /* the user data points to the main window */
free_info(info);
gtk_main_quit ();
}
/* Callback for the delete_event signal of the main application window */
static gint
delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
struct shark_info *info = data;
gtk_widget_destroy (widget); /* destroy the main window */
free_info(info);
gtk_main_quit ();
return TRUE;
}
/* Callback for the clicked signal of the tasks sync filter button */
static void
sync_task_filter_clicked (GtkWidget *subitem, gpointer data)
{
struct shark_info *info = data;
struct filter_task *task_filter;
struct filter_task *hide_tasks;
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
GtkTreeModel *model;
gboolean keep;
gchar *selections[] = { "Sync List Filter with Graph Filter",
"Sync Graph Filter with List Filter",
NULL };
int result;
if (info->sync_task_filters) {
/* Separate the List and Graph filters */
unsync_task_filters(info);
return;
}
model = gtk_tree_view_get_model(trace_tree);
if (!model)
return;
/* If they are already equal, then just perminently sync them */
if (filter_task_compare(info->ginfo->task_filter,
info->list_task_filter) &&
filter_task_compare(info->ginfo->hide_tasks,
info->list_hide_tasks))
result = 2;
else
/* Ask user which way to sync */
result = trace_sync_select_menu("Sync Task Filters",
selections, &keep);
switch (result) {
case 0:
/* Sync List Filter with Graph Filter */
filter_task_hash_free(info->list_task_filter);
filter_task_hash_free(info->list_hide_tasks);
info->list_task_filter = NULL;
info->list_hide_tasks = NULL;
task_filter = info->ginfo->task_filter;
hide_tasks = info->ginfo->hide_tasks;
if (!keep) {
info->list_task_filter = filter_task_hash_copy(task_filter);
info->list_hide_tasks = filter_task_hash_copy(hide_tasks);
}
update_tree_view_filters(info, task_filter, hide_tasks);
break;
case 1:
/* Sync Graph Filter with List Filter */
trace_graph_update_filters(info->ginfo,
info->list_task_filter,
info->list_hide_tasks);
if (keep) {
filter_task_hash_free(info->list_task_filter);
filter_task_hash_free(info->list_hide_tasks);
info->list_task_filter = NULL;
info->list_hide_tasks = NULL;
}
break;
case 2:
keep = 1;
break;
default:
keep = 0;
}
if (keep)
sync_task_filters(info);
}
/* Callback for the clicked signal of the events sync filter button */
static void
sync_events_filter_clicked (GtkWidget *subitem, gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
struct event_filter *event_filter;
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
GtkTreeModel *model;
TraceViewStore *store;
gboolean keep;
gboolean all_events;
gchar *selections[] = { "Sync List Filter with Graph Filter",
"Sync Graph Filter with List Filter",
NULL };
int result;
if (info->sync_event_filters) {
/* Separate the List and Graph filters */
unsync_event_filters(info);
return;
}
model = gtk_tree_view_get_model(trace_tree);
if (!model)
return;
store = TRACE_VIEW_STORE(model);
event_filter = trace_view_store_get_event_filter(store);
/* If they are already equal, then just perminently sync them */
if (pevent_filter_compare(event_filter, ginfo->event_filter))
result = 2;
else
/* Ask user which way to sync */
result = trace_sync_select_menu("Sync Event Filters",
selections, &keep);
switch (result) {
case 0:
/* Sync List Filter with Graph Filter */
all_events = ginfo->all_events;
trace_view_copy_filter(info->treeview, all_events,
ginfo->event_filter);
break;
case 1:
/* Sync Graph Filter with List Filter */
all_events = trace_view_store_get_all_events_enabled(store);
trace_graph_copy_filter(info->ginfo, all_events,
event_filter);
break;
case 2:
keep = 1;
break;
default:
keep = 0;
}
if (keep)
sync_event_filters(info);
}
static void filter_list_enable_clicked (gpointer data);
static void
__update_list_task_filter_callback(struct shark_info *info,
gboolean accept,
gint *selected,
struct filter_task *task_filter)
{
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
GtkTreeModel *model;
int i;
if (!accept)
return;
model = gtk_tree_view_get_model(trace_tree);
if (!model)
return;
filter_task_clear(task_filter);
if (selected) {
for (i = 0; selected[i] >= 0; i++)
filter_task_add_pid(task_filter, selected[i]);
}
update_tree_view_filters(info, info->list_task_filter, info->list_hide_tasks);
/*
* The menu filters always enable the filters.
*/
if (info->list_filter_available && !info->list_filter_enabled)
filter_list_enable_clicked(info);
}
static void
update_list_task_filter_callback(gboolean accept,
gint *selected,
gint *non_select,
gpointer data)
{
struct shark_info *info = data;
__update_list_task_filter_callback(info, accept, selected,
info->list_task_filter);
}
static void
update_list_hide_task_filter_callback(gboolean accept,
gint *selected,
gint *non_select,
gpointer data)
{
struct shark_info *info = data;
__update_list_task_filter_callback(info, accept, selected,
info->list_hide_tasks);
}
/* Callback for the clicked signal of the List Tasks filter button */
static void
__list_tasks_clicked (struct shark_info *info,
struct filter_task *task_filter,
trace_task_cb_func func)
{
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
struct graph_info *ginfo = info->ginfo;
GtkTreeModel *model;
gint *selected;
gint *tasks;
if (!ginfo->handle)
return;
model = gtk_tree_view_get_model(trace_tree);
if (!model)
return;
tasks = trace_graph_task_list(ginfo);
selected = filter_task_pids(task_filter);
trace_task_dialog(info->handle, tasks, selected, func, info);
free(tasks);
free(selected);
}
static void
list_tasks_clicked (gpointer data)
{
struct shark_info *info = data;
__list_tasks_clicked(info, info->list_task_filter,
update_list_task_filter_callback);
}
static void
list_hide_tasks_clicked (gpointer data)
{
struct shark_info *info = data;
__list_tasks_clicked(info, info->list_hide_tasks,
update_list_hide_task_filter_callback);
}
static void
__update_graph_task_filter_callback(struct shark_info *info,
gboolean accept,
gint *selected,
struct filter_task *task_filter)
{
struct graph_info *ginfo = info->ginfo;
int i;
if (!accept)
return;
filter_task_clear(task_filter);
if (selected) {
for (i = 0; selected[i] >= 0; i++)
filter_task_add_pid(task_filter, selected[i]);
}
trace_graph_refresh_filters(ginfo);
/*
* The menu filters always enable the filters.
*/
if (ginfo->filter_available) {
if (!ginfo->filter_enabled)
trace_graph_filter_toggle(info->ginfo);
if (info->sync_task_filters && !info->list_filter_enabled)
filter_list_enable_clicked(info);
}
}
static void
update_graph_task_filter_callback(gboolean accept,
gint *selected,
gint *non_select,
gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
__update_graph_task_filter_callback(info, accept, selected,
ginfo->task_filter);
}
static void
update_graph_hide_task_filter_callback(gboolean accept,
gint *selected,
gint *non_select,
gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
__update_graph_task_filter_callback(info, accept, selected,
ginfo->hide_tasks);
}
/* Callback for the clicked signal of the Tasks filter button */
static void
__graph_tasks_clicked (struct shark_info *info,
struct filter_task *task_filter,
trace_task_cb_func func)
{
struct graph_info *ginfo = info->ginfo;
gint *selected;
gint *tasks;
if (!ginfo->handle)
return;
tasks = trace_graph_task_list(ginfo);
selected = filter_task_pids(task_filter);
trace_task_dialog(ginfo->handle, tasks, selected, func, info);
free(tasks);
free(selected);
}
static void
graph_tasks_clicked (gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
__graph_tasks_clicked(info, ginfo->task_filter,
update_graph_task_filter_callback);
}
static void
graph_hide_tasks_clicked (gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
__graph_tasks_clicked(info, ginfo->hide_tasks,
update_graph_hide_task_filter_callback);
}
/* Callback for the clicked signal of the Events filter button */
static void
list_events_clicked (gpointer data)
{
struct shark_info *info = data;
struct event_filter *event_filter;
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
GtkTreeModel *model;
TraceViewStore *store;
gboolean all_events;
model = gtk_tree_view_get_model(trace_tree);
if (!model)
return;
store = TRACE_VIEW_STORE(model);
all_events = trace_view_store_get_all_events_enabled(store);
event_filter = trace_view_store_get_event_filter(store);
/*
* This menu is not available when in sync, so we
* can call the treeview callback directly.
*/
trace_filter_event_filter_dialog(store->handle, event_filter,
all_events,
trace_view_event_filter_callback,
info->treeview);
}
static void
graph_event_filter_callback(gboolean accept,
gboolean all_events,
gchar **systems,
gint *events,
gpointer data)
{
struct shark_info *info = data;
trace_graph_event_filter_callback(accept, all_events,
systems, events,
info->ginfo);
if (info->sync_event_filters)
trace_view_event_filter_callback(accept, all_events, systems,
events, info->treeview);
}
static void
graph_events_clicked (gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
gboolean all_events;
all_events = ginfo->all_events;
trace_filter_event_filter_dialog(info->handle,
ginfo->event_filter,
all_events,
graph_event_filter_callback,
info);
}
/* Callback for the clicked signal of the List advanced filter button */
static void
adv_list_filter_clicked (gpointer data)
{
struct shark_info *info = data;
struct event_filter *event_filter;
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
GtkTreeModel *model;
TraceViewStore *store;
model = gtk_tree_view_get_model(trace_tree);
if (!model)
return;
store = TRACE_VIEW_STORE(model);
event_filter = trace_view_store_get_event_filter(store);
/*
* This menu is not available when in sync, so we
* can call the treeview callback directly.
*/
trace_adv_filter_dialog(store->handle, event_filter,
trace_view_adv_filter_callback, trace_tree);
}
static void
graph_adv_filter_callback(gboolean accept,
const gchar *text,
gint *event_ids,
gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
trace_graph_adv_filter_callback(accept, text, event_ids, ginfo);
if (info->sync_event_filters)
trace_view_adv_filter_callback(accept, text, event_ids,
info->treeview);
}
/* Callback for the clicked signal of the Graph advanced filter button */
static void
adv_graph_filter_clicked (gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
trace_adv_filter_dialog(ginfo->handle, ginfo->event_filter,
graph_adv_filter_callback, info);
}
/* Callback for the clicked signal of the CPUs filter button */
static void
cpus_clicked (gpointer data)
{
struct shark_info *info = data;
GtkTreeView *trace_tree = GTK_TREE_VIEW(info->treeview);
TraceViewStore *store;
gboolean all_cpus;
guint64 *cpu_mask;
if (!info->handle)
return;
store = TRACE_VIEW_STORE(gtk_tree_view_get_model(trace_tree));
all_cpus = trace_view_store_get_all_cpus(store);
cpu_mask = trace_view_store_get_cpu_mask(store);
trace_filter_cpu_dialog(all_cpus, cpu_mask,
trace_view_store_get_cpus(store),
trace_view_cpu_filter_callback, trace_tree);
}
/* Callback for the clicked signal of the plot CPUs button */
static void
plot_cpu_clicked (gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
gboolean all_cpus;
guint64 *cpu_mask;
if (!ginfo->handle)
return;
graph_plot_cpus_plotted(ginfo, &all_cpus, &cpu_mask);
trace_filter_cpu_dialog(all_cpus, cpu_mask, ginfo->cpus,
graph_plot_cpus_update_callback, ginfo);
g_free(cpu_mask);
}
/* Callback for the clicked signal of the plot tasks button */
static void
plot_tasks_clicked (gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
gint *selected;
gint *tasks;
if (!ginfo->handle)
return;
tasks = trace_graph_task_list(ginfo);
graph_plot_task_plotted(ginfo, &selected);
trace_task_dialog(ginfo->handle, tasks, selected,
graph_plot_task_update_callback, ginfo);
free(tasks);
free(selected);
}
/* Callback for the clicked signal of the help contents button */
static void
help_content_clicked (gpointer data)
{
struct shark_info *info = data;
GError *error = NULL;
gchar *link;
link = "file://" __stringify(HELP_DIR) "/index.html";
trace_show_help(info->window, link, &error);
}
/* Callback for the clicked signal of the help about button */
static void
help_about_clicked (gpointer data)
{
struct shark_info *info = data;
trace_dialog(GTK_WINDOW(info->window), TRACE_GUI_INFO,
"KernelShark\n\n"
"version %s\n\n"
"Copyright (C) 2009, 2010 Red Hat Inc\n\n"
" Author: Steven Rostedt <srostedt@redhat.com>",
VERSION_STRING);
}
static void graph_follows_tree(struct shark_info *info,
GtkTreeView *treeview,
GtkTreePath *path)
{
TraceViewRecord *rec;
GtkTreeModel *model;
gchar *spath;
guint64 time;
gint row;
model = gtk_tree_view_get_model(treeview);
/* This can be called when we NULL out the model */
if (!model)
return;
spath = gtk_tree_path_to_string(path);
row = atoi(spath);
g_free(spath);
rec = trace_view_store_get_visible_row(TRACE_VIEW_STORE(model), row);
time = rec->timestamp;
trace_graph_select_by_time(info->ginfo, time);
}
static void row_double_clicked(GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *col,
gpointer data)
{
struct shark_info *info = data;
graph_follows_tree(info, treeview, path);
}
static void cursor_changed(GtkTreeView *treeview,
gpointer data)
{
struct shark_info *info = data;
GtkTreePath *path;
if (!info->graph_follows)
return;
gtk_tree_view_get_cursor(treeview, &path, NULL);
if (!path)
return;
graph_follows_tree(info, treeview, path);
gtk_tree_path_free(path);
}
static void
filter_graph_enable_clicked (gpointer data)
{
struct shark_info *info = data;
trace_graph_filter_toggle(info->ginfo);
}
static void
filter_list_enable_clicked (gpointer data)
{
struct shark_info *info = data;
struct filter_task *task_filter;
struct filter_task *hide_tasks;
info->list_filter_enabled ^= 1;
if (info->sync_task_filters) {
task_filter = info->ginfo->task_filter;
hide_tasks = info->ginfo->hide_tasks;
} else {
task_filter = info->list_task_filter;
hide_tasks = info->list_hide_tasks;
}
if (info->list_filter_enabled)
trace_view_update_filters(info->treeview,
task_filter, hide_tasks);
else
trace_view_update_filters(info->treeview, NULL, NULL);
}
static void
filter_update_list_filter(struct shark_info *info,
struct filter_task *filter,
struct filter_task *other_filter)
{
struct filter_task_item *task;
int pid = info->selected_task;
task = filter_task_find_pid(filter, pid);
if (task) {
filter_task_remove_pid(filter, pid);
if (!filter_task_count(filter) &&
!filter_task_count(other_filter)) {
info->list_filter_enabled = 0;
info->list_filter_available = 0;
}
} else {
filter_task_add_pid(filter, pid);
info->list_filter_available = 1;
}
}
static void
filter_add_task_clicked (gpointer data)
{
struct shark_info *info = data;
int pid = info->selected_task;
if (info->sync_task_filters) {
trace_graph_filter_add_remove_task(info->ginfo, pid);
return;
}
filter_update_list_filter(info, info->list_task_filter, info->list_hide_tasks);
trace_view_update_filters(info->treeview,
info->list_task_filter, info->list_hide_tasks);
}
static void
filter_graph_add_task_clicked (gpointer data)
{
struct shark_info *info = data;
trace_graph_filter_add_remove_task(info->ginfo, info->selected_task);
}
static void
filter_hide_task_clicked (gpointer data)
{
struct shark_info *info = data;
if (info->sync_task_filters) {
trace_graph_filter_hide_show_task(info->ginfo, info->selected_task);
return;
}
filter_update_list_filter(info, info->list_hide_tasks, info->list_task_filter);
trace_view_update_filters(info->treeview,
info->list_task_filter, info->list_hide_tasks);
}
static void
handle_display_event_clicked (gpointer data, gboolean raw)
{
struct shark_info *info = data;
struct pevent_record *record;
struct pevent *pevent;
TraceViewRecord *vrec;
GtkTreeModel *model;
guint64 offset;
gint row;
gint cpu;
row = trace_view_get_selected_row(GTK_WIDGET(info->treeview));
if (row < 0)
return;
model = gtk_tree_view_get_model(GTK_TREE_VIEW(info->treeview));
vrec = trace_view_store_get_row(TRACE_VIEW_STORE(model), row);
offset = vrec->offset;
record = tracecmd_read_at(info->handle, offset, &cpu);
if (!record)
return;
pevent = tracecmd_get_pevent(info->handle);
trace_show_record_dialog(GTK_WINDOW(info->window),
pevent, record, raw);
free_record(record);
}
static void
display_event_clicked (gpointer data)
{
handle_display_event_clicked(data, FALSE);
}
static void
display_raw_event_clicked (gpointer data)
{
handle_display_event_clicked(data, TRUE);
}
static void
filter_graph_hide_task_clicked (gpointer data)
{
struct shark_info *info = data;
trace_graph_filter_hide_show_task(info->ginfo, info->selected_task);
}
static void
filter_clear_tasks_clicked (gpointer data)
{
struct shark_info *info = data;
if (info->sync_task_filters) {
trace_graph_clear_tasks(info->ginfo);
return;
}
filter_task_clear(info->list_task_filter);
filter_task_clear(info->list_hide_tasks);
trace_view_update_filters(info->treeview, NULL, NULL);
info->list_filter_available = 0;
info->list_filter_enabled = 0;
}
static void
filter_graph_clear_tasks_clicked (gpointer data)
{
struct shark_info *info = data;
trace_graph_clear_tasks(info->ginfo);
}
static void graph_check_toggle(gpointer data, GtkWidget *widget)
{
struct shark_info *info = data;
info->graph_follows = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
}
static void set_menu_label(GtkWidget *menu, const char *comm, int pid,
const char *fmt)
{
int len = strlen(comm) + strlen(fmt) + 50;
char text[len];
snprintf(text, len, fmt, comm, pid);
gtk_menu_item_set_label(GTK_MENU_ITEM(menu), text);
}
static gboolean
do_tree_popup(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
struct shark_info *info = data;
struct graph_info *ginfo = info->ginfo;
static GtkWidget *menu;
static GtkWidget *menu_filter_graph_enable;
static GtkWidget *menu_filter_list_enable;
static GtkWidget *menu_filter_add_task;
static GtkWidget *menu_filter_hide_task;
static GtkWidget *menu_filter_clear_tasks;
static GtkWidget *menu_filter_graph_add_task;
static GtkWidget *menu_filter_graph_hide_task;
static GtkWidget *menu_filter_graph_clear_tasks;
static GtkWidget *menu_display_event;
static GtkWidget *menu_display_raw_event;
struct pevent_record *record;
TraceViewRecord *vrec;
GtkTreeModel *model;
const char *comm;
gint pid;
guint64 offset;
gint row;
gint cpu;
if (!menu) {
menu = gtk_menu_new();
menu_filter_graph_enable = gtk_menu_item_new_with_label("Enable Graph Task Filter");
gtk_widget_show(menu_filter_graph_enable);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_enable);
g_signal_connect_swapped (G_OBJECT (menu_filter_graph_enable), "activate",
G_CALLBACK (filter_graph_enable_clicked),
data);
menu_filter_list_enable = gtk_menu_item_new_with_label("Enable List Task Filter");
gtk_widget_show(menu_filter_list_enable);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_list_enable);
g_signal_connect_swapped (G_OBJECT (menu_filter_list_enable), "activate",
G_CALLBACK (filter_list_enable_clicked),
data);
menu_filter_add_task = gtk_menu_item_new_with_label("Add Task");
gtk_widget_show(menu_filter_add_task);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_add_task);
g_signal_connect_swapped (G_OBJECT (menu_filter_add_task), "activate",
G_CALLBACK (filter_add_task_clicked),
data);
menu_filter_graph_add_task = gtk_menu_item_new_with_label("Add Task to Graph");
gtk_widget_show(menu_filter_graph_add_task);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_add_task);
g_signal_connect_swapped (G_OBJECT (menu_filter_graph_add_task), "activate",
G_CALLBACK (filter_graph_add_task_clicked),
data);
menu_filter_hide_task = gtk_menu_item_new_with_label("Hide Task");
gtk_widget_show(menu_filter_hide_task);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_hide_task);
g_signal_connect_swapped (G_OBJECT (menu_filter_hide_task), "activate",
G_CALLBACK (filter_hide_task_clicked),
data);
menu_filter_graph_hide_task = gtk_menu_item_new_with_label("Hide Task from Graph");
gtk_widget_show(menu_filter_graph_hide_task);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_hide_task);
g_signal_connect_swapped (G_OBJECT (menu_filter_graph_hide_task), "activate",
G_CALLBACK (filter_graph_hide_task_clicked),
data);
menu_filter_clear_tasks = gtk_menu_item_new_with_label("Clear Task Filter");
gtk_widget_show(menu_filter_clear_tasks);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_clear_tasks);
g_signal_connect_swapped (G_OBJECT (menu_filter_clear_tasks), "activate",
G_CALLBACK (filter_clear_tasks_clicked),
data);
menu_filter_graph_clear_tasks =
gtk_menu_item_new_with_label("Clear Graph Task Filter");
gtk_widget_show(menu_filter_graph_clear_tasks);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_filter_graph_clear_tasks);
g_signal_connect_swapped (G_OBJECT (menu_filter_graph_clear_tasks), "activate",
G_CALLBACK (filter_graph_clear_tasks_clicked),
data);
menu_display_event = gtk_menu_item_new_with_label("Display event");
gtk_widget_show(menu_display_event);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_display_event);
g_signal_connect_swapped (G_OBJECT (menu_display_event), "activate",
G_CALLBACK (display_event_clicked),
data);
menu_display_raw_event = gtk_menu_item_new_with_label("Display raw event");
gtk_widget_show(menu_display_raw_event);
gtk_menu_shell_append(GTK_MENU_SHELL (menu), menu_display_raw_event);
g_signal_connect_swapped (G_OBJECT (menu_display_raw_event), "activate",
G_CALLBACK (display_raw_event_clicked),
data);
}
row = trace_view_get_selected_row(GTK_WIDGET(info->treeview));
if (row >= 0) {
model = gtk_tree_view_get_model(GTK_TREE_VIEW(info->treeview));
vrec = trace_view_store_get_row(TRACE_VIEW_STORE(model), row);
offset = vrec->offset;
record = tracecmd_read_at(info->handle, offset, &cpu);
if (record) {
pid = pevent_data_pid(ginfo->pevent, record);
comm = pevent_data_comm_from_pid(ginfo->pevent, pid);
if (info->sync_task_filters) {
if (trace_graph_filter_task_find_pid(ginfo, pid))
set_menu_label(menu_filter_add_task, comm, pid,
"Remove %s-%d from filters");
else
set_menu_label(menu_filter_add_task, comm, pid,
"Add %s-%d to filters");
if (trace_graph_hide_task_find_pid(ginfo, pid))
set_menu_label(menu_filter_hide_task, comm, pid,
"Show %s-%d");
else
set_menu_label(menu_filter_hide_task, comm, pid,
"Hide %s-%d");
gtk_widget_hide(menu_filter_graph_add_task);
gtk_widget_hide(menu_filter_graph_hide_task);
} else {
if (filter_task_find_pid(info->list_task_filter, pid))
set_menu_label(menu_filter_add_task, comm, pid,
"Remove %s-%d from List filter");
else
set_menu_label(menu_filter_add_task, comm, pid,
"Add %s-%d to List filter");
if (filter_task_find_pid(info->list_hide_tasks, pid))
set_menu_label(menu_filter_hide_task, comm, pid,
"Show %s-%d in List");
else
set_menu_label(menu_filter_hide_task, comm, pid,
"Hide %s-%d from List");
if (trace_graph_filter_task_find_pid(ginfo, pid))
set_menu_label(menu_filter_graph_add_task, comm, pid,
"Remove %s-%d from Graph filter");
else
set_menu_label(menu_filter_graph_add_task, comm, pid,
"Add %s-%d to Graph filter");
if (trace_graph_hide_task_find_pid(ginfo, pid))
set_menu_label(menu_filter_graph_hide_task, comm, pid,
"Show %s-%d in Graph");
else
set_menu_label(menu_filter_graph_hide_task, comm, pid,
"Hide %s-%d from Graph");
gtk_widget_show(menu_filter_graph_add_task);
gtk_widget_show(menu_filter_graph_hide_task);
}
ginfo->filter_task_selected = pid;
info->selected_task = pid;
gtk_widget_show(menu_filter_add_task);
gtk_widget_show(menu_filter_hide_task);
gtk_widget_show(menu_display_event);
gtk_widget_show(menu_display_raw_event);
free_record(record);
}
} else {
gtk_widget_hide(menu_filter_add_task);
gtk_widget_hide(menu_filter_hide_task);
gtk_widget_hide(menu_filter_graph_add_task);
gtk_widget_hide(menu_filter_graph_hide_task);
gtk_widget_hide(menu_display_event);
gtk_widget_hide(menu_display_raw_event);
}
if (ginfo->filter_enabled)
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable),
"Disable Graph Task Filter");
else
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_graph_enable),
"Enable Graph Task Filter");
if (info->list_filter_enabled)
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable),
"Disable List Task Filter");
else
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_list_enable),
"Enable List Task Filter");
if (ginfo->filter_available)
gtk_widget_set_sensitive(menu_filter_graph_enable, TRUE);
else
gtk_widget_set_sensitive(menu_filter_graph_enable, FALSE);
if ((info->sync_task_filters && ginfo->filter_available) ||
(!info->sync_task_filters && info->list_filter_available))
gtk_widget_set_sensitive(menu_filter_list_enable, TRUE);
else
gtk_widget_set_sensitive(menu_filter_list_enable, FALSE);
if (info->sync_task_filters) {
if (filter_task_count(ginfo->task_filter) ||
filter_task_count(ginfo->hide_tasks))
gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE);
else
gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE);
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_clear_tasks),
"Clear Task Filter");
gtk_widget_hide(menu_filter_graph_clear_tasks);
} else {
if (filter_task_count(ginfo->task_filter) ||
filter_task_count(ginfo->hide_tasks))
gtk_widget_set_sensitive(menu_filter_graph_clear_tasks, TRUE);
else
gtk_widget_set_sensitive(menu_filter_graph_clear_tasks, FALSE);
if (filter_task_count(info->list_task_filter) ||
filter_task_count(info->list_hide_tasks))
gtk_widget_set_sensitive(menu_filter_clear_tasks, TRUE);
else
gtk_widget_set_sensitive(menu_filter_clear_tasks, FALSE);
gtk_menu_item_set_label(GTK_MENU_ITEM(menu_filter_clear_tasks),
"Clear List Task Filter");
gtk_widget_show(menu_filter_graph_clear_tasks);
}
gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL, 3,
gtk_get_current_event_time());
return TRUE;
}
static gboolean
button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data)
{
if (event->button == 3)
return do_tree_popup(widget, event, data);
return FALSE;
}
static struct plugin_list {
struct plugin_list *next;
const char *file;
} *plugins;
static struct plugin_list **plugin_next = &plugins;
static void add_plugin(const char *file)
{
struct stat st;
int ret;
ret = stat(default_input_file, &st);
if (ret < 0) {
warning("plugin %s not found", file);
return;
}
*plugin_next = calloc(sizeof(struct plugin_list), 1);
if (!*plugin_next)
die("failed to allocat memory for plugin");
(*plugin_next)->file = file;
plugin_next = &(*plugin_next)->next;
}
static void handle_plugins(struct shark_info *info)
{
kshark_plugin_load_func func;
struct plugin_list *plugin;
void *handle;
while ((plugin = plugins)) {
plugins = plugin->next;
handle = dlopen(plugin->file, RTLD_NOW | RTLD_GLOBAL);
free(plugin);
if (!handle) {
warning("cound not load plugin '%s'\n%s\n",
plugin->file, dlerror());
continue;
}
func = dlsym(handle, KSHARK_PLUGIN_LOADER_NAME);
if (!func) {
warning("cound not find func '%s' in plugin '%s'\n%s\n",
KSHARK_PLUGIN_LOADER_NAME, plugin->file, dlerror());
continue;
}
func(info);
}
}
static void sig_end(int sig)
{
fprintf(stderr, "kernelshark: Received SIGINT\n");
exit(0);
}
void kernel_shark(int argc, char **argv)
{
struct tracecmd_input *handle;
struct shark_info *info;
struct stat st;
GtkWidget *window;
GtkWidget *vbox;
GtkWidget *vbox2;
GtkWidget *vpaned;
GtkWidget *hbox;
GtkWidget *menu_bar;
GtkWidget *menu;
GtkWidget *menu_item;
GtkWidget *sub_item;
GtkWidget *scrollwin;
GtkWidget *widget;
GtkWidget *label;
GtkWidget *spin;
GtkWidget *check;
GtkWidget *statusbar;
int ret;
int c;
#if !GLIB_CHECK_VERSION(2, 32, 0)
g_thread_init(NULL);
#endif
gdk_threads_init();
gtk_init(&argc, &argv);
while ((c = getopt(argc, argv, "hvi:p:")) != -1) {
switch(c) {
case 'h':
usage(argv);
return;
case 'v':
printf("%s - %s\n",
basename(argv[0]),
VERSION_STRING);
return;
case 'i':
input_file = optarg;
break;
case 'p':
add_plugin(optarg);
break;
default:
/* assume the other options are for gtk */
break;
}
}
if ((argc - optind) >= 1) {
if (input_file)
usage(argv);
input_file = argv[optind];
}
/* The python plugin overrides ^C */
signal(SIGINT, sig_end);
info = g_new0(typeof(*info), 1);
if (!info)
die("Unable to allocate info");
if (!input_file) {
ret = stat(default_input_file, &st);
if (ret >= 0)
input_file = default_input_file;
}
if (input_file)
handle = tracecmd_open(input_file);
else
handle = NULL;
info->handle = handle;
info->sync_task_filters = TRUE;
info->sync_event_filters = TRUE;
/* --- Main window --- */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
info->window = window;
trace_dialog_register_window(window);
if (input_file)
update_title(window, input_file);
/* --- Top Level Vbox --- */
vbox = gtk_vbox_new(FALSE, 0);
gtk_container_add(GTK_CONTAINER (window), vbox);
gtk_widget_show(vbox);
/* --- Menu Bar --- */
menu_bar = gtk_menu_bar_new();
gtk_box_pack_start(GTK_BOX (vbox), menu_bar, FALSE, FALSE, 0);
gtk_widget_show(menu_bar);
/* --- File Option --- */
menu_item = gtk_menu_item_new_with_label("File");
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 */
/* --- File - Load Option --- */
sub_item = gtk_menu_item_new_with_label("Load data");
/* Add them to the menu */
gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item);
g_signal_connect_swapped (G_OBJECT (sub_item), "activate",
G_CALLBACK (load_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- File - Load Filter --- */
sub_item = gtk_menu_item_new_with_label("Load filter");
/* Add them to the menu */
gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item);
info->load_filter_menu = sub_item;
/* We do need to show menu items */
gtk_widget_show(sub_item);
update_load_filter(info);
/* --- File - Save Filter Option --- */
sub_item = gtk_menu_item_new_with_label("Save filter");
/* Add them to the menu */
gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item);
/* We do need to show menu items */
gtk_widget_show(sub_item);
g_signal_connect_swapped (G_OBJECT (sub_item), "activate",
G_CALLBACK (save_filter_clicked),
(gpointer) info);
/* --- File - Export Filter Option --- */
sub_item = gtk_menu_item_new_with_label("Export filters");
/* Add them to the menu */
gtk_menu_shell_append(GTK_MENU_SHELL (menu), sub_item);
g_signal_connect_swapped (G_OBJECT (sub_item), "activate",
G_CALLBACK (export_filters_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- File - Quit Option --- */
sub_item = gtk_menu_item_new_with_label("Quit");
/* 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 (exit_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu);
/* --- 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 - Sync task Option --- */
sub_item = gtk_menu_item_new_with_label("Unsync Graph and List Task Filters");
info->task_sync_menu = sub_item;
/* 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 (G_OBJECT (sub_item), "activate",
G_CALLBACK (sync_task_filter_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Filter - Sync events Option --- */
sub_item = gtk_menu_item_new_with_label("Unsync Graph and List Event Filters");
info->events_sync_menu = sub_item;
/* 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 (G_OBJECT (sub_item), "activate",
G_CALLBACK (sync_events_filter_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Filter - Graph Tasks Option --- */
sub_item = gtk_menu_item_new_with_label("tasks");
/* 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 (graph_tasks_clicked),
(gpointer) info);
info->graph_task_menu = sub_item;
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Filter - Graph Hide Tasks Option --- */
sub_item = gtk_menu_item_new_with_label("hide tasks");
/* 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 (graph_hide_tasks_clicked),
(gpointer) info);
info->graph_hide_task_menu = sub_item;
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Filter - Events Option --- */
/* The list and graph events start off insync */
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 (graph_events_clicked),
(gpointer) info);
info->graph_events_menu = sub_item;
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Filter - Graph Advanced Events Option --- */
/* The list and graph events start off in sync */
sub_item = gtk_menu_item_new_with_label("advanced 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 (adv_graph_filter_clicked),
(gpointer) info);
info->graph_adv_events_menu = sub_item;
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Filter - List Tasks Option --- */
sub_item = gtk_menu_item_new_with_label("list tasks");
/* 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 (list_tasks_clicked),
(gpointer) info);
info->list_task_menu = sub_item;
/* Only show this item when list and graph tasks are not synced */
/* --- Filter - List Hide Tasks Option --- */
sub_item = gtk_menu_item_new_with_label("list hide tasks");
/* 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 (list_hide_tasks_clicked),
(gpointer) info);
info->list_hide_task_menu = sub_item;
/* Only show this item when list and graph tasks are not synced */
/* --- Filter - List Events Option --- */
sub_item = gtk_menu_item_new_with_label("list 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 (list_events_clicked),
(gpointer) info);
info->list_events_menu = sub_item;
/* We do not show this menu (yet) */
/* --- Filter - List Advanced Events Option --- */
sub_item = gtk_menu_item_new_with_label("list advanced event");
/* 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 (adv_list_filter_clicked),
(gpointer) info);
info->list_adv_events_menu = sub_item;
/* We do not show this menu (yet) */
/* --- Filter - CPUs Option --- */
sub_item = gtk_menu_item_new_with_label("list CPUs");
/* 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 (cpus_clicked),
(gpointer) info);
/* 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);
/* --- Plot Option --- */
menu_item = gtk_menu_item_new_with_label("Plots");
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 */
/* --- Plot - CPUs Option --- */
sub_item = gtk_menu_item_new_with_label("CPUs");
/* 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 (plot_cpu_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Plot - Tasks Option --- */
sub_item = gtk_menu_item_new_with_label("Tasks");
/* 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 (plot_tasks_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- End Plot Options --- */
gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu);
/* --- Capture Option --- */
menu_item = gtk_menu_item_new_with_label("Capture");
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 */
/* --- Capture - Record Option --- */
sub_item = gtk_menu_item_new_with_label("Record");
/* 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 (tracecmd_capture_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- End Capture Options --- */
gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu);
/* --- Help Option --- */
menu_item = gtk_menu_item_new_with_label("Help");
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 */
/* --- Help - Contents Option --- */
sub_item = gtk_menu_item_new_with_label("Contents");
/* 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 (help_content_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- Help - About Option --- */
sub_item = gtk_menu_item_new_with_label("About");
/* 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 (help_about_clicked),
(gpointer) info);
/* We do need to show menu items */
gtk_widget_show(sub_item);
/* --- End Help Options --- */
gtk_menu_item_set_submenu(GTK_MENU_ITEM (menu_item), menu);
/* --- Top Level Vpaned --- */
vpaned = gtk_vpaned_new();
gtk_box_pack_start(GTK_BOX(vbox), vpaned, TRUE, TRUE, 0);
gtk_widget_show(vpaned);
gtk_paned_set_position(GTK_PANED(vpaned), TRACE_HEIGHT / 2);
/* --- Set up Graph --- */
info->graph_cbs.select = ks_graph_select;
info->graph_cbs.filter = ks_graph_filter;
info->ginfo = trace_graph_create_with_callbacks(handle, &info->graph_cbs);
widget = trace_graph_get_window(info->ginfo);
gtk_paned_add1(GTK_PANED(vpaned), widget);
gtk_widget_show(widget);
/* --- Tree View Vbox --- */
vbox2 = gtk_vbox_new(FALSE, 0);
gtk_paned_add2(GTK_PANED(vpaned), vbox2);
gtk_widget_show(vbox2);
/* --- Paging Hbox --- */
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
gtk_widget_show(hbox);
/* --- Page Spin Button --- */
label = gtk_label_new("Page");
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
gtk_widget_show(label);
spin = gtk_spin_button_new(NULL, 1.0, 0);
gtk_spin_button_set_range(GTK_SPIN_BUTTON(spin), 1, 1);
gtk_box_pack_start(GTK_BOX(hbox), spin, FALSE, FALSE, 0);
gtk_widget_show(spin);
info->spin = spin;
/* --- Search --- */
/* The tree needs its columns loaded now */
info->treeview = gtk_tree_view_new();
trace_view_load(info->treeview, handle, spin);
label = gtk_label_new(" Search: ");
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
gtk_widget_show(label);
trace_view_search_setup(GTK_BOX(hbox), GTK_TREE_VIEW(info->treeview));
check = gtk_check_button_new_with_label("graph follows");
gtk_box_pack_start(GTK_BOX(hbox), check, TRUE, TRUE, 0);
gtk_widget_show(check);
g_signal_connect_swapped (check, "toggled",
G_CALLBACK (graph_check_toggle),
(gpointer) info);
/* --- Top Level Trace View Paging Hbox --- */
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, TRUE, TRUE, 0);
gtk_widget_show(hbox);
/* --- Scroll Window --- */
scrollwin = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_box_pack_start(GTK_BOX (hbox), scrollwin, TRUE, TRUE, 0);
gtk_widget_show(scrollwin);
/* --- Set up Trace Tree --- */
g_signal_connect(info->treeview, "row-activated",
(GCallback)row_double_clicked, info);
g_signal_connect(info->treeview, "cursor-changed",
(GCallback)cursor_changed, info);
gtk_container_add(GTK_CONTAINER(scrollwin), info->treeview);
gtk_signal_connect(GTK_OBJECT(info->treeview), "button_press_event",
(GtkSignalFunc) button_press_event, info);
gtk_widget_show(info->treeview);
/* --- Set up Status Bar --- */
statusbar = trace_status_bar_new();
gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0);
gtk_widget_show(statusbar);
/**********************************************
* Main Window
**********************************************/
/* Connect to the delete_event signal and Run the application */
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
(GtkSignalFunc) delete_event,
(gpointer) info);
gtk_widget_set_size_request(window, TRACE_WIDTH, TRACE_HEIGHT);
handle_plugins(info);
gdk_threads_enter();
/*
* The showing of the window will configure the graph and it will
* waste time drawing the events. But after the window appears and
* a border is displayed, the graph gets shown again.
*/
info->ginfo->no_draw = TRUE;
gtk_widget_show (window);
info->ginfo->no_draw = FALSE;
gtk_main ();
gdk_threads_leave();
}
int main(int argc, char **argv)
{
kernel_shark(argc, argv);
return 0;
}