diff options
author | Søren Sandmann <sandmann@redhat.com> | 2007-11-18 23:11:52 +0000 |
---|---|---|
committer | Søren Sandmann Pedersen <ssp@src.gnome.org> | 2007-11-18 23:11:52 +0000 |
commit | 05c4a202b384284e80c08487b64e600455b1d192 (patch) | |
tree | 97a33f4c14995e8aa98300dff539682d53729563 /module | |
parent | a68837a3ad67a381ca19a94e77f1945685087202 (diff) |
Crude heuristic stack scanning on x86-64.
Sun Nov 18 18:12:09 2007 Søren Sandmann <sandmann@redhat.com>
* module/sysprof-module.c (heuristic_trace): Crude heuristic stack
scanning on x86-64.
* module/sysprof-module.c (copy_kernel_stack): New function
svn path=/trunk/; revision=392
Diffstat (limited to 'module')
-rw-r--r-- | module/sysprof-module.c | 106 |
1 files changed, 67 insertions, 39 deletions
diff --git a/module/sysprof-module.c b/module/sysprof-module.c index f1473be..bc8ef8a 100644 --- a/module/sysprof-module.c +++ b/module/sysprof-module.c @@ -112,6 +112,40 @@ minimum (int a, int b) return a > b ? b : a; } +static struct pt_regs * +copy_kernel_stack (struct pt_regs *regs, + SysprofStackTrace *trace) +{ + int n_bytes; + 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 */ + return (struct pt_regs *)eos; +} + static void framepointer_trace (struct pt_regs *regs, SysprofStackTrace *trace) { @@ -119,10 +153,8 @@ framepointer_trace (struct pt_regs *regs, SysprofStackTrace *trace) int i; StackFrame frame; - i = 0; + i = 1; - trace->addresses[i++] = (void *)regs->REG_INS_PTR; - framepointer = (void *)regs->REG_FRAME_PTR; while (read_frame (framepointer, &frame) == 0 && @@ -140,7 +172,29 @@ static void heuristic_trace (struct pt_regs *regs, SysprofStackTrace *trace) { - return framepointer_trace (regs, trace); + unsigned long esp = regs->REG_STACK_PTR; + unsigned long eos = current->mm->start_stack; + + if (esp < eos - (current->mm->stack_vm << PAGE_SHIFT)) { + /* Stack pointer is not in stack map */ + + return; + } + + if (eos > esp) { + unsigned long i; + int j; + + j = 1; + for (i = esp; i < eos && j < SYSPROF_MAX_ADDRESSES; i += sizeof (void *)) { + void *x; + if (__copy_from_user_inatomic ( + &x, (char *)i, sizeof (x)) == 0) + trace->addresses[j++] = x; + } + + trace->n_addresses = j; + } } #ifdef OLD_PROFILE @@ -154,7 +208,6 @@ timer_notify (struct pt_regs *regs) struct pt_regs * regs = (struct pt_regs *)data; #endif SysprofStackTrace *trace = &(area->traces[area->head]); - int i; int is_user; static atomic_t in_timer_notify = ATOMIC_INIT(1); int n; @@ -171,7 +224,7 @@ timer_notify (struct pt_regs *regs) is_user = user_mode(regs); - if (!current || current->pid == 0 || !current->mm) + if (!current || current->pid == 0) goto out; if (is_user && current->state != TASK_RUNNING) @@ -184,45 +237,20 @@ timer_notify (struct pt_regs *regs) trace->n_kernel_words = 0; trace->n_addresses = 0; - i = 0; if (!is_user) - { - int n_bytes; - char *esp; - char *eos; + regs = copy_kernel_stack (regs, trace); - 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; - } + trace->addresses[0] = (void *)regs->REG_INS_PTR; + trace->n_addresses = 1; + if (current->mm) { #ifdef CONFIG_X86_64 - heuristic_trace (regs, trace); + heuristic_trace (regs, trace); #elif CONFIG_X86 - framepointer_trace (regs, trace); + framepointer_trace (regs, trace); #endif - + } + if (trace->n_addresses == SYSPROF_MAX_ADDRESSES) trace->truncated = 1; else |