Commit 9f8b6a6cf0ee78de87ebe1e87f54bec1c1741ef7

Authored by Ingo Molnar

Merge branch 'core' of git://git.kernel.org/pub/scm/linux/kernel/git/rric/oprofile into perf/core

Showing 8 changed files Side-by-side Diff

arch/mips/include/asm/stacktrace.h
... ... @@ -7,6 +7,10 @@
7 7 extern int raw_show_trace;
8 8 extern unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
9 9 unsigned long pc, unsigned long *ra);
  10 +extern unsigned long unwind_stack_by_address(unsigned long stack_page,
  11 + unsigned long *sp,
  12 + unsigned long pc,
  13 + unsigned long *ra);
10 14 #else
11 15 #define raw_show_trace 1
12 16 static inline unsigned long unwind_stack(struct task_struct *task,
arch/mips/kernel/process.c
... ... @@ -373,18 +373,18 @@
373 373  
374 374  
375 375 #ifdef CONFIG_KALLSYMS
376   -/* used by show_backtrace() */
377   -unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
378   - unsigned long pc, unsigned long *ra)
  376 +/* generic stack unwinding function */
  377 +unsigned long notrace unwind_stack_by_address(unsigned long stack_page,
  378 + unsigned long *sp,
  379 + unsigned long pc,
  380 + unsigned long *ra)
379 381 {
380   - unsigned long stack_page;
381 382 struct mips_frame_info info;
382 383 unsigned long size, ofs;
383 384 int leaf;
384 385 extern void ret_from_irq(void);
385 386 extern void ret_from_exception(void);
386 387  
387   - stack_page = (unsigned long)task_stack_page(task);
388 388 if (!stack_page)
389 389 return 0;
390 390  
... ... @@ -442,6 +442,15 @@
442 442 *sp += info.frame_size;
443 443 *ra = 0;
444 444 return __kernel_text_address(pc) ? pc : 0;
  445 +}
  446 +EXPORT_SYMBOL(unwind_stack_by_address);
  447 +
  448 +/* used by show_backtrace() */
  449 +unsigned long unwind_stack(struct task_struct *task, unsigned long *sp,
  450 + unsigned long pc, unsigned long *ra)
  451 +{
  452 + unsigned long stack_page = (unsigned long)task_stack_page(task);
  453 + return unwind_stack_by_address(stack_page, sp, pc, ra);
445 454 }
446 455 #endif
447 456  
arch/mips/oprofile/Makefile
... ... @@ -8,7 +8,7 @@
8 8 oprofilefs.o oprofile_stats.o \
9 9 timer_int.o )
10 10  
11   -oprofile-y := $(DRIVER_OBJS) common.o
  11 +oprofile-y := $(DRIVER_OBJS) common.o backtrace.o
