| /* |
| * 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 <ctype.h> |
| #include <dirent.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/wait.h> |
| #include <sys/select.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <pthread.h> |
| #include <gtk/gtk.h> |
| #include <errno.h> |
| #include <getopt.h> |
| |
| #include "trace-cmd.h" |
| #include "trace-gui.h" |
| #include "trace-local.h" |
| #include "kernel-shark.h" |
| #include "version.h" |
| |
| #define default_output_file "trace.dat" |
| |
| #define PLUGIN_NONE "NONE" |
| |
| #define DIALOG_WIDTH 820 |
| #define DIALOG_HEIGHT 600 |
| |
| #define CAP_STOP "Stop" |
| |
| #define DEFAULT_MAX_BUF_SIZE 1000000 |
| |
| struct trace_capture { |
| struct pevent *pevent; |
| struct shark_info *info; |
| GtkWidget *main_dialog; |
| GtkWidget *command_entry; |
| GtkWidget *file_entry; |
| GtkWidget *output_text; |
| GtkTextBuffer *output_buffer; |
| GtkWidget *event_view; |
| GtkWidget *plugin_combo; |
| GtkWidget *settings_combo; |
| GtkWidget *run_button; |
| GtkWidget *max_num_entry; |
| pthread_t thread; |
| gboolean kill_thread; |
| gboolean capture_done; |
| gboolean load_file; |
| int command_input_fd; |
| int command_output_fd; |
| int command_pid; |
| }; |
| |
| static int is_just_ws(const char *str) |
| { |
| int i; |
| |
| for (i = 0; str[i]; i++) |
| if (!isspace(str[i])) |
| break; |
| return !str[i]; |
| } |
| |
| static GString *get_home_settings_new(void) |
| { |
| char *path = getenv("HOME"); |
| GString *str; |
| |
| str = g_string_new(path); |
| g_string_append(str, "/.trace-cmd/settings/"); |
| return str; |
| } |
| |
| static int create_home_settings(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, "/settings"); |
| 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 GtkTreeModel *create_settings_model(gpointer data) |
| { |
| struct dirent *dent; |
| GtkListStore *list; |
| GtkTreeIter iter; |
| struct stat st; |
| GString *str; |
| DIR *dir; |
| int ret; |
| |
| list = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); |
| |
| gtk_list_store_append(list, &iter); |
| gtk_list_store_set(list, &iter, |
| 0, "Current", |
| 1, "", |
| -1); |
| |
| /* Search for user settings first */ |
| str = get_home_settings_new(); |
| ret = stat(str->str, &st); |
| if (ret < 0 || !S_ISDIR(st.st_mode)) |
| goto read_system; |
| |
| dir = opendir(str->str); |
| if (!dir) |
| goto read_system; |
| |
| 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, ".kss") != 0) |
| continue; |
| |
| file = g_string_new(str->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; |
| |
| gtk_list_store_append(list, &iter); |
| gtk_list_store_set(list, &iter, |
| 0, item, |
| 1, file->str, |
| -1); |
| g_free(item); |
| g_string_free(file, TRUE); |
| } |
| |
| read_system: |
| g_string_free(str, TRUE); |
| |
| return GTK_TREE_MODEL(list); |
| } |
| |
| static void refresh_settings(struct trace_capture *cap) |
| { |
| GtkTreeModel *model; |
| |
| model = create_settings_model(NULL); |
| gtk_combo_box_set_model(GTK_COMBO_BOX(cap->settings_combo), model); |
| g_object_unref(model); |
| } |
| |
| static void ks_clear_capture_events(struct shark_info *info) |
| { |
| info->cap_all_events = FALSE; |
| |
| tracecmd_free_list(info->cap_systems); |
| info->cap_systems = NULL; |
| |
| free(info->cap_events); |
| info->cap_events = NULL; |
| } |
| |
| static void clear_capture_events(struct trace_capture *cap) |
| { |
| ks_clear_capture_events(cap->info); |
| } |
| |
| void kernel_shark_clear_capture(struct shark_info *info) |
| { |
| ks_clear_capture_events(info); |
| |
| g_free(info->cap_plugin); |
| info->cap_plugin = NULL; |
| |
| g_free(info->cap_settings_name); |
| info->cap_settings_name = NULL; |
| |
| free(info->cap_file); |
| info->cap_file = NULL; |
| |
| g_free(info->cap_buffer_output); |
| info->cap_buffer_output = NULL; |
| } |
| |
| static gboolean end_capture(struct trace_capture *cap) |
| { |
| const char *filename; |
| const char *val; |
| int pid; |
| |
| val = gtk_button_get_label(GTK_BUTTON(cap->run_button)); |
| if (strcmp(val, CAP_STOP) != 0) |
| return FALSE; |
| |
| gtk_button_set_label(GTK_BUTTON(cap->run_button), "Run"); |
| |
| cap->capture_done = TRUE; |
| |
| pid = cap->command_pid; |
| cap->command_pid = 0; |
| if (pid) { |
| kill(pid, SIGINT); |
| gdk_threads_leave(); |
| waitpid(pid, NULL, 0); |
| gdk_threads_enter(); |
| } |
| |
| if (cap->kill_thread) { |
| gdk_threads_leave(); |
| pthread_join(cap->thread, NULL); |
| gdk_threads_enter(); |
| cap->kill_thread = FALSE; |
| } |
| |
| if (cap->command_input_fd) { |
| close(cap->command_input_fd); |
| cap->command_input_fd = 0; |
| } |
| |
| if (cap->command_output_fd) { |
| close(cap->command_output_fd); |
| cap->command_output_fd = 0; |
| } |
| |
| if (cap->load_file) { |
| filename = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); |
| kernelshark_load_file(cap->info, filename); |
| cap->load_file = FALSE; |
| } |
| |
| return TRUE; |
| } |
| |
| static int is_latency(char *plugin) |
| { |
| return strcmp(plugin, "wakeup") == 0 || |
| strcmp(plugin, "wakeup_rt") == 0 || |
| strcmp(plugin, "irqsoff") == 0 || |
| strcmp(plugin, "preemptoff") == 0 || |
| strcmp(plugin, "preemptirqsoff") == 0; |
| } |
| |
| static int calculate_trace_cmd_words(struct trace_capture *cap) |
| { |
| int words = 4; /* trace-cmd record -o file */ |
| int i; |
| |
| if (cap->info->cap_all_events) |
| words += 2; |
| else { |
| if (cap->info->cap_systems) { |
| for (i = 0; cap->info->cap_systems[i]; i++) |
| words += 2; |
| } |
| |
| if (cap->info->cap_events) |
| for (i = 0; cap->info->cap_events[i] >= 0; i++) |
| words += 2; |
| } |
| |
| if (cap->info->cap_plugin) |
| words += 2; |
| |
| return words; |
| } |
| |
| static char *find_tracecmd(void) |
| { |
| struct stat st; |
| char *path = getenv("PATH"); |
| char *saveptr; |
| char *str; |
| char *loc; |
| char *tracecmd = NULL; |
| int len; |
| int ret; |
| |
| if (!path) |
| return NULL; |
| |
| path = strdup(path); |
| |
| for (str = path; ; str = NULL) { |
| loc = strtok_r(str, ":", &saveptr); |
| if (!loc) |
| break; |
| len = strlen(loc) + 11; |
| tracecmd = malloc_or_die(len); |
| snprintf(tracecmd, len, "%s/trace-cmd", loc); |
| ret = stat(tracecmd, &st); |
| |
| if (ret >= 0 && S_ISREG(st.st_mode)) { |
| /* Do we have execute permissions */ |
| if (st.st_uid == geteuid() && |
| st.st_mode & S_IXUSR) |
| break; |
| if (st.st_gid == getegid() && |
| st.st_mode & S_IXGRP) |
| break; |
| if (st.st_mode & S_IXOTH) |
| break; |
| } |
| |
| free(tracecmd); |
| tracecmd = NULL; |
| } |
| free(path); |
| |
| return tracecmd; |
| } |
| |
| static int add_trace_cmd_words(struct trace_capture *cap, char **args) |
| { |
| struct event_format *event; |
| char **systems = cap->info->cap_systems; |
| const gchar *output; |
| int *events = cap->info->cap_events; |
| int words = 0; |
| int len; |
| int i; |
| |
| output = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); |
| |
| args[words++] = find_tracecmd(); |
| if (!args[0]) |
| return -1; |
| |
| args[words++] = strdup("record"); |
| args[words++] = strdup("-o"); |
| args[words++] = strdup(output); |
| |
| if (cap->info->cap_plugin) { |
| args[words++] = strdup("-p"); |
| args[words++] = strdup(cap->info->cap_plugin); |
| } |
| |
| if (cap->info->cap_all_events) { |
| args[words++] = strdup("-e"); |
| args[words++] = strdup("all"); |
| } else { |
| if (systems) { |
| for (i = 0; systems[i]; i++) { |
| args[words++] = strdup("-e"); |
| args[words++] = strdup(systems[i]); |
| } |
| } |
| |
| if (events) { |
| for (i = 0; events[i] >= 0; i++) { |
| event = pevent_find_event(cap->pevent, events[i]); |
| if (!event) |
| continue; |
| args[words++] = strdup("-e"); |
| len = strlen(event->name) + strlen(event->system) + 2; |
| args[words] = malloc_or_die(len); |
| snprintf(args[words++], len, "%s:%s", |
| event->system, event->name); |
| } |
| } |
| } |
| |
| return words; |
| } |
| |
| static gchar *get_combo_text(GtkComboBox *combo) |
| { |
| GtkTreeModel *model; |
| GtkTreeIter iter; |
| gchar *text; |
| |
| model = gtk_combo_box_get_model(combo); |
| if (!model) |
| return NULL; |
| |
| if (!gtk_combo_box_get_active_iter(combo, &iter)) |
| return NULL; |
| |
| gtk_tree_model_get(model, &iter, |
| 0, &text, |
| -1); |
| |
| return text; |
| } |
| |
| static void update_plugin(struct trace_capture *cap) |
| { |
| cap->info->cap_plugin = get_combo_text(GTK_COMBO_BOX(cap->plugin_combo)); |
| if (strcmp(cap->info->cap_plugin, PLUGIN_NONE) == 0) { |
| g_free(cap->info->cap_plugin); |
| cap->info->cap_plugin = NULL; |
| } |
| |
| } |
| |
| /* |
| * The plugin and settings combo's are set by the first item |
| * in the model. The can share the same code to set the model. |
| * |
| * Return TRUE if set, FALSE if name was not found. |
| */ |
| static int set_combo(GtkComboBox *combo, const char *name) |
| { |
| GtkTreeModel *model; |
| GtkTreeIter iter; |
| gchar *text; |
| gboolean ret; |
| |
| model = gtk_combo_box_get_model(combo); |
| if (!model) |
| return FALSE; |
| |
| if (!gtk_tree_model_get_iter_first(model, &iter)) |
| return FALSE; |
| |
| do { |
| gtk_tree_model_get(model, &iter, |
| 0, &text, |
| -1); |
| if (strcmp(text, name) == 0) { |
| g_free(text); |
| break; |
| } |
| |
| g_free(text); |
| |
| ret = gtk_tree_model_iter_next(model, &iter); |
| } while (ret); |
| |
| if (ret) { |
| /* Found */ |
| gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), |
| &iter); |
| return TRUE; |
| } |
| |
| /* set to first item (default) */ |
| gtk_tree_model_get_iter_first(model, &iter); |
| gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo), |
| &iter); |
| |
| return FALSE; |
| } |
| |
| static void set_plugin(struct trace_capture *cap) |
| { |
| GtkComboBox *combo = GTK_COMBO_BOX(cap->plugin_combo); |
| const gchar *plugin = cap->info->cap_plugin; |
| |
| if (!plugin) |
| plugin = PLUGIN_NONE; |
| |
| if (set_combo(combo, plugin)) |
| return; |
| |
| /* Not found? */ |
| g_free(cap->info->cap_plugin); |
| cap->info->cap_plugin = NULL; |
| } |
| |
| static void set_settings(struct trace_capture *cap) |
| { |
| GtkComboBox *combo = GTK_COMBO_BOX(cap->settings_combo); |
| const gchar *name = cap->info->cap_settings_name; |
| |
| if (!name) |
| name = ""; |
| |
| if (set_combo(combo, name)) |
| return; |
| |
| /* Not found? */ |
| g_free(cap->info->cap_settings_name); |
| cap->info->cap_settings_name = NULL; |
| } |
| |
| static void update_events(struct trace_capture *cap) |
| { |
| struct shark_info *info = cap->info; |
| |
| if (!cap->event_view) |
| return; |
| |
| clear_capture_events(cap); |
| |
| trace_extract_event_list_view(cap->event_view, |
| &info->cap_all_events, |
| &info->cap_systems, |
| &info->cap_events); |
| } |
| |
| static void execute_command(struct trace_capture *cap) |
| { |
| const gchar *ccommand; |
| gchar *command; |
| gchar **args; |
| gboolean space; |
| int words; |
| int tc_words; |
| int i; |
| |
| update_plugin(cap); |
| update_events(cap); |
| |
| ccommand = gtk_entry_get_text(GTK_ENTRY(cap->command_entry)); |
| if (!ccommand || !strlen(ccommand) || is_just_ws(ccommand)) { |
| words = 0; |
| command = NULL; |
| } else { |
| |
| command = strdup(ccommand); |
| |
| space = TRUE; |
| words = 0; |
| for (i = 0; command[i]; i++) { |
| if (isspace(command[i])) |
| space = TRUE; |
| else { |
| if (space) |
| words++; |
| space = FALSE; |
| } |
| } |
| } |
| |
| tc_words = calculate_trace_cmd_words(cap); |
| |
| args = malloc_or_die(sizeof(*args) * (tc_words + words + 1)); |
| |
| add_trace_cmd_words(cap, args); |
| |
| words = tc_words; |
| space = TRUE; |
| for (i = 0; command && command[i]; i++) { |
| if (isspace(command[i])) { |
| space = TRUE; |
| command[i] = 0; |
| } else { |
| if (space) { |
| args[words] = &command[i]; |
| words++; |
| } |
| space = FALSE; |
| } |
| } |
| args[words] = NULL; |
| |
| write(1, "# ", 2); |
| for (i = 0; args[i]; i++) { |
| write(1, args[i], strlen(args[i])); |
| write(1, " ", 1); |
| } |
| write(1, "\n", 1); |
| |
| execvp(args[0], args); |
| perror("execvp"); |
| |
| for (i = 0; args[i]; i++) |
| free(args[i]); |
| free(args); |
| g_free(cap->info->cap_plugin); |
| } |
| |
| static void *monitor_pipes(void *data) |
| { |
| struct trace_capture *cap = data; |
| GtkTextIter start_iter; |
| GtkTextIter cut_iter; |
| GtkTextIter iter; |
| gchar buf[BUFSIZ+1]; |
| struct timeval tv; |
| const char *val; |
| fd_set fds; |
| gboolean eof; |
| int max_size; |
| int total; |
| int del; |
| int ret; |
| int r; |
| |
| gdk_threads_enter(); |
| /* get the max size */ |
| val = gtk_entry_get_text(GTK_ENTRY(cap->max_num_entry)); |
| max_size = atoi(val); |
| |
| /* Clear the buffer */ |
| gtk_text_buffer_get_start_iter(cap->output_buffer, &start_iter); |
| gtk_text_buffer_get_end_iter(cap->output_buffer, &cut_iter); |
| gtk_text_buffer_delete(cap->output_buffer, &start_iter, &cut_iter); |
| total = 0; |
| gdk_threads_leave(); |
| |
| do { |
| FD_ZERO(&fds); |
| FD_SET(cap->command_input_fd, &fds); |
| tv.tv_sec = 6; |
| tv.tv_usec = 0; |
| ret = select(cap->command_input_fd+1, &fds, NULL, NULL, &tv); |
| if (ret < 0) |
| break; |
| |
| eof = TRUE; |
| while ((r = read(cap->command_input_fd, buf, BUFSIZ)) > 0) { |
| eof = FALSE; |
| buf[r] = 0; |
| total += r; |
| if (total > max_size) |
| del = total - max_size; |
| else |
| del = 0; |
| gdk_threads_enter(); |
| if (del) { |
| gtk_text_buffer_get_start_iter(cap->output_buffer, &start_iter); |
| gtk_text_buffer_get_start_iter(cap->output_buffer, &cut_iter); |
| gtk_text_iter_forward_chars(&cut_iter, del); |
| gtk_text_buffer_delete(cap->output_buffer, &start_iter, &cut_iter); |
| total -= del; |
| } |
| gtk_text_buffer_get_end_iter(cap->output_buffer, |
| &iter); |
| gtk_text_buffer_insert(cap->output_buffer, &iter, buf, -1); |
| gdk_threads_leave(); |
| } |
| } while (!cap->capture_done && !eof); |
| |
| if (eof) { |
| gdk_threads_enter(); |
| end_capture(cap); |
| gdk_threads_leave(); |
| } |
| |
| pthread_exit(NULL); |
| } |
| |
| static void run_command(struct trace_capture *cap) |
| { |
| int brass[2]; |
| int copper[2]; |
| int pid; |
| |
| if (pipe(brass) < 0) { |
| warning("Could not create pipe"); |
| return; |
| } |
| |
| if (pipe(copper) < 0) { |
| warning("Could not create pipe"); |
| goto fail_pipe; |
| } |
| |
| if ((pid = fork()) < 0) { |
| warning("Could not fork process"); |
| goto fail_fork; |
| } |
| |
| cap->command_pid = pid; |
| |
| if (!pid) { |
| close(brass[0]); |
| close(copper[1]); |
| close(0); |
| close(1); |
| close(2); |
| |
| dup(copper[0]); |
| dup(brass[1]); |
| dup(brass[1]); |
| |
| execute_command(cap); |
| |
| close(1); |
| exit(0); |
| } |
| close(brass[1]); |
| close(copper[0]); |
| |
| /* these should never be 0 */ |
| if (!brass[1] || !copper[0]) |
| warning("Pipes have zero as file descriptor"); |
| |
| cap->command_input_fd = brass[0]; |
| cap->command_output_fd = copper[1]; |
| |
| /* Do not create a thread under the gdk lock */ |
| gdk_threads_leave(); |
| if (pthread_create(&cap->thread, NULL, monitor_pipes, cap) < 0) |
| warning("Failed to create thread"); |
| else { |
| cap->kill_thread = TRUE; |
| cap->load_file = TRUE; |
| } |
| gdk_threads_enter(); |
| |
| return; |
| |
| fail_fork: |
| close(copper[0]); |
| close(copper[1]); |
| fail_pipe: |
| close(brass[0]); |
| close(brass[1]); |
| } |
| |
| static int trim_plugins(char **plugins) |
| { |
| int len = 0; |
| int i; |
| |
| if (!plugins) |
| return 0; |
| |
| for (i = 0; plugins[i]; i++) { |
| if (is_latency(plugins[i])) |
| continue; |
| plugins[len++] = plugins[i]; |
| } |
| plugins[len] = NULL; |
| |
| return len; |
| } |
| |
| static void |
| file_clicked (GtkWidget *widget, gpointer data) |
| { |
| struct trace_capture *cap = data; |
| gchar *filename; |
| |
| filename = trace_get_file_dialog_filter("Trace File", "Save", |
| TRACE_DIALOG_FILTER_DATA, FALSE); |
| if (!filename) |
| return; |
| |
| gtk_entry_set_text(GTK_ENTRY(cap->file_entry), filename); |
| } |
| |
| static void execute_button_clicked(struct trace_capture *cap) |
| { |
| struct stat st; |
| GtkResponseType ret; |
| const char *filename; |
| char *tracecmd; |
| |
| if (end_capture(cap)) |
| return; |
| |
| tracecmd = find_tracecmd(); |
| if (!tracecmd) { |
| warning("trace-cmd not found in path"); |
| return; |
| } |
| free(tracecmd); |
| |
| filename = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); |
| |
| if (stat(filename, &st) >= 0) { |
| ret = trace_dialog(GTK_WINDOW(cap->main_dialog), TRACE_GUI_ASK, |
| "The file '%s' already exists.\n" |
| "Are you sure you want to replace it", |
| filename); |
| if (ret == GTK_RESPONSE_NO) |
| return; |
| } |
| |
| gtk_button_set_label(GTK_BUTTON(cap->run_button), CAP_STOP); |
| run_command(cap); |
| } |
| |
| static int load_events(struct trace_capture *cap, |
| struct tracecmd_xml_handle *handle, |
| struct tracecmd_xml_system_node *node) |
| { |
| struct shark_info *info = cap->info; |
| struct tracecmd_xml_system_node *event_node; |
| struct event_format *event; |
| struct pevent *pevent = cap->pevent; |
| const char *name; |
| int *events = NULL; |
| int event_len = 0; |
| const char *system; |
| const char *event_name; |
| |
| for (node = tracecmd_xml_node_child(node); node; |
| node = tracecmd_xml_node_next(node)) { |
| name = tracecmd_xml_node_type(node); |
| |
| if (strcmp(name, "Event") != 0) |
| continue; |
| |
| event_node = tracecmd_xml_node_child(node); |
| if (!event_node) |
| continue; |
| |
| name = tracecmd_xml_node_type(event_node); |
| if (strcmp(name, "System") != 0) |
| continue; |
| system = tracecmd_xml_node_value(handle, event_node); |
| |
| event_node = tracecmd_xml_node_next(event_node); |
| if (!event_node) |
| continue; |
| |
| name = tracecmd_xml_node_type(event_node); |
| if (strcmp(name, "Name") != 0) |
| continue; |
| event_name = tracecmd_xml_node_value(handle, event_node); |
| |
| event = pevent_find_event_by_name(pevent, system, event_name); |
| |
| if (!event) |
| continue; |
| |
| events = tracecmd_add_id(events, event->id, event_len++); |
| } |
| |
| info->cap_events = events; |
| return 0; |
| } |
| |
| static int load_cap_events(struct trace_capture *cap, |
| struct tracecmd_xml_handle *handle, |
| struct tracecmd_xml_system_node *node) |
| { |
| struct shark_info *info = cap->info; |
| const char *name; |
| char **systems = NULL; |
| int sys_len = 0; |
| |
| ks_clear_capture_events(info); |
| |
| for (node = tracecmd_xml_node_child(node); node; |
| node = tracecmd_xml_node_next(node)) { |
| |
| name = tracecmd_xml_node_type(node); |
| |
| if (strcmp(name, "CaptureType") == 0) { |
| name = tracecmd_xml_node_value(handle, node); |
| if (strcmp(name, "all events") == 0) { |
| info->cap_all_events = TRUE; |
| break; |
| } |
| continue; |
| |
| } else if (strcmp(name, "System") == 0) { |
| name = tracecmd_xml_node_value(handle, node); |
| systems = tracecmd_add_list(systems, name, sys_len++); |
| |
| } else if (strcmp(name, "Events") == 0) |
| load_events(cap, handle, node); |
| } |
| |
| info->cap_systems = systems; |
| |
| return 0; |
| } |
| |
| static void load_settings(struct trace_capture *cap, gchar *filename) |
| { |
| struct shark_info *info = cap->info; |
| struct tracecmd_xml_system_node *syschild; |
| struct tracecmd_xml_handle *handle; |
| struct tracecmd_xml_system *system; |
| const char *plugin; |
| const char *name; |
| |
| handle = tracecmd_xml_open(filename); |
| if (!handle) { |
| warning("Could not open %s", filename); |
| return; |
| } |
| |
| system = tracecmd_xml_find_system(handle, "CaptureSettings"); |
| if (!system) |
| goto out; |
| |
| syschild = tracecmd_xml_system_node(system); |
| if (!syschild) |
| goto out_free_sys; |
| |
| g_free(info->cap_plugin); |
| info->cap_plugin = NULL; |
| |
| do { |
| name = tracecmd_xml_node_type(syschild); |
| if (strcmp(name, "Events") == 0) { |
| load_cap_events(cap, handle, syschild); |
| trace_update_event_view(cap->event_view, |
| cap->pevent, |
| NULL, |
| info->cap_all_events, |
| info->cap_systems, |
| info->cap_events); |
| } |
| |
| else if (strcmp(name, "Plugin") == 0) { |
| plugin = tracecmd_xml_node_value(handle, syschild); |
| info->cap_plugin = g_strdup(plugin); |
| |
| } else if (strcmp(name, "Command") == 0) { |
| name = tracecmd_xml_node_value(handle, syschild); |
| gtk_entry_set_text(GTK_ENTRY(cap->command_entry), name); |
| |
| } else if (strcmp(name, "File") == 0) { |
| name = tracecmd_xml_node_value(handle, syschild); |
| gtk_entry_set_text(GTK_ENTRY(cap->file_entry), name); |
| } |
| |
| syschild = tracecmd_xml_node_next(syschild); |
| } while (syschild); |
| |
| set_plugin(cap); |
| |
| out_free_sys: |
| tracecmd_xml_free_system(system); |
| |
| out: |
| tracecmd_xml_close(handle); |
| } |
| |
| static void settings_changed(GtkComboBox *combo, |
| gpointer data) |
| { |
| struct trace_capture *cap = data; |
| GtkTreeModel *model; |
| GtkTreeIter iter; |
| gchar *text; |
| |
| model = gtk_combo_box_get_model(combo); |
| if (!model) |
| return; |
| |
| if (!gtk_combo_box_get_active_iter(combo, &iter)) |
| return; |
| |
| gtk_tree_model_get(model, &iter, |
| 1, &text, |
| -1); |
| |
| if (text && strlen(text)) |
| load_settings(cap, text); |
| |
| g_free(text); |
| } |
| |
| static void import_settings_clicked(GtkWidget *widget, gpointer data) |
| { |
| struct trace_capture *cap = data; |
| gchar *filename; |
| |
| filename = trace_get_file_dialog_filter("Import Settings", NULL, |
| TRACE_DIALOG_FILTER_SETTING, FALSE); |
| if (!filename) |
| return; |
| |
| load_settings(cap, filename); |
| |
| g_free(filename); |
| } |
| |
| static void save_events(struct trace_capture *cap, |
| struct tracecmd_xml_handle *handle) |
| { |
| struct pevent *pevent = cap->pevent; |
| struct event_format *event; |
| char **systems = cap->info->cap_systems; |
| int *events = cap->info->cap_events; |
| int i; |
| |
| tracecmd_xml_write_element(handle, "CaptureType", "Events"); |
| |
| for (i = 0; systems && systems[i]; i++) |
| tracecmd_xml_write_element(handle, "System", systems[i]); |
| |
| if (!events || events[0] < 0) |
| return; |
| |
| tracecmd_xml_start_sub_system(handle, "Events"); |
| for (i = 0; events[i] > 0; i++) { |
| event = pevent_find_event(pevent, events[i]); |
| if (event) { |
| tracecmd_xml_start_sub_system(handle, "Event"); |
| tracecmd_xml_write_element(handle, "System", event->system); |
| tracecmd_xml_write_element(handle, "Name", event->name); |
| tracecmd_xml_end_sub_system(handle); |
| } |
| } |
| |
| tracecmd_xml_end_sub_system(handle); |
| } |
| |
| static void save_settings(struct trace_capture *cap, const char *filename) |
| { |
| struct shark_info *info = cap->info; |
| struct tracecmd_xml_handle *handle; |
| const char *file; |
| const char *command; |
| |
| handle = tracecmd_xml_create(filename, VERSION_STRING); |
| if (!handle) { |
| warning("Could not create %s", filename); |
| return; |
| } |
| |
| update_events(cap); |
| |
| tracecmd_xml_start_system(handle, "CaptureSettings"); |
| |
| tracecmd_xml_start_sub_system(handle, "Events"); |
| |
| if (info->cap_all_events) |
| tracecmd_xml_write_element(handle, "CaptureType", "all events"); |
| else if ((info->cap_systems && info->cap_systems[0]) || |
| (info->cap_events && info->cap_events[0] >= 0)) { |
| save_events(cap, handle); |
| } |
| |
| tracecmd_xml_end_sub_system(handle); |
| |
| update_plugin(cap); |
| if (info->cap_plugin) |
| tracecmd_xml_write_element(handle, "Plugin", info->cap_plugin); |
| |
| command = gtk_entry_get_text(GTK_ENTRY(cap->command_entry)); |
| if (command && strlen(command) && !is_just_ws(command)) |
| tracecmd_xml_write_element(handle, "Command", command); |
| |
| file = gtk_entry_get_text(GTK_ENTRY(cap->file_entry)); |
| if (file && strlen(file) && !is_just_ws(file)) |
| tracecmd_xml_write_element(handle, "File", file); |
| |
| tracecmd_xml_end_system(handle); |
| |
| tracecmd_xml_close(handle); |
| } |
| |
| static void save_settings_clicked(GtkWidget *widget, gpointer data) |
| { |
| struct trace_capture *cap = 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 Settings", |
| 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("Settings 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); |
| |
| again: |
| result = gtk_dialog_run(GTK_DIALOG(dialog)); |
| switch (result) { |
| case GTK_RESPONSE_ACCEPT: |
| name = gtk_entry_get_text(GTK_ENTRY(entry)); |
| if (!name || is_just_ws(name)) { |
| warning("Must enter a name"); |
| goto again; |
| } |
| /* Make sure home settings exists */ |
| if (create_home_settings() < 0) |
| break; |
| file = get_home_settings_new(); |
| g_string_append_printf(file, "/%s.kss", name); |
| ret = stat(file->str, &st); |
| if (ret >= 0) { |
| ret = trace_dialog(GTK_WINDOW(dialog), TRACE_GUI_ASK, |
| "The setting '%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_settings(cap, file->str); |
| |
| refresh_settings(cap); |
| g_free(cap->info->cap_settings_name); |
| cap->info->cap_settings_name = g_strdup(name); |
| set_settings(cap); |
| |
| g_string_free(file, TRUE); |
| break; |
| |
| case GTK_RESPONSE_REJECT: |
| break; |
| default: |
| break; |
| }; |
| |
| gtk_widget_destroy(dialog); |
| } |
| |
| static void export_settings_clicked(GtkWidget *widget, gpointer data) |
| { |
| struct trace_capture *cap = data; |
| gchar *filename; |
| |
| filename = trace_get_file_dialog_filter("Save Settings", "Save", |
| TRACE_DIALOG_FILTER_SETTING, TRUE); |
| if (!filename) |
| return; |
| |
| save_settings(cap, filename); |
| |
| g_free(filename); |
| } |
| |
| static GtkTreeModel *create_plugin_combo_model(gpointer data) |
| { |
| char **plugins = data; |
| GtkListStore *list; |
| GtkTreeIter iter; |
| int i; |
| |
| list = gtk_list_store_new(1, G_TYPE_STRING); |
| |
| gtk_list_store_append(list, &iter); |
| gtk_list_store_set(list, &iter, |
| 0, PLUGIN_NONE, |
| -1); |
| |
| for (i = 0; plugins && plugins[i]; i++) { |
| gtk_list_store_append(list, &iter); |
| gtk_list_store_set(list, &iter, |
| 0, plugins[i], |
| -1); |
| } |
| |
| return GTK_TREE_MODEL(list); |
| } |
| |
| static void insert_text(GtkEditable *buffer, |
| gchar *new_text, |
| gint new_text_length, |
| gint *position, |
| gpointer data) |
| { |
| int i; |
| guint sigid; |
| |
| /* Only allow 0-9 to be written to the entry */ |
| for (i = 0; i < new_text_length; i++) { |
| if (new_text[i] < '0' || new_text[i] > '9') { |
| sigid = g_signal_lookup("insert-text", |
| G_OBJECT_TYPE(buffer)); |
| g_signal_stop_emission(buffer, sigid, 0); |
| return; |
| } |
| } |
| } |
| |
| /* |
| * Trace Capture Dialog Window |
| * |
| * +--------------------------------------------------------------------+ |
| * | Dialog Window | |
| * | +-------------------------------+-------------------------------+ | |
| * | | Paned Window | +---------------------------+ | | |
| * | | +---------------------------+ | | Scroll window | | | |
| * | | | Hbox | | | +-----------------------+ | | | |
| * | | | Label Plugin Combo | | | | Event Tree | | | | |
| * | | +---------------------------+ | | | | | | | |
| * | | | | | | | | | |
| * | | | | +-----------------------+ | | | |
| * | | | +---------------------------+ | | |
| * | +-------------------------------+-------------------------------+ | |
| * +--------------------------------------------------------------------+ |
| */ |
| static void tracing_dialog(struct shark_info *info, const char *tracing) |
| { |
| struct pevent *pevent; |
| GtkWidget *dialog; |
| GtkWidget *button; |
| GtkWidget *combo; |
| GtkWidget *label; |
| GtkWidget *entry; |
| GtkWidget *frame; |
| GtkWidget *vbox; |
| GtkWidget *scrollwin; |
| GtkWidget *table; |
| GtkWidget *table2; |
| GtkWidget *event_tree; |
| GtkWidget *viewport; |
| GtkWidget *textview; |
| GtkWidget *hbox; |
| GtkTextBuffer *buffer; |
| GtkTextIter start_iter; |
| GtkTextIter end_iter; |
| char **plugins; |
| int nr_plugins; |
| struct trace_capture cap; |
| const gchar *file; |
| const char *command; |
| const char *val; |
| GString *str; |
| gint result; |
| |
| memset(&cap, 0, sizeof(cap)); |
| |
| cap.info = info; |
| plugins = tracecmd_local_plugins(tracing); |
| |
| /* Skip latency plugins */ |
| nr_plugins = trim_plugins(plugins); |
| if (!nr_plugins && plugins) { |
| tracecmd_free_list(plugins); |
| plugins = NULL; |
| } |
| |
| /* Send parse warnings to status display */ |
| trace_dialog_register_alt_warning(vpr_stat); |
| |
| pevent = tracecmd_local_events(tracing); |
| trace_dialog_register_alt_warning(NULL); |
| |
| cap.pevent = pevent; |
| |
| if (!pevent && !nr_plugins) { |
| warning("No events or plugins found"); |
| return; |
| } |
| |
| dialog = gtk_dialog_new(); |
| gtk_window_set_title(GTK_WINDOW(dialog), "Capture"); |
| |
| button = gtk_button_new_with_label("Run"); |
| gtk_dialog_add_action_widget(GTK_DIALOG(dialog), button, |
| GTK_RESPONSE_ACCEPT); |
| gtk_widget_show(button); |
| |
| cap.run_button = button; |
| |
| gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CLOSE, |
| GTK_RESPONSE_REJECT); |
| |
| cap.main_dialog = dialog; |
| |
| /* --- Top Level Hpaned --- */ |
| table = gtk_table_new(4, 2, FALSE); |
| |
| /* It is possible that no pevents exist. */ |
| if (pevent) { |
| |
| scrollwin = gtk_scrolled_window_new(NULL, NULL); |
| gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrollwin), |
| GTK_POLICY_AUTOMATIC, |
| GTK_POLICY_AUTOMATIC); |
| |
| gtk_table_attach(GTK_TABLE(table), scrollwin, 0, 1, 1, 2, |
| GTK_FILL, GTK_FILL|GTK_EXPAND, 0, 0); |
| gtk_widget_show(scrollwin); |
| |
| event_tree = trace_create_event_list_view(pevent, NULL, |
| cap.info->cap_all_events, |
| cap.info->cap_systems, |
| cap.info->cap_events); |
| |
| gtk_container_add(GTK_CONTAINER(scrollwin), event_tree); |
| gtk_widget_show(event_tree); |
| |
| cap.event_view = event_tree; |
| |
| } else { |
| /* No events */ |
| label = gtk_label_new("No events enabled on system"); |
| gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, |
| GTK_FILL, GTK_EXPAND|GTK_FILL, |
| 0, 10); |
| gtk_widget_show(label); |
| cap.event_view = NULL; |
| } |
| |
| gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0); |
| gtk_widget_show(table); |
| |
| /*------------------ Frame Settings --------------------------- */ |
| |
| frame = gtk_frame_new("Settings"); |
| gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 0, 1, |
| GTK_FILL, 0, 0, 10); |
| gtk_widget_show(frame); |
| |
| table2 = gtk_table_new(2, 3, FALSE); |
| gtk_container_add(GTK_CONTAINER(frame), table2); |
| gtk_widget_show(table2); |
| |
| gtk_table_set_col_spacings(GTK_TABLE(table2), 5); |
| |
| button = gtk_button_new_with_label("Save Settings"); |
| gtk_table_attach_defaults(GTK_TABLE(table2), button, 0, 1, 0, 1); |
| gtk_widget_show(button); |
| |
| g_signal_connect (button, "clicked", |
| G_CALLBACK (save_settings_clicked), |
| (gpointer)&cap); |
| |
| button = gtk_button_new_with_label("Import Settings"); |
| gtk_table_attach_defaults(GTK_TABLE(table2), button, 1, 2, 0, 1); |
| gtk_widget_show(button); |
| |
| g_signal_connect (button, "clicked", |
| G_CALLBACK (import_settings_clicked), |
| (gpointer)&cap); |
| |
| |
| button = gtk_button_new_with_label("Export Settings"); |
| gtk_table_attach_defaults(GTK_TABLE(table2), button, 2, 3, 0, 1); |
| gtk_widget_show(button); |
| |
| g_signal_connect (button, "clicked", |
| G_CALLBACK (export_settings_clicked), |
| (gpointer)&cap); |
| |
| if (cap.info->cap_settings_name) |
| set_settings(&cap); |
| |
| label = gtk_label_new("Available Settings: "); |
| gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 1, 2); |
| gtk_widget_show(label); |
| |
| combo = trace_create_combo_box(NULL, NULL, |
| create_settings_model, NULL); |
| gtk_table_attach_defaults(GTK_TABLE(table2), combo, 1, 3, 1, 2); |
| |
| cap.settings_combo = combo; |
| |
| g_signal_connect (combo, "changed", |
| G_CALLBACK (settings_changed), |
| (gpointer)&cap); |
| |
| |
| |
| /*------------------ Frame Settings --------------------------- */ |
| |
| frame = gtk_frame_new("Execute"); |
| gtk_table_attach(GTK_TABLE(table), frame, 0, 1, 3, 4, |
| GTK_FILL, 0, 0, 10); |
| gtk_widget_show(frame); |
| |
| table2 = gtk_table_new(3, 3, FALSE); |
| gtk_container_add(GTK_CONTAINER(frame), table2); |
| gtk_widget_show(table2); |
| |
| label = gtk_label_new("Plugin: "); |
| gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 0, 1); |
| gtk_widget_show(label); |
| |
| combo = trace_create_combo_box(NULL, NULL, create_plugin_combo_model, plugins); |
| cap.plugin_combo = combo; |
| |
| gtk_table_attach_defaults(GTK_TABLE(table2), combo, 1, 3, 0, 1); |
| |
| if (cap.info->cap_plugin) |
| set_plugin(&cap); |
| |
| |
| label = gtk_label_new("Command:"); |
| gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 1, 2); |
| gtk_widget_show(label); |
| |
| entry = gtk_entry_new(); |
| gtk_table_attach_defaults(GTK_TABLE(table2), entry, 1, 3, 1, 2); |
| gtk_widget_show(entry); |
| |
| cap.command_entry = entry; |
| |
| if (cap.info->cap_command) |
| gtk_entry_set_text(GTK_ENTRY(entry), cap.info->cap_command); |
| |
| label = gtk_label_new("Output file: "); |
| gtk_table_attach_defaults(GTK_TABLE(table2), label, 0, 1, 2, 3); |
| gtk_widget_show(label); |
| |
| entry = gtk_entry_new(); |
| gtk_table_attach_defaults(GTK_TABLE(table2), entry, 1, 2, 2, 3); |
| gtk_widget_show(entry); |
| |
| if (cap.info->cap_file) |
| file = cap.info->cap_file; |
| else |
| file = default_output_file; |
| |
| gtk_entry_set_text(GTK_ENTRY(entry), file); |
| cap.file_entry = entry; |
| |
| button = gtk_button_new_with_label("Browse"); |
| gtk_table_attach_defaults(GTK_TABLE(table2), button, 2, 3, 2, 3); |
| gtk_widget_show(button); |
| |
| g_signal_connect (button, "clicked", |
| G_CALLBACK (file_clicked), |
| (gpointer)&cap); |
| |
| |
| /*------------------ Command Output ------------------ */ |
| |
| vbox = gtk_vbox_new(FALSE, 0); |
| gtk_table_attach_defaults(GTK_TABLE(table), vbox, 1, 2, 0, 4); |
| gtk_widget_show(vbox); |
| gtk_widget_set_size_request(GTK_WIDGET(vbox), 500, 0); |
| |
| |
| label = gtk_label_new("Output Display:"); |
| gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); |
| gtk_widget_show(label); |
| |
| 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(vbox), scrollwin, TRUE, TRUE, 0); |
| gtk_widget_show(scrollwin); |
| |
| viewport = gtk_viewport_new(NULL, NULL); |
| gtk_widget_show(viewport); |
| |
| gtk_container_add(GTK_CONTAINER(scrollwin), viewport); |
| |
| textview = gtk_text_view_new(); |
| buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(textview)); |
| |
| gtk_container_add(GTK_CONTAINER(viewport), textview); |
| gtk_widget_show(textview); |
| |
| cap.output_text = textview; |
| cap.output_buffer = buffer; |
| |
| /* set the buffer from its previous setting */ |
| if (info->cap_buffer_output) |
| gtk_text_buffer_set_text(buffer, info->cap_buffer_output, |
| strlen(info->cap_buffer_output)); |
| |
| hbox = gtk_hbox_new(FALSE, 0); |
| gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); |
| gtk_widget_show(hbox); |
| |
| label = gtk_label_new("Max # of characters in output display: "); |
| 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); |
| |
| cap.max_num_entry = entry; |
| |
| if (!info->cap_max_buf_size) |
| info->cap_max_buf_size = DEFAULT_MAX_BUF_SIZE; |
| |
| str = g_string_new(""); |
| g_string_append_printf(str, "%d", info->cap_max_buf_size); |
| gtk_entry_set_text(GTK_ENTRY(entry), str->str); |
| g_string_free(str, TRUE); |
| |
| g_signal_connect (entry, "insert-text", |
| G_CALLBACK (insert_text), |
| (gpointer)&cap); |
| |
| |
| gtk_widget_set_size_request(GTK_WIDGET(dialog), |
| DIALOG_WIDTH, DIALOG_HEIGHT); |
| |
| gtk_widget_show(dialog); |
| |
| cont: |
| result = gtk_dialog_run(GTK_DIALOG(dialog)); |
| |
| if (result == GTK_RESPONSE_ACCEPT) { |
| execute_button_clicked(&cap); |
| goto cont; |
| } |
| |
| /* Make sure no capture is running */ |
| end_capture(&cap); |
| |
| /* Get the max buffer size */ |
| val = gtk_entry_get_text(GTK_ENTRY(entry)); |
| info->cap_max_buf_size = atoi(val); |
| |
| gtk_text_buffer_get_start_iter(cap.output_buffer, &start_iter); |
| gtk_text_buffer_get_end_iter(cap.output_buffer, &end_iter); |
| |
| g_free(info->cap_buffer_output); |
| info->cap_buffer_output = gtk_text_buffer_get_text(cap.output_buffer, |
| &start_iter, |
| &end_iter, |
| FALSE); |
| |
| /* save the plugin and file to reuse if we come back */ |
| update_plugin(&cap); |
| |
| free(info->cap_file); |
| cap.info->cap_file = strdup(gtk_entry_get_text(GTK_ENTRY(cap.file_entry))); |
| |
| free(info->cap_command); |
| command = gtk_entry_get_text(GTK_ENTRY(cap.command_entry)); |
| if (command && strlen(command) && !is_just_ws(command)) |
| cap.info->cap_command = strdup(command); |
| else |
| cap.info->cap_command = NULL; |
| |
| update_events(&cap); |
| |
| gtk_widget_destroy(dialog); |
| |
| if (pevent) |
| pevent_free(pevent); |
| |
| if (plugins) |
| tracecmd_free_list(plugins); |
| } |
| |
| void tracecmd_capture_clicked(gpointer data) |
| { |
| struct shark_info *info = data; |
| const char *tracing; |
| |
| tracing = tracecmd_get_tracing_dir(); |
| |
| if (!tracing) { |
| warning("Can not find or mount tracing directory!\n" |
| "Either tracing is not configured for this kernel\n" |
| "or you do not have the proper permissions to mount the directory"); |
| return; |
| } |
| |
| tracing_dialog(info, tracing); |
| } |