From e2fb3b29852bff7df657c222b21b19f707041d27 Mon Sep 17 00:00:00 2001 From: Soeren Sandmann Date: Sat, 24 Sep 2005 04:14:20 +0000 Subject: [PATCH] New non-GUI version, written by Lorenzo Colitti, with some changes by me. Sat Sep 24 00:01:42 2005 Soeren Sandmann * Nakefile.am, sysprof-text.c: New non-GUI version, written by Lorenzo Colitti, with some changes by me. * signal-handler.[ch]: New files that provide a way to get UNIX signals into a glib main loop. * README: add Lorenzo to credits --- ChangeLog | 12 ++++ Makefile.am | 24 ++++++- README | 5 +- TODO | 24 +++---- signal-handler.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ signal-handler.h | 27 ++++++++ sysprof-text.c | 183 +++++++++++++++++++++++++++++++++++++++++++++++++---- sysprof.c | 72 +-------------------- 8 files changed, 439 insertions(+), 98 deletions(-) create mode 100644 signal-handler.c create mode 100644 signal-handler.h diff --git a/ChangeLog b/ChangeLog index ed7d562..37c4421 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +Sat Sep 24 00:01:42 2005 Soeren Sandmann + + * Nakefile.am, sysprof-text.c: New non-GUI version, written + by Lorenzo Colitti, with some changes by me. + + * signal-handler.[ch]: New files that provide a way to get UNIX + signals into a glib main loop. + + * README: add Lorenzo to credits + Fri Sep 23 20:46:40 2005 Soeren Sandmann * sysprof.c (build_gui): If the glade file doesn't exists pop up @@ -7,6 +17,8 @@ Fri Sep 23 20:46:40 2005 Soeren Sandmann Sat Sep 17 14:35:32 2005 Soeren Sandmann + * Release 1.0 (this comment was added post facto) + * Bump version numbers * README: update * TODO: Updates diff --git a/Makefile.am b/Makefile.am index 0681d10..d477a8e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ SUBDIRS = module DIST_SUBDIRS = module -bin_PROGRAMS = sysprof +bin_PROGRAMS = sysprof sysprof-text pkgdata_DATA = sysprof.glade sysprof-icon.png sysprof_SOURCES = \ @@ -22,8 +22,30 @@ sysprof_SOURCES = \ watch.h \ watch.c +sysprof_text_SOURCES = \ + binfile.h \ + binfile.c \ + process.h \ + process.c \ + profile.h \ + profile.c \ + sfile.h \ + sfile.c \ + stackstash.h \ + stackstash.c \ + module/sysprof-module.h \ + signal-handler.h \ + signal-handler.c \ + sysprof-text.c \ + treeviewutils.h \ + treeviewutils.c \ + watch.h \ + watch.c + sysprof_LDADD = $(DEP_LIBS) +sysprof_text_LDADD = $(DEP_LIBS) + INCLUDES = \ $(DEP_CFLAGS) \ -DDATADIR=\"$(pkgdatadir)\" \ diff --git a/README b/README index 04ef77c..ae69bac 100644 --- a/README +++ b/README @@ -87,13 +87,12 @@ Debugging symbols you why, but then I'd have to kill you. - - Credits: + Lorenzo Colitti for writing the sysprof-text program Diana Fong for the icon Mike Frysinger for x86-64 support Kristian Høgsberg for the first port to the 2.6 kernel. Owen Taylor for the symbol lookup code in memprof - + Søren (sandmann@daimi.au.dk) diff --git a/TODO b/TODO index 85fdd8f..3b240e7 100644 --- a/TODO +++ b/TODO @@ -245,17 +245,6 @@ http://www.linuxbase.org/spec/booksets/LSB-Embedded/LSB-Embedded/ehframe.html - add an 'everything' object. It is really needed for a lot of things - should be easy to do with stackstash reorganization. -- Non-GUI version that can save in a format the GUI can understand. - Could be used for profiling startup etc. Would preferably be able to - dump the data to a network socket. Should be able to react to eg. - SIGUSR1 by dumping the data. - - Work done by Lorenzo: - - http://www.colitti.com/lorenzo/software/gnome-startup/sysprof-text.diff - http://www.colitti.com/lorenzo/software/gnome-startup/sysprof.log - http://colitti.com/lorenzo/software/gnome-startup/ - - Figure out how Google's pprof script works. Then add real call graph drawing. (google's script is really simple; uses dot from graphviz). @@ -274,7 +263,7 @@ Later: - .desktop file [Is this worth it? You will often want to start it as root, - and you will need to insert the module from the command line] + and you will need to insert the module from the comman line] - Applications should be able to say "start profiling", "stop profiling" so that you can limit the profiling to specific areas. Idea: @@ -435,6 +424,17 @@ Later: DONE: +* Non-GUI version that can save in a format the GUI can understand. + Could be used for profiling startup etc. Would preferably be able to + dump the data to a network socket. Should be able to react to eg. + SIGUSR1 by dumping the data. + + Work done by Lorenzo: + + http://www.colitti.com/lorenzo/software/gnome-startup/sysprof-text.diff + http://www.colitti.com/lorenzo/software/gnome-startup/sysprof.log + http://colitti.com/lorenzo/software/gnome-startup/ + * consider caching [filename => bin_file] * Check the kernel we are building against, if it is SMP or diff --git a/signal-handler.c b/signal-handler.c new file mode 100644 index 0000000..9d5a4af --- /dev/null +++ b/signal-handler.c @@ -0,0 +1,190 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk) + * + * This library 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; either version 2 of the License, or + * (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "watch.h" +#include "signal-handler.h" + +typedef struct SignalWatch SignalWatch; +struct SignalWatch +{ + int signo; + int pipe_read_end; + int pipe_write_end; + struct sigaction old_action; + SignalFunc handler; + gpointer user_data; + + SignalWatch *next; +}; + +/* This is not a GHashTable because I don't trust g_hash_table_lookup() + * to be callable from a signal handler. + */ +static SignalWatch *signal_watches; + +static SignalWatch * +lookup_watch (int signo) +{ + SignalWatch *w; + + for (w = signal_watches; w != NULL; w = w->next) + { + if (w->signo == signo) + return w; + } + + return NULL; +} + +static void +add_watch (SignalWatch *watch) +{ + g_return_if_fail (lookup_watch (watch->signo) == NULL); + + watch->next = signal_watches; + signal_watches = watch; +} + +static void +remove_watch (SignalWatch *watch) +{ + /* it's either the first one in the list, or it is the ->next + * for some other watch + */ + if (watch == signal_watches) + { + signal_watches = watch->next; + watch->next = NULL; + } + else + { + SignalWatch *w; + + for (w = signal_watches; w && w->next; w = w->next) + { + if (watch == w->next) + { + w->next = w->next->next; + watch->next = NULL; + break; + } + } + } +} + +static void +signal_handler (int signo, siginfo_t *info, void *data) +{ + SignalWatch *watch; + char x = 'x'; + + watch = lookup_watch (signo); + write (watch->pipe_write_end, &x, 1); +} + +static void +on_read (gpointer data) +{ + SignalWatch *watch = data; + char x; + + read (watch->pipe_read_end, &x, 1); + + watch->handler (watch->signo, watch->user_data); +} + +static void +create_pipe (int *read_end, + int *write_end) +{ + int p[2]; + + pipe (p); + + if (read_end) + *read_end = p[0]; + + if (write_end) + *write_end = p[1]; +} + +static void +install_signal_handler (int signo, struct sigaction *old_action) +{ + struct sigaction action; + + memset (&action, 0, sizeof (action)); + + action.sa_sigaction = signal_handler; + action.sa_flags = SA_SIGINFO; + + sigaction (signo, &action, old_action); +} + +gboolean +signal_set_handler (int signo, + SignalFunc handler, + gpointer data, + GError **err) +{ + SignalWatch *watch; + + g_return_val_if_fail (lookup_watch (signo) == NULL, FALSE); + + watch = g_new0 (SignalWatch, 1); + + create_pipe (&watch->pipe_read_end, &watch->pipe_write_end); + watch->signo = signo; + watch->handler = handler; + watch->user_data = data; + + add_watch (watch); + + fd_add_watch (watch->pipe_read_end, watch); + fd_set_read_callback (watch->pipe_read_end, on_read); + + install_signal_handler (signo, &watch->old_action); + + return TRUE; +} + +void +signal_unset_handler (int signo) +{ + SignalWatch *watch; + + watch = lookup_watch (signo); + + g_return_if_fail (watch != NULL); + + sigaction (signo, &watch->old_action, NULL); + + fd_remove_watch (watch->pipe_read_end); + + close (watch->pipe_read_end); + close (watch->pipe_write_end); + + remove_watch (watch); + + g_free (watch); +} diff --git a/signal-handler.h b/signal-handler.h new file mode 100644 index 0000000..44f3dde --- /dev/null +++ b/signal-handler.h @@ -0,0 +1,27 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */ + +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk) + * + * This library 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; either version 2 of the License, or + * (at your option) any later version. + * + * This library 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 library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +typedef void (* SignalFunc) (int signo, gpointer data); + +gboolean signal_set_handler (int signo, + SignalFunc handler, + gpointer data, + GError **err); +void signal_unset_handler (int signo); diff --git a/sysprof-text.c b/sysprof-text.c index 1e2d416..5b48ed3 100644 --- a/sysprof-text.c +++ b/sysprof-text.c @@ -1,21 +1,182 @@ -/* - * Plan: +/* Sysprof -- Sampling, systemwide CPU profiler + * Copyright 2005, Lorenzo Colitti * - * blocking_read() + * 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; either version 2 of the License, or + * (at your option) any later version. * - * select (fd); - * if (readable) - * read(); + * 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. * - * - * handle SIGUSR1 - * write spam to commandline given file - * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include + +#include +#include +#include +#include +#include +#include + +#include "stackstash.h" +#include "module/sysprof-module.h" +#include "profile.h" +#include "process.h" +#include "watch.h" +#include "signal-handler.h" + +typedef struct Application Application; +struct Application +{ + int fd; + StackStash *stack_stash; + char * outfile; + GMainLoop * main_loop; +}; + +void +read_trace (StackStash *stash, + SysprofStackTrace *trace, + GTimeVal now) +{ + Process *process; + int i; + + process = process_get_from_pid (trace->pid); + + for (i = 0; i < trace->n_addresses; ++i) + { + process_ensure_map (process, trace->pid, + (gulong)trace->addresses[i]); + } + + stack_stash_add_trace ( + stash, process, + (gulong *)trace->addresses, trace->n_addresses, 1); +} + +void +on_read (gpointer data) +{ + Application *app = data; + SysprofStackTrace trace; + int bytesread; + GTimeVal now; + + bytesread = read (app->fd, &trace, sizeof (trace)); + g_get_current_time (&now); + + if (bytesread < 0) + { + perror("read"); + return; + } + + if (bytesread > 0) + read_trace (app->stack_stash, &trace, now); +} + +void +dump_data (Application *app) +{ + GError *err = NULL; + Profile *profile = profile_new (app->stack_stash); + + profile_save (profile, app->outfile, &err); + + if (err) + { + fprintf (stderr, "%s: %s\n", app->outfile, err->message); + exit (1); + } +} + +void +signal_handler (int signo, gpointer data) +{ + Application *app = data; + + g_print ("signal %d caught: dumping data\n", signo); + + dump_data (app); + + g_main_loop_quit (app->main_loop); +} + +#define SYSPROF_FILE "/proc/sysprof-trace" + +static void +no_module (void) +{ + perror (SYSPROF_FILE); + fprintf (stderr, + "\n" + "Can't open /proc/sysprof-trace. You need to insert " + "the sysprof kernel module. Run\n" + "\n" + " modprobe sysprof-module\n" + "\n" + "as root.\n"); +} + +static void +usage (const char *name) +{ + fprintf (stderr, + "\n" + "Usage: \n" + " %s \n" + "\n" + "On SIGTERM or SIGINT (Ctrl-C) write the profile to \n" + "\n", + name); +} int -main () +main (int argc, + char *argv[]) { + gboolean quit; + Application *app = g_new0 (Application, 1); + int fd; + + fd = open (SYSPROF_FILE, O_RDONLY); + + quit = FALSE; + + if (fd < 0) + { + no_module (); + quit = TRUE; + } + + if (argc < 2) + { + usage (argv[0]); + quit = TRUE; + } + + if (quit) + return -1; + + app->fd = fd; + app->outfile = g_strdup (argv[1]); + app->stack_stash = stack_stash_new (); + app->main_loop = g_main_loop_new (NULL, 0); + + signal_set_handler (SIGTERM, signal_handler, app, NULL); + signal_set_handler (SIGINT, signal_handler, app, NULL); + + fd_add_watch (app->fd, app); + fd_set_read_callback (app->fd, on_read); + g_main_loop_run (app->main_loop); + return 0; } diff --git a/sysprof.c b/sysprof.c index 39db569..680c9a8 100644 --- a/sysprof.c +++ b/sysprof.c @@ -1,4 +1,4 @@ -/* Sysprof -- Sampling, systemwide CPU profiler +/* Sysprof -- Sampling, systemwide CPU profiler * Copyright 2004, Red Hat, Inc. * Copyright 2004, 2005, Soeren Sandmann * @@ -250,76 +250,6 @@ set_busy (GtkWidget *widget, gboolean busy) gdk_flush (); } -#if 0 -static gchar * -get_name (pid_t pid) -{ - char *cmdline; - char *name = g_strdup_printf ("/proc/%d/cmdline", pid); - - if (g_file_get_contents (name, &cmdline, NULL, NULL)) - return cmdline; - else - return g_strdup (""); -} -#endif - -#if 0 -static void -on_timeout (gpointer data) -{ - Application *app = data; - GList *pids, *list; - int mypid = getpid(); - - pids = list_processes (); - - for (list = pids; list != NULL; list = list->next) - { - int pid = GPOINTER_TO_INT (list->data); - - if (pid == mypid) - continue; - - if (get_process_state (pid) == PROCESS_RUNNING) - { - Process *process; - SysprofStackTrace trace; - int i; - - if (!generate_stack_trace (pid, &trace)) - { - continue; - } - - process = process_get_from_pid (pid); - -#if 0 - process_ensure_map (process, trace.pid, - (gulong)trace.addresses[i]); -#endif - - g_print ("n addr: %d\n", trace.n_addresses); - for (i = 0; i < trace.n_addresses; ++i) - process_ensure_map (process, trace.pid, - (gulong)trace.addresses[i]); - g_assert (!app->generating_profile); - - stack_stash_add_trace ( - app->stash, process, - (gulong *)trace.addresses, trace.n_addresses, 1); - - app->n_samples++; - } - } - - update_sensitivity (app); - g_list_free (pids); - - return TRUE; -} -#endif - static double timeval_to_ms (const GTimeVal *timeval) { -- 2.7.4