diff options
author | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-06-07 14:30:21 +0100 |
---|---|---|
committer | kfraser@localhost.localdomain <kfraser@localhost.localdomain> | 2007-06-07 14:30:21 +0100 |
commit | 94c85019981270a21e6e3aeba694f3b7b444b566 (patch) | |
tree | c42a47aae990fa457bbd10e8372a4cb9744d094e | |
parent | 30f01d9011561268556d47273a8599ebd9b3173a (diff) | |
download | xen-94c85019981270a21e6e3aeba694f3b7b444b566.tar.gz xen-94c85019981270a21e6e3aeba694f3b7b444b566.tar.bz2 xen-94c85019981270a21e6e3aeba694f3b7b444b566.zip |
Add backtrace support to xenoprof.
Signed-off-by: Amitabha Roy <amitabha.roy@gmail.com>
Reviewed-by: Jose Renato G Santos <joserenato.santos@hp.com>
-rw-r--r-- | xen/Rules.mk | 32 | ||||
-rw-r--r-- | xen/arch/ia64/xen/oprofile/perfmon.c | 7 | ||||
-rw-r--r-- | xen/arch/x86/oprofile/Makefile | 1 | ||||
-rw-r--r-- | xen/arch/x86/oprofile/backtrace.c | 132 | ||||
-rw-r--r-- | xen/arch/x86/oprofile/op_model_athlon.c | 6 | ||||
-rw-r--r-- | xen/arch/x86/oprofile/op_model_p4.c | 8 | ||||
-rw-r--r-- | xen/arch/x86/oprofile/op_model_ppro.c | 6 | ||||
-rw-r--r-- | xen/arch/x86/traps.c | 2 | ||||
-rw-r--r-- | xen/common/xenoprof.c | 135 | ||||
-rw-r--r-- | xen/include/asm-ia64/xenoprof.h | 12 | ||||
-rw-r--r-- | xen/include/asm-x86/xenoprof.h | 11 | ||||
-rw-r--r-- | xen/include/public/xenoprof.h | 8 | ||||
-rw-r--r-- | xen/include/xen/xenoprof.h | 2 |
13 files changed, 300 insertions, 62 deletions
diff --git a/xen/Rules.mk b/xen/Rules.mk index ac42711a11..55ae9746e0 100644 --- a/xen/Rules.mk +++ b/xen/Rules.mk @@ -3,10 +3,11 @@ # If you change any of these configuration options then you must # 'make clean' before rebuilding. # -verbose ?= n -perfc ?= n -perfc_arrays?= n -crash_debug ?= n +verbose ?= n +perfc ?= n +perfc_arrays ?= n +crash_debug ?= n +frame_pointer ?= n XEN_ROOT=$(BASEDIR)/.. include $(XEN_ROOT)/Config.mk @@ -14,11 +15,15 @@ include $(XEN_ROOT)/Config.mk # Hardcoded configuration implications and dependencies. # Do this is a neater way if it becomes unwieldy. ifeq ($(debug),y) -verbose := y +verbose := y +frame_pointer := y endif ifeq ($(perfc_arrays),y) perfc := y endif +ifeq ($(frame_pointer),y) +CFLAGS := $(shell echo $(CFLAGS) | sed -e 's/-f[^ ]*omit-frame-pointer//g') +endif # Set ARCH/SUBARCH appropriately. override COMPILE_SUBARCH := $(XEN_COMPILE_ARCH) @@ -50,18 +55,19 @@ ALL_OBJS-y += $(BASEDIR)/drivers/built_in.o ALL_OBJS-$(ACM_SECURITY) += $(BASEDIR)/acm/built_in.o ALL_OBJS-y += $(BASEDIR)/arch/$(TARGET_ARCH)/built_in.o -CFLAGS-y += -g -D__XEN__ -CFLAGS-$(ACM_SECURITY) += -DACM_SECURITY -CFLAGS-$(verbose) += -DVERBOSE -CFLAGS-$(crash_debug) += -DCRASH_DEBUG -CFLAGS-$(perfc) += -DPERF_COUNTERS -CFLAGS-$(perfc_arrays) += -DPERF_ARRAYS +CFLAGS-y += -g -D__XEN__ +CFLAGS-$(ACM_SECURITY) += -DACM_SECURITY +CFLAGS-$(verbose) += -DVERBOSE +CFLAGS-$(crash_debug) += -DCRASH_DEBUG +CFLAGS-$(perfc) += -DPERF_COUNTERS +CFLAGS-$(perfc_arrays) += -DPERF_ARRAYS +CFLAGS-$(frame_pointer) += -fno-omit-frame-pointer -DCONFIG_FRAME_POINTER ifneq ($(max_phys_cpus),) -CFLAGS-y += -DMAX_PHYS_CPUS=$(max_phys_cpus) +CFLAGS-y += -DMAX_PHYS_CPUS=$(max_phys_cpus) endif -AFLAGS-y += -D__ASSEMBLY__ +AFLAGS-y += -D__ASSEMBLY__ ALL_OBJS := $(ALL_OBJS-y) diff --git a/xen/arch/ia64/xen/oprofile/perfmon.c b/xen/arch/ia64/xen/oprofile/perfmon.c index 0488e1298b..4fce815fb0 100644 --- a/xen/arch/ia64/xen/oprofile/perfmon.c +++ b/xen/arch/ia64/xen/oprofile/perfmon.c @@ -37,7 +37,7 @@ #include <asm/ptrace.h> // XXX move them to an appropriate header file -extern void xenoprof_log_event(struct vcpu *vcpu, +extern void xenoprof_log_event(struct vcpu *vcpu, struct pt_regs * regs, unsigned long eip, int mode, int event); extern int is_active(struct domain *d); @@ -55,7 +55,10 @@ xenoprof_handler(struct task_struct *task, void *buf, pfm_ovfl_arg_t *arg, if (!allow_virq || !allow_ints) return 0; - xenoprof_log_event(current, ip, xenoprofile_get_mode(task, regs), event); + // Note that log event actually expect cpu_user_regs, cast back + // appropriately when doing the backtrace implementation in ia64 + xenoprof_log_event(current, regs, ip, xenoprofile_get_mode(task, regs), + event); // send VIRQ_XENOPROF if (is_active(current->domain) && !ring_0(regs)) diff --git a/xen/arch/x86/oprofile/Makefile b/xen/arch/x86/oprofile/Makefile index 1956c02df5..956e3d1b5d 100644 --- a/xen/arch/x86/oprofile/Makefile +++ b/xen/arch/x86/oprofile/Makefile @@ -3,3 +3,4 @@ obj-y += nmi_int.o obj-y += op_model_p4.o obj-y += op_model_ppro.o obj-y += op_model_athlon.o +obj-y += backtrace.o diff --git a/xen/arch/x86/oprofile/backtrace.c b/xen/arch/x86/oprofile/backtrace.c new file mode 100644 index 0000000000..ad0b6eb0aa --- /dev/null +++ b/xen/arch/x86/oprofile/backtrace.c @@ -0,0 +1,132 @@ +/** + * @file backtrace.c + * + * @remark Copyright 2002 OProfile authors + * @remark Read the file COPYING + * + * @author John Levon + * @author David Smith + * Modified for Xen by Amitabha Roy + * + */ + +#include<xen/types.h> +#include<asm/page.h> +#include<xen/xenoprof.h> +#include<asm/guest_access.h> + +struct frame_head { + struct frame_head * ebp; + unsigned long ret; +} __attribute__((packed)); + +static struct frame_head * +dump_hypervisor_backtrace(struct domain *d, struct vcpu *vcpu, + struct frame_head * head, int mode) +{ + if (!xenoprof_add_trace(d, vcpu, head->ret, mode)) + return 0; + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (head >= head->ebp) + return NULL; + + return head->ebp; +} + +static struct frame_head * +dump_guest_backtrace(struct domain *d, struct vcpu *vcpu, + struct frame_head * head, int mode) +{ + struct frame_head bufhead[2]; + XEN_GUEST_HANDLE(char) guest_head = guest_handle_from_ptr(head, char); + + /* Also check accessibility of one struct frame_head beyond */ + if (!guest_handle_okay(guest_head, sizeof(bufhead))) + return 0; + if (__copy_from_guest_offset((char *)bufhead, guest_head, 0, + sizeof(bufhead))) + return 0; + + if (!xenoprof_add_trace(d, vcpu, bufhead[0].ret, mode)) + return 0; + + /* frame pointers should strictly progress back up the stack + * (towards higher addresses) */ + if (head >= bufhead[0].ebp) + return NULL; + + return bufhead[0].ebp; +} + +/* + * | | /\ Higher addresses + * | | + * --------------- stack base (address of current_thread_info) + * | thread info | + * . . + * | stack | + * --------------- saved regs->ebp value if valid (frame_head address) + * . . + * --------------- saved regs->rsp value if x86_64 + * | | + * --------------- struct pt_regs * stored on stack if 32-bit + * | | + * . . + * | | + * --------------- %esp + * | | + * | | \/ Lower addresses + * + * Thus, regs (or regs->rsp for x86_64) <-> stack base restricts the + * valid(ish) ebp values. Note: (1) for x86_64, NMI and several other + * exceptions use special stacks, maintained by the interrupt stack table + * (IST). These stacks are set up in trap_init() in + * arch/x86_64/kernel/traps.c. Thus, for x86_64, regs now does not point + * to the kernel stack; instead, it points to some location on the NMI + * stack. On the other hand, regs->rsp is the stack pointer saved when the + * NMI occurred. (2) For 32-bit, regs->esp is not valid because the + * processor does not save %esp on the kernel stack when interrupts occur + * in the kernel mode. + */ +#if defined(CONFIG_FRAME_POINTER) +static int valid_hypervisor_stack(struct frame_head * head, + struct cpu_user_regs * regs) +{ + unsigned long headaddr = (unsigned long)head; +#ifdef CONFIG_X86_64 + unsigned long stack = (unsigned long)regs->rsp; +#else + unsigned long stack = (unsigned long)regs; +#endif + unsigned long stack_base = (stack & ~(STACK_SIZE - 1)) + STACK_SIZE; + + return headaddr > stack && headaddr < stack_base; +} +#else +/* without fp, it's just junk */ +static int valid_hypervisor_stack(struct frame_head * head, + struct cpu_user_regs * regs) +{ + return 0; +} +#endif + +void xenoprof_backtrace(struct domain *d, struct vcpu *vcpu, + struct cpu_user_regs * const regs, + unsigned long depth, int mode) +{ + struct frame_head *head; + + head = (struct frame_head *)regs->ebp; + + if (mode > 1) { + while (depth-- && valid_hypervisor_stack(head, regs)) + head = dump_hypervisor_backtrace(d, vcpu, head, mode); + return; + } + + while (depth-- && head) + head = dump_guest_backtrace(d, vcpu, head, mode); +} diff --git a/xen/arch/x86/oprofile/op_model_athlon.c b/xen/arch/x86/oprofile/op_model_athlon.c index ec99be48b0..adcc9209f7 100644 --- a/xen/arch/x86/oprofile/op_model_athlon.c +++ b/xen/arch/x86/oprofile/op_model_athlon.c @@ -43,8 +43,8 @@ static unsigned long reset_value[NUM_COUNTERS]; -extern void xenoprof_log_event(struct vcpu *v, unsigned long eip, - int mode, int event); +extern void xenoprof_log_event(struct vcpu *v, struct cpu_user_regs * regs, + unsigned long eip, int mode, int event); extern int xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs); @@ -130,7 +130,7 @@ static int athlon_check_ctrs(unsigned int const cpu, for (i = 0 ; i < NUM_COUNTERS; ++i) { CTR_READ(low, high, msrs, i); if (CTR_OVERFLOWED(low)) { - xenoprof_log_event(current, eip, mode, i); + xenoprof_log_event(current, regs, eip, mode, i); CTR_WRITE(reset_value[i], msrs, i); ovf = 1; } diff --git a/xen/arch/x86/oprofile/op_model_p4.c b/xen/arch/x86/oprofile/op_model_p4.c index c285ea1d41..8fcb7ce0bc 100644 --- a/xen/arch/x86/oprofile/op_model_p4.c +++ b/xen/arch/x86/oprofile/op_model_p4.c @@ -620,8 +620,8 @@ static void p4_setup_ctrs(struct op_msrs const * const msrs) } } -extern void xenoprof_log_event(struct vcpu *v, unsigned long eip, - int mode, int event); +extern void xenoprof_log_event(struct vcpu *v, struct cpu_user_regs * regs, + unsigned long eip, int mode, int event); extern int xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs); @@ -664,8 +664,8 @@ static int p4_check_ctrs(unsigned int const cpu, CCCR_READ(low, high, real); CTR_READ(ctr, high, real); if (CCCR_OVF_P(low) || CTR_OVERFLOW_P(ctr)) { - xenoprof_log_event(current, eip, mode, i); - CTR_WRITE(reset_value[i], real); + xenoprof_log_event(current, regs, eip, mode, i); + CTR_WRITE(reset_value[i], real); CCCR_CLEAR_OVF(low); CCCR_WRITE(low, high, real); CTR_WRITE(reset_value[i], real); diff --git a/xen/arch/x86/oprofile/op_model_ppro.c b/xen/arch/x86/oprofile/op_model_ppro.c index 139f4571bf..6c4344ee21 100644 --- a/xen/arch/x86/oprofile/op_model_ppro.c +++ b/xen/arch/x86/oprofile/op_model_ppro.c @@ -88,8 +88,8 @@ static void ppro_setup_ctrs(struct op_msrs const * const msrs) } } -extern void xenoprof_log_event(struct vcpu *v, unsigned long eip, - int mode, int event); +extern void xenoprof_log_event(struct vcpu *v, struct cpu_user_regs * regs, + unsigned long eip, int mode, int event); extern int xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs); @@ -106,7 +106,7 @@ static int ppro_check_ctrs(unsigned int const cpu, for (i = 0 ; i < NUM_COUNTERS; ++i) { CTR_READ(low, high, msrs, i); if (CTR_OVERFLOWED(low)) { - xenoprof_log_event(current, eip, mode, i); + xenoprof_log_event(current, regs, eip, mode, i); CTR_WRITE(reset_value[i], msrs, i); ovf = 1; } diff --git a/xen/arch/x86/traps.c b/xen/arch/x86/traps.c index fb454043eb..ec9738e9e3 100644 --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -167,7 +167,7 @@ static void show_guest_stack(struct cpu_user_regs *regs) printk("\n"); } -#ifdef NDEBUG +#if !defined(CONFIG_FRAME_POINTER) static void show_trace(struct cpu_user_regs *regs) { diff --git a/xen/common/xenoprof.c b/xen/common/xenoprof.c index 7e2a04bb5c..13dbdd8a04 100644 --- a/xen/common/xenoprof.c +++ b/xen/common/xenoprof.c @@ -31,6 +31,7 @@ unsigned int pdomains; unsigned int activated; struct domain *xenoprof_primary_profiler; int xenoprof_state = XENOPROF_IDLE; +static unsigned long backtrace_depth; u64 total_samples; u64 invalid_buffer_samples; @@ -205,7 +206,8 @@ static int alloc_xenoprof_struct( i = 0; for_each_vcpu ( d, v ) { - xenoprof_buf_t *buf = (xenoprof_buf_t *)&d->xenoprof->rawbuf[i * bufsize]; + xenoprof_buf_t *buf = (xenoprof_buf_t *) + &d->xenoprof->rawbuf[i * bufsize]; d->xenoprof->vcpu[v->vcpu_id].event_size = max_samples; d->xenoprof->vcpu[v->vcpu_id].buffer = buf; @@ -414,21 +416,82 @@ static int add_passive_list(XEN_GUEST_HANDLE(void) arg) return ret; } -void xenoprof_log_event( - struct vcpu *vcpu, unsigned long eip, int mode, int event) + +/* Get space in the buffer */ +static int xenoprof_buf_space(struct domain *d, xenoprof_buf_t * buf, int size) +{ + int head, tail; + + head = xenoprof_buf(d, buf, event_head); + tail = xenoprof_buf(d, buf, event_tail); + + return ((tail > head) ? 0 : size) + tail - head - 1; +} + +/* Check for space and add a sample. Return 1 if successful, 0 otherwise. */ +static int xenoprof_add_sample(struct domain *d, xenoprof_buf_t *buf, + unsigned long eip, int mode, int event) +{ + int head, tail, size; + + head = xenoprof_buf(d, buf, event_head); + tail = xenoprof_buf(d, buf, event_tail); + size = xenoprof_buf(d, buf, event_size); + + /* make sure indexes in shared buffer are sane */ + if ( (head < 0) || (head >= size) || (tail < 0) || (tail >= size) ) + { + corrupted_buffer_samples++; + return 0; + } + + if ( xenoprof_buf_space(d, buf, size) > 0 ) + { + xenoprof_buf(d, buf, event_log[head].eip) = eip; + xenoprof_buf(d, buf, event_log[head].mode) = mode; + xenoprof_buf(d, buf, event_log[head].event) = event; + head++; + if ( head >= size ) + head = 0; + + xenoprof_buf(d, buf, event_head) = head; + } + else + { + xenoprof_buf(d, buf, lost_samples)++; + lost_samples++; + return 0; + } + + return 1; +} + +int xenoprof_add_trace(struct domain *d, struct vcpu *vcpu, + unsigned long eip, int mode) +{ + xenoprof_buf_t *buf = d->xenoprof->vcpu[vcpu->vcpu_id].buffer; + + /* Do not accidentally write an escape code due to a broken frame. */ + if ( eip == XENOPROF_ESCAPE_CODE ) + { + invalid_buffer_samples++; + return 0; + } + + return xenoprof_add_sample(d, buf, eip, mode, 0); +} + +void xenoprof_log_event(struct vcpu *vcpu, + struct cpu_user_regs * regs, unsigned long eip, + int mode, int event) { struct domain *d = vcpu->domain; struct xenoprof_vcpu *v; xenoprof_buf_t *buf; - int head; - int tail; - int size; - total_samples++; - /* ignore samples of un-monitored domains */ - /* Count samples in idle separate from other unmonitored domains */ + /* Ignore samples of un-monitored domains. */ if ( !is_profiled(d) ) { others_samples++; @@ -436,41 +499,29 @@ void xenoprof_log_event( } v = &d->xenoprof->vcpu[vcpu->vcpu_id]; - - /* Sanity check. Should never happen */ if ( v->buffer == NULL ) { invalid_buffer_samples++; return; } - + buf = v->buffer; - head = xenoprof_buf(d, buf, event_head); - tail = xenoprof_buf(d, buf, event_tail); - size = v->event_size; - - /* make sure indexes in shared buffer are sane */ - if ( (head < 0) || (head >= size) || (tail < 0) || (tail >= size) ) + /* Provide backtrace if requested. */ + if ( backtrace_depth > 0 ) { - corrupted_buffer_samples++; - return; + if ( (xenoprof_buf_space(d, buf, v->event_size) < 2) || + !xenoprof_add_sample(d, buf, XENOPROF_ESCAPE_CODE, mode, + XENOPROF_TRACE_BEGIN) ) + { + xenoprof_buf(d, buf, lost_samples)++; + lost_samples++; + return; + } } - if ( (head == tail - 1) || (head == size - 1 && tail == 0) ) + if ( xenoprof_add_sample(d, buf, eip, mode, event) ) { - xenoprof_buf(d, buf, lost_samples)++; - lost_samples++; - } - else - { - xenoprof_buf(d, buf, event_log[head].eip) = eip; - xenoprof_buf(d, buf, event_log[head].mode) = mode; - xenoprof_buf(d, buf, event_log[head].event) = event; - head++; - if ( head >= size ) - head = 0; - xenoprof_buf(d, buf, event_head) = head; if ( is_active(vcpu->domain) ) active_samples++; else @@ -481,9 +532,15 @@ void xenoprof_log_event( xenoprof_buf(d, buf, kernel_samples)++; else xenoprof_buf(d, buf, xen_samples)++; + } + + if ( backtrace_depth > 0 ) + xenoprof_backtrace(d, vcpu, regs, backtrace_depth, mode); } + + static int xenoprof_op_init(XEN_GUEST_HANDLE(void) arg) { struct xenoprof_init xenoprof_init; @@ -685,7 +742,8 @@ int do_xenoprof_op(int op, XEN_GUEST_HANDLE(void) arg) break; case XENOPROF_stop: - if ( xenoprof_state != XENOPROF_PROFILING ) { + if ( xenoprof_state != XENOPROF_PROFILING ) + { ret = -EPERM; break; } @@ -729,9 +787,18 @@ int do_xenoprof_op(int op, XEN_GUEST_HANDLE(void) arg) activated = 0; adomains=0; xenoprof_primary_profiler = NULL; + backtrace_depth=0; ret = 0; } break; + + case XENOPROF_set_backtrace: + ret = 0; + if ( !xenoprof_backtrace_supported() ) + ret = -EINVAL; + else if ( copy_from_guest(&backtrace_depth, arg, 1) ) + ret = -EFAULT; + break; default: ret = -ENOSYS; diff --git a/xen/include/asm-ia64/xenoprof.h b/xen/include/asm-ia64/xenoprof.h index b8b1e602d9..159716cf05 100644 --- a/xen/include/asm-ia64/xenoprof.h +++ b/xen/include/asm-ia64/xenoprof.h @@ -37,7 +37,17 @@ void xenoprof_arch_release_counters(void); struct vcpu; struct cpu_user_regs; int xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs); - +static inline int xenoprof_backtrace_supported(void) +{ + return 0; +} +static inline void xenoprof_backtrace( + struct domain *d, struct vcpu *vcpu, + struct pt_regs *const regs, unsigned long depth, int mode) +{ + /* To be implemented */ + return; +} #define xenoprof_shared_gmfn(d, gmaddr, maddr) \ assign_domain_page((d), (gmaddr), (maddr)); diff --git a/xen/include/asm-x86/xenoprof.h b/xen/include/asm-x86/xenoprof.h index 8554f7c383..2ce369dd81 100644 --- a/xen/include/asm-x86/xenoprof.h +++ b/xen/include/asm-x86/xenoprof.h @@ -46,7 +46,18 @@ int xenoprof_arch_counter(XEN_GUEST_HANDLE(void) arg); struct vcpu; struct cpu_user_regs; + int xenoprofile_get_mode(struct vcpu *v, struct cpu_user_regs * const regs); + +static inline int xenoprof_backtrace_supported(void) +{ + return 1; +} + +void xenoprof_backtrace( + struct domain *d, struct vcpu *vcpu, + struct cpu_user_regs *const regs, unsigned long depth, int mode); + #define xenoprof_shared_gmfn(d, gmaddr, maddr) \ do { \ (void)(maddr); \ diff --git a/xen/include/public/xenoprof.h b/xen/include/public/xenoprof.h index d16d8ca3f6..183078d8db 100644 --- a/xen/include/public/xenoprof.h +++ b/xen/include/public/xenoprof.h @@ -49,7 +49,8 @@ #define XENOPROF_release_counters 12 #define XENOPROF_shutdown 13 #define XENOPROF_get_buffer 14 -#define XENOPROF_last_op 14 +#define XENOPROF_set_backtrace 15 +#define XENOPROF_last_op 15 #define MAX_OPROF_EVENTS 32 #define MAX_OPROF_DOMAINS 25 @@ -62,6 +63,11 @@ struct event_log { uint8_t event; }; +/* PC value that indicates a special code */ +#define XENOPROF_ESCAPE_CODE ~0UL +/* Transient events for the xenoprof->oprofile cpu buf */ +#define XENOPROF_TRACE_BEGIN 1 + /* Xenoprof buffer shared between Xen and domain - 1 per VCPU */ struct xenoprof_buf { uint32_t event_head; diff --git a/xen/include/xen/xenoprof.h b/xen/include/xen/xenoprof.h index 8983f0c987..83e9ec094e 100644 --- a/xen/include/xen/xenoprof.h +++ b/xen/include/xen/xenoprof.h @@ -66,6 +66,8 @@ struct domain; void free_xenoprof_pages(struct domain *d); int do_xenoprof_op(int op, XEN_GUEST_HANDLE(void) arg); +int xenoprof_add_trace(struct domain *d, struct vcpu *v, + unsigned long eip, int mode); extern struct domain *xenoprof_primary_profiler; |