12 12  
13 13 oprofile-$(CONFIG_CPU_MIPS32) += op_model_mipsxx.o
14 14 oprofile-$(CONFIG_CPU_MIPS64) += op_model_mipsxx.o
arch/mips/oprofile/backtrace.c
  1 +#include <linux/oprofile.h>
  2 +#include <linux/sched.h>
  3 +#include <linux/mm.h>
  4 +#include <linux/uaccess.h>
  5 +#include <asm/ptrace.h>
  6 +#include <asm/stacktrace.h>
  7 +#include <linux/stacktrace.h>
  8 +#include <linux/kernel.h>
  9 +#include <asm/sections.h>
  10 +#include <asm/inst.h>
  11 +
  12 +struct stackframe {
  13 + unsigned long sp;
  14 + unsigned long pc;
  15 + unsigned long ra;
  16 +};
  17 +
  18 +static inline int get_mem(unsigned long addr, unsigned long *result)
  19 +{
  20 + unsigned long *address = (unsigned long *) addr;
  21 + if (!access_ok(VERIFY_READ, addr, sizeof(unsigned long)))
  22 + return -1;
  23 + if (__copy_from_user_inatomic(result, address, sizeof(unsigned long)))
  24 + return -3;
  25 + return 0;
  26 +}
  27 +
  28 +/*
  29 + * These two instruction helpers were taken from process.c
  30 + */
  31 +static inline int is_ra_save_ins(union mips_instruction *ip)
  32 +{
  33 + /* sw / sd $ra, offset($sp) */
  34 + return (ip->i_format.opcode == sw_op || ip->i_format.opcode == sd_op)
  35 + && ip->i_format.rs == 29 && ip->i_format.rt == 31;
  36 +}
  37 +
  38 +static inline int is_sp_move_ins(union mips_instruction *ip)
  39 +{
  40 + /* addiu/daddiu sp,sp,-imm */
  41 + if (ip->i_format.rs != 29 || ip->i_format.rt != 29)
  42 + return 0;
  43 + if (ip->i_format.opcode == addiu_op || ip->i_format.opcode == daddiu_op)
  44 + return 1;
  45 + return 0;
  46 +}
  47 +
  48 +/*
  49 + * Looks for specific instructions that mark the end of a function.
  50 + * This usually means we ran into the code area of the previous function.
  51 + */
  52 +static inline int is_end_of_function_marker(union mips_instruction *ip)
  53 +{
  54 + /* jr ra */
  55 + if (ip->r_format.func == jr_op && ip->r_format.rs == 31)
  56 + return 1;
  57 + /* lui gp */
  58 + if (ip->i_format.opcode == lui_op && ip->i_format.rt == 28)
  59 + return 1;
  60 + return 0;
  61 +}
  62 +
  63 +/*
  64 + * TODO for userspace stack unwinding:
  65 + * - handle cases where the stack is adjusted inside a function
  66 + * (generally doesn't happen)
  67 + * - find optimal value for max_instr_check
  68 + * - try to find a way to handle leaf functions
  69 + */
  70 +
  71 +static inline int unwind_user_frame(struct stackframe *old_frame,
  72 + const unsigned int max_instr_check)
  73 +{
  74 + struct stackframe new_frame = *old_frame;
  75 + off_t ra_offset = 0;
  76 + size_t stack_size = 0;
  77 + unsigned long addr;
  78 +
  79 + if (old_frame->pc == 0 || old_frame->sp == 0 || old_frame->ra == 0)
  80 + return -9;
  81 +
  82 + for (addr = new_frame.pc; (addr + max_instr_check > new_frame.pc)
  83 + && (!ra_offset || !stack_size); --addr) {
  84 + union mips_instruction ip;
  85 +
  86 + if (get_mem(addr, (unsigned long *) &ip))
  87 + return -11;
  88 +
  89 + if (is_sp_move_ins(&ip)) {
  90 + int stack_adjustment = ip.i_format.simmediate;
  91 + if (stack_adjustment > 0)
  92 + /* This marks the end of the previous function,
  93 + which means we overran. */
  94 + break;
  95 + stack_size = (unsigned) stack_adjustment;
  96 + } else if (is_ra_save_ins(&ip)) {
  97 + int ra_slot = ip.i_format.simmediate;
  98 + if (ra_slot < 0)
  99 + /* This shouldn't happen. */
  100 + break;
  101 + ra_offset = ra_slot;
  102 + } else if (is_end_of_function_marker(&ip))
  103 + break;
  104 + }
  105 +
  106 + if (!ra_offset || !stack_size)
  107 + return -1;
  108 +
  109 + if (ra_offset) {
  110 + new_frame.ra = old_frame->sp + ra_offset;
  111 + if (get_mem(new_frame.ra, &(new_frame.ra)))
  112 + return -13;
  113 + }
  114 +
  115 + if (stack_size) {
  116 + new_frame.sp = old_frame->sp + stack_size;
  117 + if (get_mem(new_frame.sp, &(new_frame.sp)))
  118 + return -14;
  119 + }
  120 +
  121 + if (new_frame.sp > old_frame->sp)
  122 + return -2;
  123 +
  124 + new_frame.pc = old_frame->ra;
  125 + *old_frame = new_frame;
  126 +
  127 + return 0;
  128 +}
  129 +
  130 +static inline void do_user_backtrace(unsigned long low_addr,
  131 + struct stackframe *frame,
  132 + unsigned int depth)
  133 +{
  134 + const unsigned int max_instr_check = 512;
  135 + const unsigned long high_addr = low_addr + THREAD_SIZE;
  136 +
  137 + while (depth-- && !unwind_user_frame(frame, max_instr_check)) {
  138 + oprofile_add_trace(frame->ra);
  139 + if (frame->sp < low_addr || frame->sp > high_addr)
  140 + break;
  141 + }
  142 +}
  143 +
  144 +#ifndef CONFIG_KALLSYMS
  145 +static inline void do_kernel_backtrace(unsigned long low_addr,
  146 + struct stackframe *frame,
  147 + unsigned int depth) { }
  148 +#else
  149 +static inline void do_kernel_backtrace(unsigned long low_addr,
  150 + struct stackframe *frame,
  151 + unsigned int depth)
  152 +{
  153 + while (depth-- && frame->pc) {
  154 + frame->pc = unwind_stack_by_address(low_addr,
  155 + &(frame->sp),
  156 + frame->pc,
  157 + &(frame->ra));
  158 + oprofile_add_trace(frame->ra);
  159 + }
  160 +}
  161 +#endif
  162 +
  163 +void notrace op_mips_backtrace(struct pt_regs *const regs, unsigned int depth)
  164 +{
  165 + struct stackframe frame = { .sp = regs->regs[29],
  166 + .pc = regs->cp0_epc,
  167 + .ra = regs->regs[31] };
  168 + const int userspace = user_mode(regs);
  169 + const unsigned long low_addr = ALIGN(frame.sp, THREAD_SIZE);
  170 +
  171 + if (userspace)
  172 + do_user_backtrace(low_addr, &frame, depth);
  173 + else
  174 + do_kernel_backtrace(low_addr, &frame, depth);
  175 +}
