mirror of
https://github.com/varun-r-mallya/sysprof.git
synced 2026-02-12 16:10:54 +00:00
Add support for looking up kernel symbols from /proc/kallsyms
2007-09-16 Soren Sandmann <sandmann@daimi.au.dk> * process.c (process_lookup_kernel_symbol): Add support for looking up kernel symbols from /proc/kallsyms * sysprof-text.c (dump_data): Print note that the file is being saved. * module/sysprof-module.[ch] (timer_notify): Send a copy of the kernel stack to userspace. * collector.c: Do kernel symbol lookups. svn path=/trunk/; revision=372
This commit is contained in:
committed by
Søren Sandmann Pedersen
parent
39cb26654a
commit
f1cbdbf27c
13
ChangeLog
13
ChangeLog
@ -1,3 +1,16 @@
|
|||||||
|
2007-09-16 Soren Sandmann <sandmann@daimi.au.dk>
|
||||||
|
|
||||||
|
* process.c (process_lookup_kernel_symbol): Add support for
|
||||||
|
looking up kernel symbols from /proc/kallsyms
|
||||||
|
|
||||||
|
* sysprof-text.c (dump_data): Print note that the file is being
|
||||||
|
saved.
|
||||||
|
|
||||||
|
* module/sysprof-module.[ch] (timer_notify): Send a copy of the
|
||||||
|
kernel stack to userspace.
|
||||||
|
|
||||||
|
* collector.c: Do kernel symbol lookups.
|
||||||
|
|
||||||
2007-08-26 Soren Sandmann <sandmann@daimi.au.dk>
|
2007-08-26 Soren Sandmann <sandmann@daimi.au.dk>
|
||||||
|
|
||||||
* profile.c (profile_get_size): Compute the size by simply summing
|
* profile.c (profile_get_size): Compute the size by simply summing
|
||||||
|
|||||||
46
TODO
46
TODO
@ -23,6 +23,15 @@ Before 1.0.4:
|
|||||||
|
|
||||||
Before 1.2:
|
Before 1.2:
|
||||||
|
|
||||||
|
* Performance:
|
||||||
|
Switching between descendant views is a slow:
|
||||||
|
- gtk_tree_store_get_path() is O(n^2) and accounts
|
||||||
|
for 43% of the time.
|
||||||
|
- GObject signal emission overhead accounts for 18% of
|
||||||
|
the time.
|
||||||
|
Consider adding a forked version of GtkTreeStore with
|
||||||
|
performance fixes.
|
||||||
|
|
||||||
* Make sure that labels look decent in case of "No Map" etc.
|
* Make sure that labels look decent in case of "No Map" etc.
|
||||||
|
|
||||||
* Elf bugs:
|
* Elf bugs:
|
||||||
@ -64,12 +73,27 @@ Before 1.2:
|
|||||||
Unless of course, we store the entire stack in
|
Unless of course, we store the entire stack in
|
||||||
the stackstash. This may use way too much memory though.
|
the stackstash. This may use way too much memory though.
|
||||||
|
|
||||||
- vdso
|
- Locking, possibly useful code:
|
||||||
- assume its the same across processes, just look at
|
|
||||||
sysprof's own copy.
|
/* In principle we should use get_task_mm() but
|
||||||
Done: vdso is done now
|
* that will use task_lock() leading to deadlock
|
||||||
- send copy of it to userspace once, or for every
|
* if somebody already has the lock
|
||||||
sample.
|
*/
|
||||||
|
if (spin_is_locked (¤t->alloc_lock))
|
||||||
|
printk ("alreadylocked\n");
|
||||||
|
{
|
||||||
|
struct mm_struct *mm = current->mm;
|
||||||
|
if (mm)
|
||||||
|
{
|
||||||
|
printk (KERN_ALERT "stack size: %d (%d)\n",
|
||||||
|
mm->start_stack - regs->REG_STACK_PTR,
|
||||||
|
current->pid);
|
||||||
|
|
||||||
|
stacksize = mm->start_stack - regs->REG_STACK_PTR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
stacksize = 1;
|
||||||
|
}
|
||||||
|
|
||||||
- regular elf
|
- regular elf
|
||||||
- usually have eh_frame section which is mapped into memory
|
- usually have eh_frame section which is mapped into memory
|
||||||
@ -713,7 +737,15 @@ Later:
|
|||||||
of outstanding disk requests.
|
of outstanding disk requests.
|
||||||
|
|
||||||
|
|
||||||
-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ALREADY DONE -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ALREADY DONE: -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||||
|
|
||||||
|
|
||||||
|
* vdso
|
||||||
|
- assume its the same across processes, just look at
|
||||||
|
sysprof's own copy.
|
||||||
|
Done: vdso is done now
|
||||||
|
- send copy of it to userspace once, or for every
|
||||||
|
sample.
|
||||||
|
|
||||||
* Various:
|
* Various:
|
||||||
- decorate_node should be done lazily
|
- decorate_node should be done lazily
|
||||||
|
|||||||
100
collector.c
100
collector.c
@ -92,23 +92,53 @@ add_trace_to_stash (const SysprofStackTrace *trace,
|
|||||||
gulong *addrs;
|
gulong *addrs;
|
||||||
Process *process = process_get_from_pid (trace->pid);
|
Process *process = process_get_from_pid (trace->pid);
|
||||||
int n_addresses;
|
int n_addresses;
|
||||||
|
int n_kernel_words;
|
||||||
|
int a;
|
||||||
|
|
||||||
n_addresses = trace->n_addresses;
|
n_addresses = trace->n_addresses;
|
||||||
|
n_kernel_words = trace->n_kernel_words;
|
||||||
|
|
||||||
addrs = g_new (gulong, n_addresses + 1);
|
addrs = g_new (gulong, n_addresses + n_kernel_words + 2);
|
||||||
|
|
||||||
|
a = 0;
|
||||||
|
/* Add kernel addresses */
|
||||||
|
if (trace->n_kernel_words)
|
||||||
|
{
|
||||||
|
for (i = 0; i < trace->n_kernel_words; ++i)
|
||||||
|
{
|
||||||
|
gulong addr = (gulong)trace->kernel_stack[i];
|
||||||
|
|
||||||
|
if (process_is_kernel_address (addr))
|
||||||
|
addrs[a++] = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add kernel marker */
|
||||||
|
addrs[a++] = 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add user addresses */
|
||||||
|
|
||||||
for (i = 0; i < n_addresses; ++i)
|
for (i = 0; i < n_addresses; ++i)
|
||||||
{
|
{
|
||||||
process_ensure_map (process, trace->pid,
|
gulong addr = (gulong)trace->addresses[i];
|
||||||
(gulong)trace->addresses[i]);
|
|
||||||
|
|
||||||
addrs[i] = (gulong)trace->addresses[i];
|
process_ensure_map (process, trace->pid, addr);
|
||||||
|
addrs[a++] = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs[i] = (gulong)process;
|
/* Add process */
|
||||||
|
addrs[a++] = (gulong)process;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (a != n_addresses)
|
||||||
|
g_print ("a: %d, n_addresses: %d, kernel words: %d\n trace->nad %d",
|
||||||
|
a, n_addresses, trace->n_kernel_words, trace->n_addresses);
|
||||||
|
|
||||||
|
g_assert (a == n_addresses);
|
||||||
|
#endif
|
||||||
|
|
||||||
stack_stash_add_trace (
|
stack_stash_add_trace (
|
||||||
stash, addrs, n_addresses + 1, 1);
|
stash, addrs, a, 1);
|
||||||
|
|
||||||
g_free (addrs);
|
g_free (addrs);
|
||||||
}
|
}
|
||||||
@ -263,6 +293,12 @@ collector_start (Collector *collector,
|
|||||||
if (collector->fd < 0 && !open_fd (collector, err))
|
if (collector->fd < 0 && !open_fd (collector, err))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
/* Hack to make sure we parse the kernel symbols before
|
||||||
|
* starting collection, so the parsing doesn't interfere
|
||||||
|
* with the profiling.
|
||||||
|
*/
|
||||||
|
process_is_kernel_address (0);
|
||||||
|
|
||||||
fd_set_read_callback (collector->fd, on_read);
|
fd_set_read_callback (collector->fd, on_read);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -330,15 +366,44 @@ unique_dup (GHashTable *unique_symbols, const char *sym)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char *
|
static char *
|
||||||
lookup_symbol (Process *process, gpointer address, GHashTable *unique_symbols)
|
lookup_symbol (Process *process, gpointer address,
|
||||||
|
GHashTable *unique_symbols,
|
||||||
|
gboolean kernel,
|
||||||
|
gboolean first_kernel_addr)
|
||||||
{
|
{
|
||||||
const char *sym;
|
const char *sym;
|
||||||
|
|
||||||
g_assert (process);
|
g_assert (process);
|
||||||
|
|
||||||
sym = process_lookup_symbol (process, (gulong)address);
|
if (kernel)
|
||||||
|
{
|
||||||
|
gulong offset;
|
||||||
|
sym = process_lookup_kernel_symbol ((gulong)address, &offset);
|
||||||
|
|
||||||
return unique_dup (unique_symbols, sym);
|
/* If offset is 0, it is a callback, not a return address */
|
||||||
|
if (offset == 0 && !first_kernel_addr)
|
||||||
|
sym = NULL;
|
||||||
|
|
||||||
|
/* If offset is greater than 4096, then what happened is most
|
||||||
|
* likely that it is the address of something in the gap between the
|
||||||
|
* kernel text and the text of the modules. Rather than assign
|
||||||
|
* this to the last function of the kernel text, we remove it here.
|
||||||
|
*
|
||||||
|
* FIXME: what we really should do is find out where this split
|
||||||
|
* is, and act accordingly.
|
||||||
|
*/
|
||||||
|
if (offset > 4096)
|
||||||
|
sym = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sym = process_lookup_symbol (process, (gulong)address);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sym)
|
||||||
|
return unique_dup (unique_symbols, sym);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -350,15 +415,28 @@ resolve_symbols (GList *trace, gint size, gpointer data)
|
|||||||
Process *process = g_list_last (trace)->data;
|
Process *process = g_list_last (trace)->data;
|
||||||
GPtrArray *resolved_trace = g_ptr_array_new ();
|
GPtrArray *resolved_trace = g_ptr_array_new ();
|
||||||
char *cmdline;
|
char *cmdline;
|
||||||
|
gboolean in_kernel = FALSE;
|
||||||
|
gboolean first_kernel_addr = TRUE;
|
||||||
|
|
||||||
|
for (list = trace; list && list->next; list = list->next)
|
||||||
|
{
|
||||||
|
if (list->data == GINT_TO_POINTER (0x01))
|
||||||
|
in_kernel = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
for (list = trace; list && list->next; list = list->next)
|
for (list = trace; list && list->next; list = list->next)
|
||||||
{
|
{
|
||||||
gpointer address = list->data;
|
gpointer address = list->data;
|
||||||
char *symbol;
|
char *symbol;
|
||||||
|
|
||||||
symbol = lookup_symbol (process, address, info->unique_symbols);
|
if (address == GINT_TO_POINTER (0x01))
|
||||||
|
in_kernel = FALSE;
|
||||||
|
symbol = lookup_symbol (process, address, info->unique_symbols,
|
||||||
|
in_kernel, first_kernel_addr);
|
||||||
|
first_kernel_addr = FALSE;
|
||||||
|
|
||||||
g_ptr_array_add (resolved_trace, symbol);
|
if (symbol)
|
||||||
|
g_ptr_array_add (resolved_trace, symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdline = g_hash_table_lookup (info->unique_cmdlines,
|
cmdline = g_hash_table_lookup (info->unique_cmdlines,
|
||||||
|
|||||||
@ -106,6 +106,12 @@ read_frame (void *frame_pointer, StackFrame *frame)
|
|||||||
|
|
||||||
DEFINE_PER_CPU(int, n_samples);
|
DEFINE_PER_CPU(int, n_samples);
|
||||||
|
|
||||||
|
static int
|
||||||
|
minimum (int a, int b)
|
||||||
|
{
|
||||||
|
return a > b ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef OLD_PROFILE
|
#ifdef OLD_PROFILE
|
||||||
static int timer_notify(struct notifier_block * self, unsigned long val, void * data)
|
static int timer_notify(struct notifier_block * self, unsigned long val, void * data)
|
||||||
#else
|
#else
|
||||||
@ -123,9 +129,6 @@ timer_notify (struct pt_regs *regs)
|
|||||||
StackFrame frame;
|
StackFrame frame;
|
||||||
int result;
|
int result;
|
||||||
static atomic_t in_timer_notify = ATOMIC_INIT(1);
|
static atomic_t in_timer_notify = ATOMIC_INIT(1);
|
||||||
#if 0
|
|
||||||
int stacksize;
|
|
||||||
#endif
|
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
n = ++get_cpu_var(n_samples);
|
n = ++get_cpu_var(n_samples);
|
||||||
@ -135,7 +138,6 @@ timer_notify (struct pt_regs *regs)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* 0: locked, 1: unlocked */
|
/* 0: locked, 1: unlocked */
|
||||||
|
|
||||||
if (!atomic_dec_and_test(&in_timer_notify))
|
if (!atomic_dec_and_test(&in_timer_notify))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@ -151,57 +153,50 @@ timer_notify (struct pt_regs *regs)
|
|||||||
|
|
||||||
trace->pid = current->pid;
|
trace->pid = current->pid;
|
||||||
|
|
||||||
|
trace->n_kernel_words = 0;
|
||||||
|
trace->n_addresses = 0;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
if (!is_user)
|
if (!is_user)
|
||||||
{
|
{
|
||||||
trace->addresses[i++] = (void *)0x01;
|
int n_bytes;
|
||||||
regs = (void *)current->thread.REG_STACK_PTR0 - sizeof (struct pt_regs);
|
char *esp;
|
||||||
|
char *eos;
|
||||||
|
|
||||||
|
trace->kernel_stack[0] = (void *)regs->REG_INS_PTR;
|
||||||
|
trace->n_kernel_words = 1;
|
||||||
|
|
||||||
|
/* The timer interrupt happened in kernel mode. When this
|
||||||
|
* happens the registers are pushed on the stack, _except_
|
||||||
|
* esp. So we can't use regs->esp to copy the stack pointer.
|
||||||
|
* Instead we use the fact that the regs pointer itself
|
||||||
|
* points to the stack.
|
||||||
|
*/
|
||||||
|
esp = (char *)regs + sizeof (struct pt_regs);
|
||||||
|
eos = (char *)current->thread.REG_STACK_PTR0 - sizeof (struct pt_regs);
|
||||||
|
|
||||||
|
n_bytes = minimum ((char *)eos - esp,
|
||||||
|
sizeof (trace->kernel_stack));
|
||||||
|
|
||||||
|
if (n_bytes > 0) {
|
||||||
|
memcpy (&(trace->kernel_stack[1]), esp, n_bytes);
|
||||||
|
|
||||||
|
trace->n_kernel_words += (n_bytes) / sizeof (void *);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now trace the user stack */
|
||||||
|
regs = (struct pt_regs *)eos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i = 0;
|
||||||
trace->addresses[i++] = (void *)regs->REG_INS_PTR;
|
trace->addresses[i++] = (void *)regs->REG_INS_PTR;
|
||||||
|
|
||||||
frame_pointer = (void *)regs->REG_FRAME_PTR;
|
frame_pointer = (void *)regs->REG_FRAME_PTR;
|
||||||
|
|
||||||
{
|
|
||||||
#if 0
|
|
||||||
/* In principle we should use get_task_mm() but
|
|
||||||
* that will use task_lock() leading to deadlock
|
|
||||||
* if somebody already has the lock
|
|
||||||
*/
|
|
||||||
if (spin_is_locked (¤t->alloc_lock))
|
|
||||||
printk ("alreadylocked\n");
|
|
||||||
{
|
|
||||||
struct mm_struct *mm = current->mm;
|
|
||||||
if (mm)
|
|
||||||
{
|
|
||||||
printk (KERN_ALERT "stack size: %d (%d)\n",
|
|
||||||
mm->start_stack - regs->REG_STACK_PTR,
|
|
||||||
current->pid);
|
|
||||||
|
|
||||||
stacksize = mm->start_stack - regs->REG_STACK_PTR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
stacksize = 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#if 0
|
|
||||||
else
|
|
||||||
printk (KERN_ALERT "could not lock on %d\n", current->pid);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
if (stacksize < 100000)
|
|
||||||
goto out;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (((result = read_frame (frame_pointer, &frame)) == 0) &&
|
while (((result = read_frame (frame_pointer, &frame)) == 0) &&
|
||||||
i < SYSPROF_MAX_ADDRESSES &&
|
i < SYSPROF_MAX_ADDRESSES &&
|
||||||
(unsigned long)frame_pointer >= regs->REG_STACK_PTR)
|
(unsigned long)frame_pointer >= regs->REG_STACK_PTR)
|
||||||
{
|
{
|
||||||
#if 0
|
|
||||||
printk ("frame pointer: %p (retaddr: %p)\n", frame_pointer, frame.return_address);
|
|
||||||
#endif
|
|
||||||
trace->addresses[i++] = (void *)frame.return_address;
|
trace->addresses[i++] = (void *)frame.return_address;
|
||||||
frame_pointer = (StackFrame *)frame.next;
|
frame_pointer = (StackFrame *)frame.next;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,16 +25,18 @@ typedef struct SysprofStackInfo SysprofStackInfo;
|
|||||||
typedef struct SysprofMmapArea SysprofMmapArea;
|
typedef struct SysprofMmapArea SysprofMmapArea;
|
||||||
|
|
||||||
#define SYSPROF_N_TRACES 64
|
#define SYSPROF_N_TRACES 64
|
||||||
#define SYSPROF_MAX_ADDRESSES 1021 /* to make it one page wide */
|
#define SYSPROF_MAX_ADDRESSES 1020 /* to make it three pages wide */
|
||||||
|
|
||||||
struct SysprofStackTrace
|
struct SysprofStackTrace
|
||||||
{
|
{
|
||||||
|
void *kernel_stack[1024];
|
||||||
|
void *addresses[SYSPROF_MAX_ADDRESSES];
|
||||||
|
int n_kernel_words;
|
||||||
|
int n_addresses; /* note: this can be 1 if the process was compiled
|
||||||
|
* with -fomit-frame-pointer or is otherwise weird
|
||||||
|
*/
|
||||||
int pid; /* -1 if in kernel */
|
int pid; /* -1 if in kernel */
|
||||||
int truncated;
|
int truncated;
|
||||||
int n_addresses; /* note: this can be 1 if the process was compiled
|
|
||||||
* with -fomit-frame-pointer or is otherwise weird
|
|
||||||
*/
|
|
||||||
void *addresses[SYSPROF_MAX_ADDRESSES];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SysprofMmapArea
|
struct SysprofMmapArea
|
||||||
|
|||||||
169
process.c
169
process.c
@ -3,7 +3,7 @@
|
|||||||
* Copyright 2002, Kristian Rietveld
|
* Copyright 2002, Kristian Rietveld
|
||||||
*
|
*
|
||||||
* Sysprof -- Sampling, systemwide CPU profiler
|
* Sysprof -- Sampling, systemwide CPU profiler
|
||||||
* Copyright 2004-2005 Soeren Sandmann
|
* Copyright 2004-2007 Soeren Sandmann
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@ -285,7 +285,7 @@ static int
|
|||||||
page_size (void)
|
page_size (void)
|
||||||
{
|
{
|
||||||
static int page_size;
|
static int page_size;
|
||||||
static gboolean has_page_size;
|
static gboolean has_page_size = FALSE;
|
||||||
|
|
||||||
if (!has_page_size)
|
if (!has_page_size)
|
||||||
{
|
{
|
||||||
@ -489,9 +489,15 @@ find_kernel_binary (void)
|
|||||||
return binary;
|
return binary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gulong address;
|
||||||
|
char *name;
|
||||||
|
} KernelSymbol;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
parse_kallsym_line (const char *line,
|
parse_kallsym_line (const char *line,
|
||||||
GHashTable *table)
|
GArray *table)
|
||||||
{
|
{
|
||||||
char **tokens = g_strsplit_set (line, " \t", -1);
|
char **tokens = g_strsplit_set (line, " \t", -1);
|
||||||
|
|
||||||
@ -502,19 +508,25 @@ parse_kallsym_line (const char *line,
|
|||||||
|
|
||||||
address = strtoul (tokens[0], &endptr, 16);
|
address = strtoul (tokens[0], &endptr, 16);
|
||||||
|
|
||||||
if (*endptr == '\0')
|
if (*endptr == '\0' &&
|
||||||
|
(strcmp (tokens[1], "T") == 0 ||
|
||||||
|
strcmp (tokens[1], "t") == 0))
|
||||||
{
|
{
|
||||||
g_hash_table_insert (
|
KernelSymbol sym;
|
||||||
table, GUINT_TO_POINTER (address), g_strdup (tokens[2]));
|
|
||||||
|
sym.address = address;
|
||||||
|
sym.name = g_strdup (tokens[2]);
|
||||||
|
|
||||||
|
g_array_append_val (table, sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_strfreev (tokens);
|
g_strfreev (tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
parse_kallsyms (const char *kallsyms,
|
parse_kallsyms (const char *kallsyms,
|
||||||
GHashTable *table)
|
GArray *table)
|
||||||
{
|
{
|
||||||
const char *sol;
|
const char *sol;
|
||||||
const char *eol;
|
const char *eol;
|
||||||
@ -532,47 +544,133 @@ parse_kallsyms (const char *kallsyms,
|
|||||||
sol = eol + 1;
|
sol = eol + 1;
|
||||||
eol = strchr (sol, '\n');
|
eol = strchr (sol, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (table->len <= 1)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GHashTable *
|
static int
|
||||||
|
compare_syms (gconstpointer a, gconstpointer b)
|
||||||
|
{
|
||||||
|
const KernelSymbol *sym_a = a;
|
||||||
|
const KernelSymbol *sym_b = b;
|
||||||
|
|
||||||
|
if (sym_a->address > sym_b->address)
|
||||||
|
return 1;
|
||||||
|
else if (sym_a->address == sym_b->address)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GArray *
|
||||||
get_kernel_symbols (void)
|
get_kernel_symbols (void)
|
||||||
{
|
{
|
||||||
static gboolean read_symbols = FALSE;
|
static GArray *kernel_syms;
|
||||||
static GHashTable *kernel_syms;
|
static gboolean initialized = FALSE;
|
||||||
|
|
||||||
if (!read_symbols)
|
if (!initialized)
|
||||||
{
|
{
|
||||||
char *kallsyms;
|
char *kallsyms;
|
||||||
g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL);
|
if (g_file_get_contents ("/proc/kallsyms", &kallsyms, NULL, NULL))
|
||||||
|
|
||||||
if (kallsyms)
|
|
||||||
{
|
{
|
||||||
kernel_syms = g_hash_table_new_full (g_direct_hash, g_direct_equal,
|
if (kallsyms)
|
||||||
NULL, g_free);
|
{
|
||||||
|
kernel_syms = g_array_new (TRUE, TRUE, sizeof (KernelSymbol));
|
||||||
|
|
||||||
parse_kallsyms (kallsyms, kernel_syms);
|
if (parse_kallsyms (kallsyms, kernel_syms))
|
||||||
|
{
|
||||||
g_free (kallsyms);
|
g_array_sort (kernel_syms, compare_syms);
|
||||||
g_hash_table_destroy (kernel_syms);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_array_free (kernel_syms, TRUE);
|
||||||
|
kernel_syms = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
read_symbols = TRUE;
|
if (!kernel_syms)
|
||||||
|
g_print ("Warning: /proc/kallsyms could not be "
|
||||||
|
"read. Kernel symbols will not be available\n");
|
||||||
|
|
||||||
|
initialized = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return kernel_syms;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
gboolean
|
||||||
lookup_kernel_symbol (gulong address)
|
process_is_kernel_address (gulong address)
|
||||||
{
|
{
|
||||||
static const char *const kernel = "In kernel";
|
GArray *ksyms = get_kernel_symbols ();
|
||||||
|
|
||||||
return kernel;
|
if (ksyms &&
|
||||||
|
address >= g_array_index (ksyms, KernelSymbol, 0).address &&
|
||||||
|
address < g_array_index (ksyms, KernelSymbol, ksyms->len - 1).address)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static KernelSymbol *
|
||||||
|
do_lookup (KernelSymbol *symbols,
|
||||||
|
gulong address,
|
||||||
|
int first,
|
||||||
|
int last)
|
||||||
|
{
|
||||||
|
if (address >= symbols[last].address)
|
||||||
|
{
|
||||||
|
return &(symbols[last]);
|
||||||
|
}
|
||||||
|
else if (last - first < 3)
|
||||||
|
{
|
||||||
|
while (last >= first)
|
||||||
|
{
|
||||||
|
if (address >= symbols[last].address)
|
||||||
|
return &(symbols[last]);
|
||||||
|
|
||||||
|
last--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int mid = (first + last) / 2;
|
||||||
|
|
||||||
|
if (symbols[mid].address > address)
|
||||||
|
return do_lookup (symbols, address, first, mid);
|
||||||
|
else
|
||||||
|
return do_lookup (symbols, address, mid, last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
process_lookup_kernel_symbol (gulong address,
|
||||||
|
gulong *offset)
|
||||||
|
{
|
||||||
|
GArray *ksyms = get_kernel_symbols ();
|
||||||
|
KernelSymbol *result;
|
||||||
|
|
||||||
|
if (ksyms->len == 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
result = do_lookup ((KernelSymbol *)ksyms->data, address, 0, ksyms->len - 1);
|
||||||
|
if (result && offset)
|
||||||
|
*offset = address - result->address;
|
||||||
|
|
||||||
|
return result? result->name : NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
const char *
|
||||||
process_lookup_symbol (Process *process, gulong address)
|
process_lookup_symbol (Process *process, gulong address)
|
||||||
{
|
{
|
||||||
|
static const char *const kernel = "kernel";
|
||||||
const BinSymbol *result;
|
const BinSymbol *result;
|
||||||
Map *map = process_locate_map (process, address);
|
Map *map = process_locate_map (process, address);
|
||||||
|
|
||||||
@ -580,12 +678,16 @@ process_lookup_symbol (Process *process, gulong address)
|
|||||||
|
|
||||||
if (address == 0x1)
|
if (address == 0x1)
|
||||||
{
|
{
|
||||||
get_kernel_symbols ();
|
return kernel;
|
||||||
|
|
||||||
return lookup_kernel_symbol (address);
|
|
||||||
}
|
}
|
||||||
else if (!map)
|
else if (!map)
|
||||||
{
|
{
|
||||||
|
gulong offset;
|
||||||
|
const char *res = process_lookup_kernel_symbol (address, &offset);
|
||||||
|
|
||||||
|
if (res && offset != 0)
|
||||||
|
return res;
|
||||||
|
|
||||||
if (!process->undefined)
|
if (!process->undefined)
|
||||||
{
|
{
|
||||||
process->undefined =
|
process->undefined =
|
||||||
@ -616,11 +718,6 @@ process_lookup_symbol (Process *process, gulong address)
|
|||||||
address -= map->start;
|
address -= map->start;
|
||||||
address += map->offset;
|
address += map->offset;
|
||||||
|
|
||||||
#if 0
|
|
||||||
address -= map->start;
|
|
||||||
address += map->offset;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (strcmp (map->filename, "[vdso]") == 0)
|
if (strcmp (map->filename, "[vdso]") == 0)
|
||||||
{
|
{
|
||||||
@ -637,7 +734,7 @@ process_lookup_symbol (Process *process, gulong address)
|
|||||||
if (!bin_file_check_inode (map->bin_file, map->inode))
|
if (!bin_file_check_inode (map->bin_file, map->inode))
|
||||||
{
|
{
|
||||||
/* If the inodes don't match, it's probably because the
|
/* If the inodes don't match, it's probably because the
|
||||||
* file has changed since the process started. Just return
|
* file has changed since the process was started. Just return
|
||||||
* the undefined symbol in that case.
|
* the undefined symbol in that case.
|
||||||
*/
|
*/
|
||||||
address = 0x0;
|
address = 0x0;
|
||||||
|
|||||||
@ -56,5 +56,8 @@ const char * process_lookup_symbol (Process *process,
|
|||||||
const char * process_get_cmdline (Process *process);
|
const char * process_get_cmdline (Process *process);
|
||||||
void process_flush_caches (void);
|
void process_flush_caches (void);
|
||||||
const guint8 *process_get_vdso_bytes (gsize *length);
|
const guint8 *process_get_vdso_bytes (gsize *length);
|
||||||
|
gboolean process_is_kernel_address (gulong address);
|
||||||
|
const char * process_lookup_kernel_symbol (gulong address,
|
||||||
|
gulong *offset);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "stackstash.h"
|
#include "stackstash.h"
|
||||||
#include "module/sysprof-module.h"
|
#include "module/sysprof-module.h"
|
||||||
@ -46,18 +47,23 @@ static void
|
|||||||
dump_data (Application *app)
|
dump_data (Application *app)
|
||||||
{
|
{
|
||||||
GError *err = NULL;
|
GError *err = NULL;
|
||||||
Profile *profile = collector_create_profile (app->collector);
|
Profile *profile;
|
||||||
|
|
||||||
|
printf ("Saving profile in %s ... ", app->outfile);
|
||||||
|
fflush (stdout);
|
||||||
|
|
||||||
|
profile = collector_create_profile (app->collector);
|
||||||
profile_save (profile, app->outfile, &err);
|
profile_save (profile, app->outfile, &err);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
|
printf ("failed\n");
|
||||||
fprintf (stderr, "Error saving %s: %s\n", app->outfile, err->message);
|
fprintf (stderr, "Error saving %s: %s\n", app->outfile, err->message);
|
||||||
exit (1);
|
exit (1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
printf ("Saved profile in %s\n\n", app->outfile);
|
printf ("done\n\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
19
sysprof.c
19
sysprof.c
@ -461,6 +461,10 @@ fill_main_list (Application *app)
|
|||||||
OBJECT_NAME, object->name,
|
OBJECT_NAME, object->name,
|
||||||
OBJECT_SELF, 100.0 * object->self / profile_size,
|
OBJECT_SELF, 100.0 * object->self / profile_size,
|
||||||
OBJECT_TOTAL, 100.0 * object->total / profile_size,
|
OBJECT_TOTAL, 100.0 * object->total / profile_size,
|
||||||
|
#if 0
|
||||||
|
OBJECT_SELF, (double)object->self,
|
||||||
|
OBJECT_TOTAL, (double)object->total,
|
||||||
|
#endif
|
||||||
OBJECT_OBJECT, object->name,
|
OBJECT_OBJECT, object->name,
|
||||||
-1);
|
-1);
|
||||||
}
|
}
|
||||||
@ -493,6 +497,10 @@ add_node (GtkTreeStore *store,
|
|||||||
DESCENDANTS_NAME, node->name,
|
DESCENDANTS_NAME, node->name,
|
||||||
DESCENDANTS_SELF, 100 * (node->self)/(double)size,
|
DESCENDANTS_SELF, 100 * (node->self)/(double)size,
|
||||||
DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size,
|
DESCENDANTS_NON_RECURSE, 100 * (node->non_recursion)/(double)size,
|
||||||
|
#if 0
|
||||||
|
DESCENDANTS_SELF, (double)node->self,
|
||||||
|
DESCENDANTS_NON_RECURSE, (double)node->non_recursion,
|
||||||
|
#endif
|
||||||
DESCENDANTS_OBJECT, node->name,
|
DESCENDANTS_OBJECT, node->name,
|
||||||
-1);
|
-1);
|
||||||
|
|
||||||
@ -560,6 +568,10 @@ add_callers (GtkListStore *list_store,
|
|||||||
CALLERS_NAME, name,
|
CALLERS_NAME, name,
|
||||||
CALLERS_SELF, 100.0 * callers->self / profile_size,
|
CALLERS_SELF, 100.0 * callers->self / profile_size,
|
||||||
CALLERS_TOTAL, 100.0 * callers->total / profile_size,
|
CALLERS_TOTAL, 100.0 * callers->total / profile_size,
|
||||||
|
#if 0
|
||||||
|
CALLERS_SELF, (double)callers->self,
|
||||||
|
CALLERS_TOTAL, (double)callers->total,
|
||||||
|
#endif
|
||||||
CALLERS_OBJECT, callers->name,
|
CALLERS_OBJECT, callers->name,
|
||||||
-1);
|
-1);
|
||||||
|
|
||||||
@ -1146,7 +1158,8 @@ update_screenshot_window (Application *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
app->update_screenshot_id = g_idle_add (update_screenshot_window_idle, app);
|
app->update_screenshot_id = g_idle_add (
|
||||||
|
update_screenshot_window_idle, app);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -1676,6 +1689,10 @@ main (int argc,
|
|||||||
g_idle_add_full (G_PRIORITY_LOW, load_file, file_open_data, NULL);
|
g_idle_add_full (G_PRIORITY_LOW, load_file, file_open_data, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
g_idle_add (gtk_main_quit, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
gtk_main ();
|
gtk_main ();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user