From 4260cf5624164e6532f1a6dc374c70d88d7b9665 Mon Sep 17 00:00:00 2001 From: Kris Van Hees Date: Thu, 13 Oct 2011 22:42:27 -0400 Subject: [PATCH] dtrace: migrate stacktrace dumping and move headers about: fix reloc overrun Stacktrace dumping has been moved to the GPL-licensed dtrace_os.c because it depends on a symbol that is exported as GPL-only. Functionality in dtrace_isa that requires stacktrace dumping can now use dtrace_stacktrace(). The GPL-licensed dtrace_os.h C header file is now made available through the /include/linux hierarchy, and it is included in dtrace.h. Fixed a bug in dtrace_relocs.c where section names where copied into a memory area that was 1 byte short, causing various unpleasant forms of behaviour. Signed-off-by: Kris Van Hees --- .../linux/dtrace_os.h | 23 +++- kernel/dtrace/dtrace_os.c | 115 +++++++++++++++++- scripts/dtrace_relocs.c | 6 +- 3 files changed, 133 insertions(+), 11 deletions(-) rename kernel/dtrace/systrace_os.h => include/linux/dtrace_os.h (68%) diff --git a/kernel/dtrace/systrace_os.h b/include/linux/dtrace_os.h similarity index 68% rename from kernel/dtrace/systrace_os.h rename to include/linux/dtrace_os.h index 914e3f057bcb..9b3268f60885 100644 --- a/kernel/dtrace/systrace_os.h +++ b/include/linux/dtrace_os.h @@ -1,9 +1,12 @@ /* Copyright (C) 2011 Oracle Corporation */ -#ifndef _SYSTRACE_OS_H_ -#define _SYSTRACE_OS_H_ +#ifndef _DTRACE_OS_H_ +#define _DTRACE_OS_H_ + +#include typedef uint32_t dtrace_id_t; + #define DTRACE_IDNONE 0 typedef void (*sys_call_ptr_t)(void); @@ -31,4 +34,18 @@ typedef struct systrace_info { extern systrace_info_t *dtrace_syscalls_init(void); -#endif /* _SYSTRACE_OS_H_ */ +#define STACKTRACE_KERNEL 0x01 +#define STACKTRACE_USER 0x02 +#define STACKTRACE_SKIP 0x10 + +typedef struct stacktrace_state { + uint64_t *pcs; + uint64_t *fps; + int limit; + int depth; + int flags; +} stacktrace_state_t; + +extern void dtrace_stacktrace(stacktrace_state_t *); + +#endif /* _DTRACE_OS_H_ */ diff --git a/kernel/dtrace/dtrace_os.c b/kernel/dtrace/dtrace_os.c index d062128e0940..56f9000e8a54 100644 --- a/kernel/dtrace/dtrace_os.c +++ b/kernel/dtrace/dtrace_os.c @@ -6,12 +6,11 @@ */ #include +#include #include #include #include -#include - -#include "systrace_os.h" +#include /* * Return a high resolution timer value that is guaranteed to always increase. @@ -183,8 +182,8 @@ static systrace_info_t systrace_info = { */ #undef __SYSCALL #define __SYSCALL(nr, sym) [nr] { __stringify(sym), }, -#undef _ASM_X86_UNISTD_64_H -#include +# undef _ASM_X86_UNISTD_64_H +#include } }; @@ -236,3 +235,109 @@ systrace_info_t *dtrace_syscalls_init() { return &systrace_info; } EXPORT_SYMBOL(dtrace_syscalls_init); + +static int dtrace_stacktrace_stack(void *data, char *name) +{ + stacktrace_state_t *st = (stacktrace_state_t *)data; + + /* + * We do not skip anything for non-user stack analysis. + */ + if (!(st->flags & STACKTRACE_USER)) + return 0; + + if (name != NULL && strlen(name) > 3) { + /* + * Sadly, the dump stack code calls us with both and EOI. + * Consistency would be much nicer. + */ + if ((name[0] == '<' && name[1] == 'E' && name[2] == 'O') || + (name[0] == 'E' && name[2] == 'O')) + st->flags &= ~STACKTRACE_SKIP; + } + + return 0; +} + +static void dtrace_stacktrace_address(void *data, unsigned long addr, + int reliable) +{ + stacktrace_state_t *st = (stacktrace_state_t *)data; + + if (st->flags & STACKTRACE_SKIP) + return; + + if (reliable == 2) { + if (st->fps) + st->fps[st->depth] = addr; + } else { + if (st->pcs != NULL) { + if (st->depth < st->limit) + st->pcs[st->depth++] = addr; + } else + st->depth++; + } +} + +static inline int valid_sp(struct thread_info *tinfo, void *p, + unsigned int size, void *end) +{ + void *t = tinfo; + + if (end) { + if (p < end && p >= (end - THREAD_SIZE)) + return 1; + else + return 0; + } + + return p > t && p < t + THREAD_SIZE - size; +} + +struct frame { + struct frame *fr_savfp; + unsigned long fr_savpc; +} __attribute__((packed)); + +static unsigned long dtrace_stacktrace_walk_stack( + struct thread_info *tinfo, + unsigned long *stack, + unsigned long bp, + const struct stacktrace_ops *ops, + void *data, unsigned long *end, + int *graph) +{ + struct frame *fr = (struct frame *)bp; + unsigned long *pcp = &(fr->fr_savpc); + + while (valid_sp(tinfo, pcp, sizeof(*pcp), end)) { + unsigned long addr = *pcp; + + fr = fr->fr_savfp; + ops->address(data, (unsigned long)fr, 2); + ops->address(data, addr, 1); + pcp = &(fr->fr_savpc); + } + + return (unsigned long)fr; +} + +static const struct stacktrace_ops dtrace_stacktrace_ops = { + .stack = dtrace_stacktrace_stack, + .address = dtrace_stacktrace_address, + .walk_stack = print_context_stack +}; + +static const struct stacktrace_ops dtrace_fpstacktrace_ops = { + .stack = dtrace_stacktrace_stack, + .address = dtrace_stacktrace_address, + .walk_stack = dtrace_stacktrace_walk_stack +}; + +void dtrace_stacktrace(stacktrace_state_t *st) +{ + dump_trace(NULL, NULL, NULL, 0, + st->fps != NULL ? &dtrace_fpstacktrace_ops + : &dtrace_stacktrace_ops, st); +} +EXPORT_SYMBOL(dtrace_stacktrace); diff --git a/scripts/dtrace_relocs.c b/scripts/dtrace_relocs.c index 01501bd07c18..339dc2032380 100644 --- a/scripts/dtrace_relocs.c +++ b/scripts/dtrace_relocs.c @@ -154,7 +154,7 @@ static int get_section_info(FILE *fin, char buf[500], struct sym_entry *sect) sect->section_used = false; sect->section_index = sect_index; sect->len = sect_size; - sect->sym = malloc(strlen(sect_name)); + sect->sym = malloc(strlen(sect_name) + 1); if (!sect->sym) { fprintf(stderr, "relocs failure: " "unable to allocate required amount of memory\n"); @@ -163,8 +163,8 @@ static int get_section_info(FILE *fin, char buf[500], struct sym_entry *sect) strcpy((char *)sect->sym, sect_name); #ifdef INFO - fprintf(stderr, "sect: index=%d, name=%s, addr/offset=0x%llx, sect_size=0x%x, align=%s, vma=0x%llx, lma=0x%llx, flags=%s\n", - sect_index, sect->sym, sect->addr, sect->len, sect_align, + fprintf(stderr, "sect: index=%d, name=%s (%d), addr/offset=0x%llx, sect_size=0x%x, align=%s, vma=0x%llx, lma=0x%llx, flags=%s\n", + sect_index, sect->sym, strlen(sect->sym), sect->addr, sect->len, sect_align, vma, lma, flags); #endif -- 2.50.1