mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2025-12-31 20:36:25 +00:00
380 lines
8.0 KiB
C
380 lines
8.0 KiB
C
/* Sysprof -- Sampling, systemwide CPU profiler
|
|
* Copyright 2004, Red Hat, Inc.
|
|
* Copyright 2004, 2005, Soeren Sandmann
|
|
*
|
|
* 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.
|
|
*
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
#include "stackstash.h"
|
|
#include "collector.h"
|
|
#include "module/sysprof-module.h"
|
|
#include "watch.h"
|
|
#include "process.h"
|
|
#include "elfparser.h"
|
|
|
|
#include <errno.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/types.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
|
|
static void set_no_module_error (GError **err);
|
|
static void set_cant_open_error (GError **err, int eno);
|
|
|
|
struct Collector
|
|
{
|
|
CollectorFunc callback;
|
|
gpointer data;
|
|
|
|
StackStash * stash;
|
|
int fd;
|
|
GTimeVal latest_reset;
|
|
int n_samples;
|
|
};
|
|
|
|
/* callback is called whenever a new sample arrives */
|
|
Collector *
|
|
collector_new (CollectorFunc callback,
|
|
gpointer data)
|
|
{
|
|
Collector *collector = g_new0 (Collector, 1);
|
|
|
|
collector->callback = callback;
|
|
collector->data = data;
|
|
collector->fd = -1;
|
|
collector->stash = NULL;
|
|
|
|
collector_reset (collector);
|
|
|
|
return collector;
|
|
}
|
|
|
|
static double
|
|
timeval_to_ms (const GTimeVal *timeval)
|
|
{
|
|
return (timeval->tv_sec * G_USEC_PER_SEC + timeval->tv_usec) / 1000.0;
|
|
}
|
|
|
|
static double
|
|
time_diff (const GTimeVal *first,
|
|
const GTimeVal *second)
|
|
{
|
|
double first_ms = timeval_to_ms (first);
|
|
double second_ms = timeval_to_ms (second);
|
|
|
|
return first_ms - second_ms;
|
|
}
|
|
|
|
#define RESET_DEAD_PERIOD 250
|
|
|
|
static void
|
|
add_trace_to_stash (SysprofStackTrace *trace,
|
|
StackStash *stash)
|
|
{
|
|
int i;
|
|
gulong *addrs;
|
|
Process *process = process_get_from_pid (trace->pid);
|
|
|
|
addrs = g_new (gulong, trace->n_addresses + 1);
|
|
|
|
for (i = 0; i < trace->n_addresses; ++i)
|
|
{
|
|
process_ensure_map (process, trace->pid,
|
|
(gulong)trace->addresses[i]);
|
|
|
|
addrs[i] = (gulong)trace->addresses[i];
|
|
}
|
|
|
|
addrs[i] = (gulong)process;
|
|
|
|
stack_stash_add_trace (
|
|
stash, addrs, trace->n_addresses + 1, 1);
|
|
|
|
g_free (addrs);
|
|
}
|
|
|
|
static void
|
|
on_read (gpointer data)
|
|
{
|
|
SysprofStackTrace trace;
|
|
Collector *collector = data;
|
|
GTimeVal now;
|
|
int rd;
|
|
double diff;
|
|
|
|
rd = read (collector->fd, &trace, sizeof (trace));
|
|
|
|
if (rd == -1 && errno == EWOULDBLOCK)
|
|
return;
|
|
|
|
g_get_current_time (&now);
|
|
|
|
/* After a reset we ignore samples for a short period so that
|
|
* a reset will actually cause 'samples' to become 0
|
|
*/
|
|
diff = time_diff (&now, &collector->latest_reset);
|
|
|
|
if (diff >= 0.0 && diff < RESET_DEAD_PERIOD)
|
|
return;
|
|
|
|
#if 0
|
|
{
|
|
int i;
|
|
g_print ("pid: %d (%d)\n", trace.pid, trace.n_addresses);
|
|
for (i=0; i < trace.n_addresses; ++i)
|
|
g_print ("rd: %08x\n", trace.addresses[i]);
|
|
g_print ("-=-\n");
|
|
}
|
|
#endif
|
|
|
|
if (rd > 0)
|
|
{
|
|
add_trace_to_stash (&trace, collector->stash);
|
|
|
|
collector->n_samples++;
|
|
|
|
if (collector->callback)
|
|
collector->callback (collector->data);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
load_module (void)
|
|
{
|
|
int exit_status = -1;
|
|
char *dummy1, *dummy2;
|
|
|
|
if (g_spawn_command_line_sync ("/sbin/modprobe sysprof-module",
|
|
&dummy1, &dummy2,
|
|
&exit_status,
|
|
NULL))
|
|
{
|
|
if (WIFEXITED (exit_status))
|
|
exit_status = WEXITSTATUS (exit_status);
|
|
|
|
g_free (dummy1);
|
|
g_free (dummy2);
|
|
}
|
|
|
|
return (exit_status == 0);
|
|
}
|
|
|
|
static gboolean
|
|
open_fd (Collector *collector,
|
|
GError **err)
|
|
{
|
|
int fd;
|
|
|
|
fd = open (SYSPROF_FILE, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
if (load_module())
|
|
{
|
|
GTimer *timer = g_timer_new ();
|
|
|
|
while (fd < 0 && g_timer_elapsed (timer, NULL) < 0.5)
|
|
{
|
|
/* Wait for udev to discover the new device */
|
|
usleep (100000);
|
|
|
|
errno = 0;
|
|
fd = open (SYSPROF_FILE, O_RDONLY);
|
|
}
|
|
|
|
g_timer_destroy (timer);
|
|
|
|
if (fd < 0)
|
|
{
|
|
set_cant_open_error (err, errno);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (fd < 0)
|
|
{
|
|
set_no_module_error (err);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
collector->fd = fd;
|
|
fd_add_watch (collector->fd, collector);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
collector_start (Collector *collector,
|
|
GError **err)
|
|
{
|
|
if (collector->fd < 0 && !open_fd (collector, err))
|
|
return FALSE;
|
|
|
|
fd_set_read_callback (collector->fd, on_read);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
collector_stop (Collector *collector)
|
|
{
|
|
if (collector->fd >= 0)
|
|
{
|
|
fd_remove_watch (collector->fd);
|
|
|
|
close (collector->fd);
|
|
collector->fd = -1;
|
|
}
|
|
}
|
|
|
|
void
|
|
collector_reset (Collector *collector)
|
|
{
|
|
if (collector->stash)
|
|
stack_stash_unref (collector->stash);
|
|
|
|
process_flush_caches();
|
|
|
|
collector->stash = stack_stash_new (NULL);
|
|
collector->n_samples = 0;
|
|
|
|
g_get_current_time (&collector->latest_reset);
|
|
}
|
|
|
|
int
|
|
collector_get_n_samples (Collector *collector)
|
|
{
|
|
return collector->n_samples;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
StackStash *resolved_stash;
|
|
GHashTable *unique_symbols;
|
|
} ResolveInfo;
|
|
|
|
static char *
|
|
unique_dup (GHashTable *unique_symbols, const char *sym)
|
|
{
|
|
char *result;
|
|
|
|
result = g_hash_table_lookup (unique_symbols, sym);
|
|
if (!result)
|
|
{
|
|
result = elf_demangle (sym);
|
|
g_hash_table_insert (unique_symbols, (char *)sym, result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static char *
|
|
lookup_symbol (Process *process, gpointer address, GHashTable *unique_symbols)
|
|
{
|
|
const char *sym = process_lookup_symbol (process, (gulong)address);
|
|
|
|
return unique_dup (unique_symbols, sym);
|
|
}
|
|
|
|
static void
|
|
resolve_symbols (GList *trace, gint size, gpointer data)
|
|
{
|
|
GList *list;
|
|
ResolveInfo *info = data;
|
|
Process *process = g_list_last (trace)->data;
|
|
GPtrArray *resolved_trace = g_ptr_array_new ();
|
|
|
|
for (list = trace; list && list->next; list = list->next)
|
|
{
|
|
gpointer address = list->data;
|
|
char *symbol;
|
|
|
|
symbol = lookup_symbol (process, address, info->unique_symbols);
|
|
|
|
g_ptr_array_add (resolved_trace, symbol);
|
|
}
|
|
|
|
g_ptr_array_add (resolved_trace,
|
|
unique_dup (info->unique_symbols,
|
|
(char *)process_get_cmdline (process)));
|
|
g_ptr_array_add (resolved_trace,
|
|
unique_dup (info->unique_symbols,
|
|
"Everything"));
|
|
|
|
stack_stash_add_trace (info->resolved_stash,
|
|
(gulong *)resolved_trace->pdata,
|
|
resolved_trace->len, size);
|
|
|
|
g_ptr_array_free (resolved_trace, TRUE);
|
|
}
|
|
|
|
Profile *
|
|
collector_create_profile (Collector *collector)
|
|
{
|
|
ResolveInfo info;
|
|
Profile *profile;
|
|
|
|
info.resolved_stash = stack_stash_new ((GDestroyNotify)g_free);
|
|
info.unique_symbols = g_hash_table_new (g_direct_hash, g_direct_equal);
|
|
|
|
stack_stash_foreach (collector->stash, resolve_symbols, &info);
|
|
|
|
/* FIXME: we are leaking the value strings in info.unique_symbols */
|
|
|
|
g_hash_table_destroy (info.unique_symbols);
|
|
|
|
profile = profile_new (info.resolved_stash);
|
|
|
|
stack_stash_unref (info.resolved_stash);
|
|
|
|
return profile;
|
|
}
|
|
|
|
static void
|
|
set_no_module_error (GError **err)
|
|
{
|
|
g_set_error (err,
|
|
COLLECTOR_ERROR,
|
|
COLLECTOR_ERROR_CANT_OPEN_FILE,
|
|
"Can't open " SYSPROF_FILE ". You need to insert "
|
|
"the sysprof kernel module. Run\n"
|
|
"\n"
|
|
" modprobe sysprof-module\n"
|
|
"\n"
|
|
"as root");
|
|
}
|
|
|
|
static void
|
|
set_cant_open_error (GError **err,
|
|
int eno)
|
|
{
|
|
g_set_error (err,
|
|
COLLECTOR_ERROR,
|
|
COLLECTOR_ERROR_CANT_OPEN_FILE,
|
|
"Can't open " SYSPROF_FILE ": %s",
|
|
g_strerror (eno));
|
|
}
|
|
|
|
GQuark
|
|
collector_error_quark (void)
|
|
{
|
|
static GQuark q = 0;
|
|
|
|
if (q == 0)
|
|
q = g_quark_from_static_string ("collector-error-quark");
|
|
|
|
return q;
|
|
}
|