arch/mips/oprofile/common.c
... ... @@ -115,6 +115,7 @@
115 115 ops->start = op_mips_start;
116 116 ops->stop = op_mips_stop;
117 117 ops->cpu_type = lmodel->cpu_type;
  118 + ops->backtrace = op_mips_backtrace;
118 119  
119 120 printk(KERN_INFO "oprofile: using %s performance monitoring.\n",
120 121 lmodel->cpu_type);
arch/mips/oprofile/op_impl.h
... ... @@ -36,5 +36,7 @@
36 36 unsigned char num_counters;
37 37 };
38 38  
  39 +void op_mips_backtrace(struct pt_regs * const regs, unsigned int depth);
  40 +
39 41 #endif
arch/x86/oprofile/backtrace.c
... ... @@ -11,10 +11,12 @@
11 11 #include <linux/oprofile.h>
12 12 #include <linux/sched.h>
13 13 #include <linux/mm.h>
  14 +#include <linux/compat.h>
  15 +#include <linux/highmem.h>
  16 +
14 17 #include <asm/ptrace.h>
15 18 #include <asm/uaccess.h>
16 19 #include <asm/stacktrace.h>
17   -#include <linux/compat.h>
18 20  
19 21 static int backtrace_stack(void *data, char *name)
20 22 {
21 23  
22 24  
23 25  
24 26  
... ... @@ -36,18 +38,54 @@
36 38 .walk_stack = print_context_stack,
37 39 };
38 40  
  41 +/* from arch/x86/kernel/cpu/perf_event.c: */
  42 +
  43 +/*
  44 + * best effort, GUP based copy_from_user() that assumes IRQ or NMI context
  45 + */
  46 +static unsigned long
  47 +copy_from_user_nmi(void *to, const void __user *from, unsigned long n)
  48 +{
  49 + unsigned long offset, addr = (unsigned long)from;
  50 + unsigned long size, len = 0;
  51 + struct page *page;
  52 + void *map;
  53 + int ret;
  54 +
  55 + do {
  56 + ret = __get_user_pages_fast(addr, 1, 0, &page);
  57 + if (!ret)
  58 + break;
  59 +
  60 + offset = addr & (PAGE_SIZE - 1);
  61 + size = min(PAGE_SIZE - offset, n - len);
  62 +
  63 + map = kmap_atomic(page);
  64 + memcpy(to, map+offset, size);
  65 + kunmap_atomic(map);
  66 + put_page(page);
  67 +
  68 + len += size;
  69 + to += size;
  70 + addr += size;
  71 +
  72 + } while (len < n);
  73 +
  74 + return len;
  75 +}
  76 +
