Module: xenomai-gch Branch: 2.6-fpu_trace Commit: b684dda6bb5d70e51b6561e031f94a0221ce9102 URL: http://git.xenomai.org/?p=xenomai-gch.git;a=commit;h=b684dda6bb5d70e51b6561e031f94a0221ce9102
Author: Gilles Chanteperdrix <gilles.chanteperd...@xenomai.org> Date: Tue May 15 00:48:41 2012 +0200 fpu tracer --- include/asm-arm/bits/pod.h | 16 +- include/asm-arm/fp_dump.h | 89 +++++++ include/asm-generic/fp_dump.h | 152 ++++++++++++ include/asm-x86/bits/pod.h | 63 +++++- include/asm-x86/fp_dump.h | 96 ++++++++ include/nucleus/fpu_trace.h | 47 ++++ ksrc/nucleus/Kconfig | 7 + ksrc/nucleus/Makefile | 2 + ksrc/nucleus/fpu_trace.c | 542 +++++++++++++++++++++++++++++++++++++++++ ksrc/nucleus/pod.c | 3 + ksrc/nucleus/shadow.c | 3 + 11 files changed, 1011 insertions(+), 9 deletions(-) diff --git a/include/asm-arm/bits/pod.h b/include/asm-arm/bits/pod.h index 53a692e..c512b42 100644 --- a/include/asm-arm/bits/pod.h +++ b/include/asm-arm/bits/pod.h @@ -141,6 +141,7 @@ static inline void xnarch_enable_fpu(xnarchtcb_t *tcb) */ if (likely(!tcb->is_root)) { rthal_enable_fpu(); + fp_trace_xeno_enable(0, NULL); /* No exception should be pending, since it should have caused a trap earlier. */ @@ -149,8 +150,10 @@ static inline void xnarch_enable_fpu(xnarchtcb_t *tcb) unsigned cpu; #ifndef CONFIG_SMP if (likely(!(fpexc & RTHAL_VFP_ANY_EXC) - && !(rthal_vfp_fmrx(FPSCR) & FPSCR_IXE))) + && !(rthal_vfp_fmrx(FPSCR) & FPSCR_IXE))) { + fp_trace_xeno_enable(1, NULL); return; + } /* If current process has pending exceptions it is illegal to restore the FPEXC register with them, we must @@ -166,11 +169,13 @@ static inline void xnarch_enable_fpu(xnarchtcb_t *tcb) systems for still unknown reasons. */ rthal_save_fpu(tcb->fpup, fpexc); + fp_trace_xeno_enable(2, &tcb->fpup->vfpstate); cpu = rthal_processor_id(); vfp_current_hw_state[cpu] = NULL; rthal_disable_fpu(); - } + } else + fp_trace_xeno_enable(3, NULL); #else /* !CONFIG_VFP */ if (!tcb->user_task) rthal_enable_fpu(); @@ -196,8 +201,11 @@ static inline void xnarch_save_fpu(xnarchtcb_t * tcb) { #ifdef CONFIG_XENO_HW_FPU #ifdef CONFIG_VFP - if (tcb->fpup) + if (tcb->fpup) { rthal_save_fpu(tcb->fpup, rthal_enable_fpu()); + fp_trace_xeno_save(0, &tcb->fpup->vfpstate); + } else + fp_trace_xeno_save(1, NULL); #else /* !CONFIG_VFP */ if (tcb->fpup) { rthal_save_fpu(tcb->fpup); @@ -218,6 +226,7 @@ static inline void xnarch_restore_fpu(xnarchtcb_t * tcb) if (likely(!tcb->is_root)) { rthal_enable_fpu(); rthal_restore_fpu(tcb->fpup); + fp_trace_xeno_restore(0, &tcb->fpup->vfpstate); } else { /* We are restoring the Linux current thread which does not own the FPU context, so the FPU must be disabled, so that a fault will occur if @@ -232,6 +241,7 @@ static inline void xnarch_restore_fpu(xnarchtcb_t * tcb) unsigned cpu = rthal_processor_id(); vfp_current_hw_state[cpu] = NULL; rthal_disable_fpu(); + fp_trace_xeno_restore(1, NULL); } #else /* !CONFIG_VFP */ if (tcb->fpup) { diff --git a/include/asm-arm/fp_dump.h b/include/asm-arm/fp_dump.h new file mode 100644 index 0000000..53c59e4 --- /dev/null +++ b/include/asm-arm/fp_dump.h @@ -0,0 +1,89 @@ +#ifndef FP_DUMP_ARM_H +#define FP_DUMP_ARM_H + +#include <asm/fpstate.h> +#include <asm/xenomai/hal.h> +#include <asm-generic/xenomai/fp_dump.h> + +#define DECLARE_FPENV(var) \ + fpenv_t var ## _buf; \ + fpenv_t *var = &var ## _buf + +typedef struct fpreg { + unsigned long fracl; + unsigned long frach: 20; + unsigned short exp: 11; /* signed, but not using 2's complement. */ + unsigned short sign: 1; +} fpreg_t; + +typedef struct vfp_hard_struct fpenv_t; + +#define fp_enabled() (rthal_vfp_fmrx(FPEXC) & FPEXC_EN) + +static inline void fpenv_save(fpenv_t *fpuenv) +{ + rthal_vfp_save((union vfp_state *)fpuenv, rthal_vfp_fmrx(FPEXC)); +} + +static inline void fpenv_dump(void *cookie, fpenv_t *fpenv) +{ + unsigned i; + + fp_print(cookie, "FPU state:\n"); + fp_print(cookie, "fpexc: 0x%08x\n", fpenv->fpexc); + for(i = 0; i < ARRAY_SIZE(fpenv->fpregs); i++) { + fpreg_t *reg = (fpreg_t *)&fpenv->fpregs[i]; + fp_conv_t fp = { + .significand = ((1ULL << 63) + + (((unsigned long long)reg->frach) << 43) + + (((unsigned long long)reg->fracl) << 11)), + .exp = (int)(reg->exp - 1023), + .pow10 = 0, + }; + + fp_print(cookie, "D%2u: %Lu, ", i, fpenv->fpregs[i]); + + switch(reg->exp) { + case 0: + /* zero or denormalized. */ + if(!reg->frach && !reg->fracl) { + fp_print(cookie, "0"); + break; + } + + fp_print(cookie, "(DEN) "); + fp.significand <<= 1; + fp.exp--; + /* Fall through normalized. */ + + default: + /* normalized. */ + fp_disp(cookie, &fp, reg->sign); + break; + + case 2047: + /* infinities or nans. */ + if(!reg->frach && !reg->fracl) + fp_print(cookie, "%c inf.", + reg->sign ? '-' : '+'); + else + fp_print(cookie, "%cnan.", + (reg->frach & (1UL << 31) ? 'Q' : 'S' )); + break; + } + fp_print(cookie, " %c", i % 2 ? '\n' : ' '); + } +} + +static inline int fpenv_cmp(fpenv_t *env1p, fpenv_t *env2p) +{ + unsigned i; + + /* Only compare the values of the 32 registers; do not take status/control + registers value into account. */ + for(i = 0; i < ARRAY_SIZE(env1p->fpregs); i++) + if(memcmp(&env1p->fpregs[i], &env2p->fpregs[i], sizeof(fpreg_t))) + return 1; + return 0; +} +#endif /* FP_DUMP_ARM_H */ diff --git a/include/asm-generic/fp_dump.h b/include/asm-generic/fp_dump.h new file mode 100644 index 0000000..6826b47 --- /dev/null +++ b/include/asm-generic/fp_dump.h @@ -0,0 +1,152 @@ +#ifndef FP_DUMP_INNER_H +#define FP_DUMP_INNER_H + +#include <linux/kernel.h> /* For printk */ +#include <linux/bitops.h> /* For fls */ +#include <asm/div64.h> /* For do_div */ + +#ifndef fp_print +#define fp_print(cookie, fmt, ...) printk(fmt , ##args) +#endif /* fp_print */ + +/* Float in course of conversion from base 2 to base 10. + f == significand * 2 ^ (exp - 63) * 10 ^ pow10. */ +typedef struct fp_conv { + unsigned long long significand; + int pow10; + int exp; +} fp_conv_t; + +static inline unsigned __attribute__((const)) fp_sq(unsigned x) +{ + return x * x; +} + +static inline unsigned __attribute__((const)) fp_pow3(unsigned x) +{ + return x * fp_sq(x); +} + +static inline unsigned __attribute__((const)) fp_pow6(unsigned x) +{ + return fp_sq(fp_pow3(x)); +} + +static inline unsigned __attribute__((const)) fp_pow9(unsigned x) +{ + return fp_pow3(fp_pow3(x)); +} + +#define fp_flnzul(word) (fls(word) - 1) + +static inline unsigned long fp_flnzull(unsigned long long ull) +{ + unsigned long h = (ull >> 32); + if(h) + return 32 + fp_flnzul(h); + return fp_flnzul((unsigned long) ull); +} + +static inline unsigned long upow(unsigned long x, unsigned long n) +{ + switch(n) { + case 1: return x; + case 2: return fp_sq(x); + case 3: return fp_pow3(x); + case 6: return fp_pow6(x); + case 9: return fp_pow9(x); + } + + return -1; +} + +/* Divide significand by a power of two and multiply by a power of 10. */ +static inline void shiftmul(fp_conv_t *fp, unsigned shift, unsigned pow10) +{ + /* Avoid overflow. */ + if((fp->significand & (1ULL << 63))) { + fp->significand >>= 1; + fp->exp++; + } + + fp->significand >>= shift - pow10 -1; + fp->significand *= upow(5, pow10); + fp->exp += shift - 1; + fp->pow10 -= pow10; +} + +/* Divide significand by a power of ten and multiply by a power of 2. */ +static inline void divshift(fp_conv_t *fp, unsigned pow10, unsigned shift) +{ + do_div(fp->significand, upow(5, pow10)); + fp->significand <<= shift - pow10 -1; + fp->exp -= shift - 1; + fp->pow10 += pow10; + + /* Maintain accuracy. */ + if(!(fp->significand & (1ULL << 63))) { + fp->significand <<= 1; + fp->exp--; + } +} + +static inline void fp_disp(void *cookie, fp_conv_t *fp, int neg) +{ + /* Normalize. */ + if(fp->significand && !(fp->significand & (1ULL << 63))) { + unsigned shift = 63 - fp_flnzull(fp->significand); + fp->significand <<= shift; + fp->exp -= shift; + } + + /* Convert power of 2 exponent to power of 10, + bring exponent between 0 and 63 for integers, + between 0 and 29 for non integers. */ + while(fp->exp <= 0) + shiftmul(fp, 30, 9); + while(fp->exp > 63 || (fp->pow10 && fp->exp >= 29)) + divshift(fp, 9, 30); + + /* For non-integer, bring integer part between 0 and 10. */ + if(fp->pow10) { + if(fp->exp >= 19) + divshift(fp, 6, 20); + if(fp->exp >= 9) + divshift(fp, 3, 10); + if(fp->exp >= 6) + divshift(fp, 2, 7); + if(fp->exp > 3) + divshift(fp, 1, 4); + } + + { + unsigned long long tmp, i; + unsigned long f, rem; + /* Integer part. */ + i = fp->significand >> (63 - fp->exp); + + /* Fractionary part, left-aligned. */ + tmp = (fp->significand << (fp->exp + 1)); + + /* Convert to base 10, to 9 places. */ + tmp >>= 21; + tmp *= fp_pow9(5); + + /* Round to 32 bits. */ + f = tmp >> 34; + rem = (tmp >> 2); + if(rem > (1U << 31) || + (rem == (1U << 31) && (f % 2)) /* Round to even. */) + ++f; + + + /* Display. */ + fp_print(cookie, "%c%llu", neg ? '-' : ' ', i); + if (f) + fp_print(cookie, ".%09lu", f); + if(fp->pow10) + fp_print(cookie, "E%+d", fp->pow10); + } +} + +#endif /* FP_DUMP_INNER_H */ diff --git a/include/asm-x86/bits/pod.h b/include/asm-x86/bits/pod.h index 90e36f0..d5e8ebe 100644 --- a/include/asm-x86/bits/pod.h +++ b/include/asm-x86/bits/pod.h @@ -271,28 +271,41 @@ static inline void __save_i387(x86_fpustate *fpup) static inline void xnarch_save_fpu(xnarchtcb_t *tcb) { struct task_struct *task = tcb->user_task; + unsigned branch; if (!tcb->is_root) { if (task) { /* fpu not used or already saved by __switch_to. */ - if (wrap_test_fpu_used(task) == 0) + if (wrap_test_fpu_used(task) == 0) { + fp_trace_xeno_save(0, NULL); return; + } /* Tell Linux that we already saved the state * of the FPU hardware of this task. */ wrap_clear_fpu_used(task); - } + branch = 1; + } else + branch = 2; } else { - if (tcb->cr0_ts || - (tcb->ts_usedfpu && wrap_test_fpu_used(task) == 0)) + if (tcb->cr0_ts) { + fp_trace_xeno_save(3, NULL); + return; + } + + if(tcb->ts_usedfpu && wrap_test_fpu_used(task) == 0) { + fp_trace_xeno_save(4, NULL); return; + } wrap_clear_fpu_used(task); + branch = 5; } clts(); __save_i387(tcb->fpup); + fp_trace_xeno_save(branch, &tcb->fpup->fxsave); } static inline void __restore_i387(x86_fpustate *fpup) @@ -332,10 +345,12 @@ static inline void __restore_i387(x86_fpustate *fpup) static inline void xnarch_restore_fpu(xnarchtcb_t * tcb) { struct task_struct *task = tcb->user_task; + unsigned branch; if (!tcb->is_root) { if (task) { if (!xnarch_fpu_init_p(task)) { + fp_trace_xeno_restore(0, NULL); stts(); return; /* Uninit fpu area -- do not restore. */ } @@ -343,10 +358,13 @@ static inline void xnarch_restore_fpu(xnarchtcb_t * tcb) /* Tell Linux that this task has altered the state of * the FPU hardware. */ wrap_set_fpu_used(task); - } + branch = 1; + } else + branch = 2; } else { /* Restore state of FPU only if TS bit in cr0 was clear. */ if (tcb->cr0_ts) { + fp_trace_xeno_restore(3, NULL); wrap_clear_fpu_used(task); stts(); return; @@ -358,25 +376,35 @@ static inline void xnarch_restore_fpu(xnarchtcb_t * tcb) it since we are switching to root, where fpu can be in lazy state. */ stts(); + fp_trace_xeno_restore(4, NULL); return; } + branch = 5; } /* Restore the FPU hardware with valid fp registers from a user-space or kernel thread. */ clts(); +<<<<<<< HEAD:include/asm-x86/bits/pod.h __restore_i387(tcb->fpup); +======= + fp_trace_xeno_restore(branch, &tcb->fpup->fxsave); + __restore_i387_checking(&tcb->fpup->fxsave); +>>>>>>> fpu tracer:include/asm-x86/bits/pod_64.h } static inline void xnarch_enable_fpu(xnarchtcb_t *tcb) { struct task_struct *task = tcb->user_task; + unsigned branch; if (!tcb->is_root) { if (task) { - if (!xnarch_fpu_init_p(task)) + if (!xnarch_fpu_init_p(task)) { + fp_trace_xeno_enable(0, NULL); return; +<<<<<<< HEAD:include/asm-x86/bits/pod.h /* * We used to test here if __switch_to had not * saved current fpu state, but this can not @@ -391,8 +419,31 @@ static inline void xnarch_enable_fpu(xnarchtcb_t *tcb) return; /* The comment in the non-root case applies here too. */ +======= + } + branch = 1; + + /* We used to test here if __switch_to had not saved + current fpu state, but this can not happen, since + xnarch_enable_fpu may only be called when switching + back to a user-space task after one or several + switches to non-fpu kernel-space real-time tasks, so + xnarch_switch_to never uses __switch_to. */ + } else + branch = 2; + } else { + if (tcb->cr0_ts) { + fp_trace_xeno_enable(3, NULL); + return; + } + + /* The comment in the non-root case applies here too. */ + branch = 4; + } +>>>>>>> fpu tracer:include/asm-x86/bits/pod_64.h clts(); + fp_trace_xeno_enable(branch, NULL); } #else /* !CONFIG_XENO_HW_FPU */ diff --git a/include/asm-x86/fp_dump.h b/include/asm-x86/fp_dump.h new file mode 100644 index 0000000..bd02993 --- /dev/null +++ b/include/asm-x86/fp_dump.h @@ -0,0 +1,96 @@ +#ifndef FP_DUMP_H +#define FP_DUMP_H + +#include <asm/processor.h> +#include <asm/fpu-internal.h> +#include <asm/xenomai/hal.h> +#include <asm-generic/xenomai/fp_dump.h> + +#define DECLARE_FPENV(var) \ + fpenv_t var ## _buf; \ + fpenv_t *var = (fpenv_t *)(((unsigned long)(&var ## _buf) + 15) & ~15) + +typedef struct fpreg { + unsigned long long significand; + unsigned short exp : 15; + unsigned short sign : 1; +} fpreg_t; + +typedef struct { + struct i387_fxsave_struct u; + char align[15]; +} fpenv_t; + +#define fp_enabled() ((read_cr0() & X86_CR0_TS) == 0) + +static inline void fpenv_save(fpenv_t *fpuenv) +{ +#ifdef __i386__ + asm volatile ("fnop; fwait; fxsave %0; fwait" : "=m"(fpuenv->u)); +#else + asm volatile ("fnop; fwait; fxsaveq %0; fwait" : "=m"(fpuenv->u)); +#endif +} + +static inline void fpenv_dump(void *cookie, fpenv_t *fpuenv) +{ + struct i387_fxsave_struct *fpenv = &fpuenv->u; + unsigned top, i; + char *stack; + + top = (fpenv->swd >> 11) & 0x7; + + fp_print(cookie, "FPU state:\n"); + fp_print(cookie, "cwd: 0x%04x, swd: 0x%04x\n", fpenv->cwd, fpenv->swd); + fp_print(cookie, "FPU registers stack, top at %u\n", top); + + stack = (char *)&fpenv->st_space[0]; + + for (i = 7; i <= 7; i--) { + fpreg_t *reg = (fpreg_t *)(stack + 16 * ((i - top) % 8)); + unsigned tag = !!(fpenv->twd & (1 << i)); + fp_conv_t fp = { + .significand = reg->significand, + .exp = (int)(reg->exp - 0x3FFF), + .pow10 = 0, + }; + + fp_print(cookie, "ST%u/MM%u: ", i, i); + switch (tag) { + case 0: /* Empty */ + fp_print(cookie, "Empty"); + break; + case 1: /* Not empty */ + switch (reg->exp) { + unsigned s; + case 0x7fff: /* Special */ + if (reg->significand == 0) { + s = reg->sign; + fp_print(cookie, "%c inf.", s ? '-' : '+'); + } else { + s = !!(reg->significand & (1ULL << 63)); + fp_print(cookie, "%cnan.", s ? 'Q' : 'S'); + } + break; + + case 0: + if (reg->significand == 0) { + fp_print(cookie, "0"); + break; + } + + /* Fall through wanted */ + default: + s = !!(reg->significand & (1ULL << 63)); + if (s == 0) + fp_print(cookie, "(DEN) "); + fp_disp(cookie, &fp, reg->sign); + break; + } + } + fp_print(cookie, " %c", i % 2 ? ' ' : '\n'); + } +} + + +#endif /* FP_DUMP_H */ diff --git a/include/nucleus/fpu_trace.h b/include/nucleus/fpu_trace.h new file mode 100644 index 0000000..712dc70 --- /dev/null +++ b/include/nucleus/fpu_trace.h @@ -0,0 +1,47 @@ +#ifndef FPU_TRACE_H +#define FPU_TRACE_H + +#ifdef CONFIG_FPU_TRACE + +struct task_struct; +struct xnthread; + +void fp_trace_switch(struct task_struct *from, struct task_struct *to); + +void fp_trace_save(void *to); + +void fp_trace_restore(void *from); + +void fp_trace_xeno_switch(struct xnthread *from, struct xnthread *to); + +void fp_trace_xeno_save(unsigned branch, void *to); + +void fp_trace_xeno_restore(unsigned branch, void *from); + +void fp_trace_xeno_enable(unsigned branch, void *from); + +void fp_trace_printf(const char *fmt, ...); + +void fp_trace_freeze(void); + +#else /* !CONFIG_FPU_TRACE */ + +#define fp_trace_switch(f, t) do { } while (0) + +#define fp_trace_save(t) do { } while (0) + +#define fp_trace_restore(f) do { } while (0) + +#define fp_trace_xeno_switch(f, t) do { } while (0) + +#define fp_trace_xeno_save(b, t) do { } while (0) + +#define fp_trace_xeno_restore(b, t) do { } while (0) + +#define fp_trace_xeno_enable(b, f) do { } while (0) + +#define fp_trace_freeze() do { } while (0) + +#endif /* !CONFIG_FPU_TRACE */ + +#endif /* FPU_TRACE_H */ diff --git a/ksrc/nucleus/Kconfig b/ksrc/nucleus/Kconfig index 2f2f6f6..5d6775e 100644 --- a/ksrc/nucleus/Kconfig +++ b/ksrc/nucleus/Kconfig @@ -357,6 +357,13 @@ config XENO_OPT_WATCHDOG_TIMEOUT Watchdog timeout value (in seconds). +config FPU_TRACE + bool "FPU tracing" + depends on XENO_OPT_DEBUG + help + + Enable FPU tracer + config XENO_OPT_SHIRQ bool "Shared interrupts" help diff --git a/ksrc/nucleus/Makefile b/ksrc/nucleus/Makefile index 70ee9b8..bd71109 100644 --- a/ksrc/nucleus/Makefile +++ b/ksrc/nucleus/Makefile @@ -18,6 +18,8 @@ xeno_nucleus-$(CONFIG_XENO_OPT_MAP) += map.o xeno_nucleus-$(CONFIG_XENO_OPT_SELECT) += select.o xeno_nucleus-$(CONFIG_PROC_FS) += vfile.o +xeno_nucleus-$(CONFIG_FPU_TRACE) += fpu_trace.o + # CAUTION: this module shall appear last, so that dependencies may # exist on initcalls defined by other object files. xeno_nucleus-y += module.o diff --git a/ksrc/nucleus/fpu_trace.c b/ksrc/nucleus/fpu_trace.c new file mode 100644 index 0000000..ca97814 --- /dev/null +++ b/ksrc/nucleus/fpu_trace.c @@ -0,0 +1,542 @@ +#include <linux/module.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> +#include <linux/percpu.h> +#include <nucleus/thread.h> +#include <nucleus/fpu_trace.h> + +#define fp_print(cookie, format, args...) \ + seq_printf((struct seq_file *)(cookie), format , ##args) + +#include <asm/xenomai/fp_dump.h> + +#ifndef SZ_128M +#define SZ_128M 0x08000000 +#endif /* SZ_128M */ +#define STORAGE_SIZE SZ_128M + +#define FP_TRACE_LTHR 0 +#define FP_TRACE_LFPS 1 +#define FP_TRACE_LFPR 2 +#define FP_TRACE_XTHR 3 +#define FP_TRACE_XFPS 4 +#define FP_TRACE_XFPR 5 +#define FP_TRACE_XFPEN 6 +#define FP_TRACE_STOP 7 +#define FP_TRACE_TEXT 8 + +struct fp_trace_pt { + unsigned short type; + unsigned short branch; +}; + +struct fp_trace_switch { + char from[64]; + char to[64]; +}; + +struct fp_trace_text { + char msg[128]; +}; + +struct tracer { + char *storage; + unsigned begin; + unsigned end; + unsigned points; + unsigned starting; + unsigned frozen; + unsigned bug; +}; + +struct tracer_it { + struct tracer *tracer; + unsigned pos; +}; + +static unsigned size_mask, ready, run = 0; +static DEFINE_PER_CPU(struct tracer, percpu_tracer); + +static void fp_trace_push(unsigned type, + unsigned branch, void *buf, unsigned len); + +static unsigned +fp_trace_print_task(char *buf, size_t size, + xnthread_t *thread, struct task_struct *user); + +void fp_trace_switch(struct task_struct *from, struct task_struct *to) +{ + struct fp_trace_switch buf; + + if (!run) + return; + fp_trace_print_task(&buf.from[0], sizeof(buf.from), NULL, from); + fp_trace_print_task(&buf.to[0], sizeof(buf.to), NULL, to); + fp_trace_push(FP_TRACE_LTHR, 0, &buf, sizeof(buf)); +} + +void fp_trace_save(void *to) +{ + fpenv_t *ctxt = (fpenv_t *)to; + if (!run) + return; + fp_trace_push(FP_TRACE_LFPS, 0, ctxt, sizeof(*ctxt)); +} + +void fp_trace_restore(void *from) +{ + fpenv_t *ctxt = (fpenv_t *)from; + if (!run) + return; + fp_trace_push(FP_TRACE_LFPR, 0, ctxt, sizeof(*ctxt)); +} + +void fp_trace_xeno_switch(xnthread_t *from, xnthread_t *to) +{ + struct fp_trace_switch buf; + + if (!run) + return; + + fp_trace_print_task(&buf.from[0], + sizeof(buf.from), from, xnthread_user_task(from)); + fp_trace_print_task(&buf.to[0], + sizeof(buf.to), to, xnthread_user_task(to)); + fp_trace_push(FP_TRACE_XTHR, 0, &buf, sizeof(buf)); +} + +void fp_trace_xeno_save(unsigned branch, void *to) +{ + DECLARE_FPENV(ctxt); + + if (!run) + return; + + if (to == NULL && fp_enabled()) + fpenv_save(ctxt); + else + ctxt = (fpenv_t *)to; + + if (ctxt) + fp_trace_push(FP_TRACE_XFPS, + branch | (1 << 15), ctxt, sizeof(*ctxt)); + else + fp_trace_push(FP_TRACE_XFPS, branch, NULL, 0); +} + +void fp_trace_xeno_restore(unsigned branch, void *from) +{ + DECLARE_FPENV(ctxt); + + if (!run) + return; + + if (from == NULL && fp_enabled()) + fpenv_save(ctxt); + else + ctxt = (fpenv_t *)from; + + if (ctxt) + fp_trace_push(FP_TRACE_XFPR, + branch | (1 << 15), ctxt, sizeof(*ctxt)); + else + fp_trace_push(FP_TRACE_XFPR, branch, NULL, 0); +} + +void fp_trace_xeno_enable(unsigned branch, void *from) +{ + DECLARE_FPENV(ctxt); + + if (!run) + return; + + if (from == NULL && fp_enabled()) + fpenv_save(ctxt); + else + ctxt = (fpenv_t *)from; + + if (ctxt) + fp_trace_push(FP_TRACE_XFPEN, + branch | (1 << 15), ctxt, sizeof(*ctxt)); + else + fp_trace_push(FP_TRACE_XFPEN, branch, NULL, 0); +} + +void fp_trace_freeze(void) +{ + struct tracer *t; + + preempt_disable(); + t = &get_cpu_var(percpu_tracer); + + if (fp_enabled()) { + DECLARE_FPENV(ctxt); + fpenv_save(ctxt); + fp_trace_push(FP_TRACE_STOP, 1 << 15, ctxt, sizeof(*ctxt)); + } else + fp_trace_push(FP_TRACE_STOP, 0, NULL, 0); + t->frozen = 1; + preempt_enable(); +} + +void fp_trace_printf(const char *fmt, ...) +{ + struct fp_trace_text txt; + va_list ap; + + va_start(ap, fmt); + vsnprintf(txt.msg, sizeof(txt.msg), fmt, ap); + va_end(ap); + + fp_trace_push(FP_TRACE_TEXT, 0, &txt, sizeof(txt)); +} + +static unsigned fp_trace_next_entry(struct tracer *t, unsigned cur) +{ + struct fp_trace_pt *pt = (struct fp_trace_pt *)(t->storage + cur); + if (cur % 4) { + t->bug = 1; + show_stack(NULL, NULL); + BUG(); + } + cur += sizeof(*pt); + switch(pt->type) { + case FP_TRACE_LTHR: + case FP_TRACE_XTHR: + cur += sizeof(struct fp_trace_switch); + break; + case FP_TRACE_LFPS: + case FP_TRACE_LFPR: + cur += sizeof(fpenv_t); + break; + case FP_TRACE_XFPS: + case FP_TRACE_XFPR: + case FP_TRACE_XFPEN: + if (pt->branch & (1 << 15)) + cur += sizeof(fpenv_t); + break; + case FP_TRACE_STOP: + if (pt->branch & (1 << 15)) + cur += sizeof(fpenv_t); + break; + case FP_TRACE_TEXT: + cur += sizeof(struct fp_trace_text); + break; + default: + printk("next_entry(0x%08x/0x%08x): invalid type %d\n", + cur, size_mask, pt->type); + t->bug = 1; + BUG(); + } + if (cur % 4) { + t->bug = 1; + show_stack(NULL, NULL); + BUG(); + } + return cur & size_mask; +} + +static void fp_trace_push(unsigned type, + unsigned branch, void *buf, unsigned len) +{ + struct fp_trace_pt *pt; + struct tracer *t; + unsigned long flags; + + local_irq_save_hw(flags); + t = &get_cpu_var(percpu_tracer); + if (t->frozen || ready == 0 || t->bug) + goto unlock; + + pt = (struct fp_trace_pt *)(t->storage + t->end); + if (unlikely(t->starting)) + t->starting = 0; + else + while (((t->begin - t->end) & size_mask) < sizeof(*t) + len) { + t->begin = fp_trace_next_entry(t, t->begin); + --t->points; + } + + BUG_ON(type > FP_TRACE_TEXT); + pt->type = type; + pt->branch = branch; + t->end = (t->end + sizeof(*pt)) & size_mask; + if (!len) + goto inc_points; + if (t->end + len <= size_mask + 1) + memcpy(t->storage + t->end, buf, len); + else { + unsigned cut = size_mask + 1 - t->end; + memcpy(t->storage + t->end, buf, cut); + memcpy(t->storage, buf + cut, len - cut); + } + t->end = (t->end + len) & size_mask; +#if 0 + if (fp_trace_next_entry(t, (char *)pt - t->storage) != t->end) { + printk("Trace, type: %d, entry: %x, len: %x, next: %x," + " end: %x\n", pt->type, (char *)pt - t->storage, len, + fp_trace_next_entry(t, (char *)pt - t->storage), t->end); + t->bug = 1; + BUG(); + } +#endif + inc_points: + ++t->points; + unlock: + local_irq_restore_hw(flags); +} + +static unsigned +fp_trace_print_task(char *buf, size_t size, + xnthread_t *thread, struct task_struct *user) +{ + if (!user) + return snprintf(buf, size, + "RTK: %s(%p)", xnthread_name(thread), thread); + + if (thread && !xnthread_test_state(thread, XNROOT)) + return snprintf(buf, size, + "RTUP: %s[%d]", user->comm, user->pid); + + thread = xnshadow_thread(user); + if (thread) + return snprintf(buf, size, + "RTUS: %s[%d]", user->comm, user->pid); + + return snprintf(buf, size, "NRT: %s[%d]", user->comm, user->pid); +} + +static void *fp_trace_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct tracer_it *it = seq->private; + struct tracer *t = it->tracer; + unsigned i; + + if (!t->frozen || *pos >= t->points) + return NULL; + + for (i = 0, it->pos = t->begin; i < *pos; i++) + it->pos = fp_trace_next_entry(t, it->pos); + + return t->storage + it->pos; +} + +static void *fp_trace_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct tracer_it *it = seq->private; + struct tracer *t = it->tracer; + + if(++*pos >= t->points) + return NULL; + + it->pos = fp_trace_next_entry(t, it->pos); + + return t->storage + it->pos; +} + +static void fp_trace_seq_stop(struct seq_file *seq, void *v) +{ +} + +static void cpy_buf(struct tracer *t, void *dest, unsigned len, void *src) +{ + unsigned cur = ((char *)src - t->storage) & size_mask; + + if (cur + len <= size_mask + 1) + memcpy(dest, src, len); + else { + unsigned cut = size_mask - cur; + memcpy(dest, src, cut); + memcpy(dest + cut, t->storage, len - cut); + } +} + +static int fp_trace_seq_show(struct seq_file *seq, void *v) +{ + struct tracer_it *it = seq->private; + struct tracer *t = it->tracer; + struct fp_trace_pt *pt = v; + struct fp_trace_switch swtch; + struct fp_trace_text text; + fpenv_t ctxt; + + switch(pt->type) { + case FP_TRACE_LTHR: + cpy_buf(t, &swtch, sizeof(swtch), pt + 1); + seq_printf(seq, "Linux switch from %s to %s\n", + swtch.from, swtch.to); + break; + + case FP_TRACE_LFPS: + seq_printf(seq, "Linux saving fpu\n"); + cpy_buf(t, &ctxt, sizeof(ctxt), pt + 1); + fpenv_dump(seq, &ctxt); + break; + + case FP_TRACE_LFPR: + seq_printf(seq, "Linux restoring fpu\n"); + cpy_buf(t, &ctxt, sizeof(ctxt), pt + 1); + fpenv_dump(seq, &ctxt); + break; + + case FP_TRACE_XTHR: + cpy_buf(t, &swtch, sizeof(swtch), pt + 1); + seq_printf(seq, "Xeno switch from %s to %s\n", + swtch.from, swtch.to); + break; + + case FP_TRACE_XFPS: + seq_printf(seq, "Xeno saving fpu (branch %d)\n", + pt->branch & ~(1 << 15)); + if (pt->branch & (1 << 15)) { + cpy_buf(t, &ctxt, sizeof(ctxt), pt + 1); + fpenv_dump(seq, &ctxt); + } + break; + + case FP_TRACE_XFPR: + seq_printf(seq, "Xeno restoring fpu (branch %d)\n", + pt->branch & ~(1 << 15)); + if (pt->branch & (1 << 15)) { + cpy_buf(t, &ctxt, sizeof(ctxt), pt + 1); + fpenv_dump(seq, &ctxt); + } + break; + + case FP_TRACE_XFPEN: + seq_printf(seq, "Xeno enabling fpu (branch %d)\n", + pt->branch & ~(1 << 15)); + if (pt->branch & (1 << 15)) { + cpy_buf(t, &ctxt, sizeof(ctxt), pt + 1); + fpenv_dump(seq, &ctxt); + } + break; + + case FP_TRACE_STOP: + seq_printf(seq, "Capture stopped\n"); + if (pt->branch & (1 << 15)) { + cpy_buf(t, &ctxt, sizeof(ctxt), pt + 1); + fpenv_dump(seq, &ctxt); + } + break; + + case FP_TRACE_TEXT: + cpy_buf(t, &text, sizeof(text), pt + 1); + seq_printf(seq, "%s\n", text.msg); + break; + + default: + printk("type: 0x%04x, cur: 0x%016lx\n", + pt->type, (char *) pt - t->storage); + return -EINVAL; + } + + return 0; +} + +static struct seq_operations fp_trace_seq_op = { + .start = &fp_trace_seq_start, + .next = &fp_trace_seq_next, + .stop = &fp_trace_seq_stop, + .show = &fp_trace_seq_show +}; + +static int fp_trace_seq_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq; + struct tracer_it *it; + struct tracer *t; + int rc, i; + + if (!ready) + return -ENOENT; + + for (i = 0; i < num_online_cpus(); i++) { + t = &per_cpu(percpu_tracer, i); + if (t->frozen) + goto found; + } + + return -ENOENT; + found: + it = kmalloc(sizeof(*it), GFP_KERNEL); + if (!it) + return -ENOSPC; + it->tracer = t; + + rc = seq_open(file, &fp_trace_seq_op); + if (rc < 0) { + kfree(it); + return rc; + } + + seq = file->private_data; + seq->private = it; + + return rc; +} + +static int fp_trace_seq_release(struct inode *inode, struct file *file) +{ + struct seq_file *seq = file->private_data; + struct trace_it *it; + + if (!seq) + return 0; + + it = seq->private; + if (it) + kfree(it); + + return seq_release(inode, file); +} + +static struct file_operations fp_trace_seq_operations = { + .owner = THIS_MODULE, + .open = fp_trace_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = fp_trace_seq_release, +}; + +extern struct proc_dir_entry *rthal_proc_root; + +int __init fp_trace_init(void) +{ + struct proc_dir_entry *entry; + unsigned i, d, cpus; + char *glob_storage; + + glob_storage = vmalloc(STORAGE_SIZE); + if (glob_storage == 0) + return -ENOSPC; + + cpus = num_online_cpus(); + d = 1 << fp_flnzul(cpus); + if (cpus != d) + d <<= 1; + for (i = 0; i < cpus; i++) { + struct tracer *t = &per_cpu(percpu_tracer, i); + + t->storage = glob_storage + i * (STORAGE_SIZE / d); + t->begin = 0; + t->end = 0; + t->points = 0; + t->starting = 1; + t->frozen = 0; + t->bug = 0; + } + + entry = create_proc_entry("fp_trace_dump", 0, NULL); + if (!entry) + return -EINVAL; + + entry->proc_fops = &fp_trace_seq_operations; + wrap_proc_dir_entry_owner(entry); + + size_mask = STORAGE_SIZE / d - 1; + run = ready = 1; + return 0; +} + +module_init(fp_trace_init); diff --git a/ksrc/nucleus/pod.c b/ksrc/nucleus/pod.c index f977db3..77321cd 100644 --- a/ksrc/nucleus/pod.c +++ b/ksrc/nucleus/pod.c @@ -44,6 +44,7 @@ #include <nucleus/stat.h> #include <nucleus/assert.h> #include <nucleus/select.h> +#include <nucleus/fpu_trace.h> #include <asm/xenomai/bits/pod.h> /* @@ -2086,6 +2087,7 @@ EXPORT_SYMBOL_GPL(xnpod_welcome_thread); static inline void xnpod_switch_to(xnsched_t *sched, xnthread_t *prev, xnthread_t *next) { + fp_trace_xeno_switch(prev, next); #ifdef CONFIG_XENO_HW_UNLOCKED_SWITCH sched->last = prev; __setbits(sched->status, XNINSW); @@ -2571,6 +2573,7 @@ int xnpod_trap_fault(xnarch_fltinfo_t *fltinfo) if (xnarch_fault_fpu_p(fltinfo)) { if (__xnpod_fault_init_fpu(thread)) return 1; + fp_trace_freeze(); print_symbol("invalid use of FPU in Xenomai context at %s\n", xnarch_fault_pc(fltinfo)); } diff --git a/ksrc/nucleus/shadow.c b/ksrc/nucleus/shadow.c index 7c4e032..be0843a 100644 --- a/ksrc/nucleus/shadow.c +++ b/ksrc/nucleus/shadow.c @@ -53,6 +53,8 @@ #include <nucleus/stat.h> #include <nucleus/sys_ppd.h> #include <nucleus/vdso.h> +#include <nucleus/fpu_trace.h> + #include <asm/xenomai/features.h> #include <asm/xenomai/syscall.h> #include <asm/xenomai/bits/shadow.h> @@ -2041,6 +2043,7 @@ static int xnshadow_sys_trace(struct pt_regs *regs) break; case __xntrace_op_user_freeze: + fp_trace_freeze(); err = xnarch_trace_user_freeze(__xn_reg_arg2(regs), __xn_reg_arg3(regs)); break; _______________________________________________ Xenomai-git mailing list Xenomai-git@xenomai.org http://www.xenomai.org/mailman/listinfo/xenomai-git