39 77 #ifdef CONFIG_COMPAT
40 78 static struct stack_frame_ia32 *
41 79 dump_user_backtrace_32(struct stack_frame_ia32 *head)
42 80 {
  81 + /* Also check accessibility of one struct frame_head beyond: */
43 82 struct stack_frame_ia32 bufhead[2];
44 83 struct stack_frame_ia32 *fp;
  84 + unsigned long bytes;
45 85  
46   - /* Also check accessibility of one struct frame_head beyond */
47   - if (!access_ok(VERIFY_READ, head, sizeof(bufhead)))
  86 + bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead));
  87 + if (bytes != sizeof(bufhead))
48 88 return NULL;
49   - if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead)))
50   - return NULL;
51 89  
52 90 fp = (struct stack_frame_ia32 *) compat_ptr(bufhead[0].next_frame);
53 91  
54 92  
55 93  
... ... @@ -87,12 +125,12 @@
87 125  
88 126 static struct stack_frame *dump_user_backtrace(struct stack_frame *head)
89 127 {
  128 + /* Also check accessibility of one struct frame_head beyond: */
90 129 struct stack_frame bufhead[2];
  130 + unsigned long bytes;
91 131  
92   - /* Also check accessibility of one struct stack_frame beyond */
93   - if (!access_ok(VERIFY_READ, head, sizeof(bufhead)))
94   - return NULL;
95   - if (__copy_from_user_inatomic(bufhead, head, sizeof(bufhead)))
  132 + bytes = copy_from_user_nmi(bufhead, head, sizeof(bufhead));
  133 + if (bytes != sizeof(bufhead))
96 134 return NULL;
97 135  
98 136 oprofile_add_trace(bufhead[0].return_address);
arch/x86/oprofile/nmi_int.c
... ... @@ -112,8 +112,10 @@
112 112 static int nmi_start(void)
113 113 {
114 114 get_online_cpus();
115   - on_each_cpu(nmi_cpu_start, NULL, 1);
116 115 ctr_running = 1;
  116 + /* make ctr_running visible to the nmi handler: */
  117 + smp_mb();
  118 + on_each_cpu(nmi_cpu_start, NULL, 1);
117 119 put_online_cpus();
118 120 return 0;
119 121 }
120 122  
121 123  
... ... @@ -504,15 +506,18 @@
504 506  
505 507 nmi_enabled = 0;
506 508 ctr_running = 0;
507   - barrier();
  509 + /* make variables visible to the nmi handler: */
  510 + smp_mb();
508 511 err = register_die_notifier(&profile_exceptions_nb);
509 512 if (err)
510 513 goto fail;
511 514  
512 515 get_online_cpus();
513 516 register_cpu_notifier(&oprofile_cpu_nb);
514   - on_each_cpu(nmi_cpu_setup, NULL, 1);
515 517 nmi_enabled = 1;
  518 + /* make nmi_enabled visible to the nmi handler: */
  519 + smp_mb();
  520 + on_each_cpu(nmi_cpu_setup, NULL, 1);
516 521 put_online_cpus();
517 522  
518 523 return 0;
... ... @@ -531,7 +536,8 @@
531 536 nmi_enabled = 0;
532 537 ctr_running = 0;
533 538 put_online_cpus();
534   - barrier();
  539 + /* make variables visible to the nmi handler: */
  540 + smp_mb();
535 541 unregister_die_notifier(&profile_exceptions_nb);
536 542 msrs = &get_cpu_var(cpu_msrs);
537 543 model->shutdown(msrs);