Commit 7d241ff0567b9503d79ee775c40927d09b509f83

Authored by Michal Simek
1 parent 6d9e60ce30

microblaze: ftrace: Add dynamic trace support

With dynamic function tracer, by default, _mcount is defined as an
"empty" function, it returns directly without any more action. When
enabling it in user-space, it will jump to a real tracing
function(ftrace_caller), and do the real job for us.

Differ from the static function tracer, dynamic function tracer provides
two functions ftrace_make_call()/ftrace_make_nop() to enable/disable the
tracing of some indicated kernel functions(set_ftrace_filter).

In the kernel version, there is only one "_mcount" string for every
kernel function, so, we just need to match this one in mcount_regex of
scripts/recordmcount.pl.

For more information please look at code and Documentation/trace folder.

Steven ACK that scripts/recordmcount.pl part.

Acked-by: Steven Rostedt <rostedt@goodmis.org>
Signed-off-by: Michal Simek <monstr@monstr.eu>

Showing 6 changed files with 181 additions and 1 deletions Side-by-side Diff

arch/microblaze/Kconfig
... ... @@ -8,6 +8,8 @@
8 8 select HAVE_LMB
9 9 select HAVE_FUNCTION_TRACER
10 10 select HAVE_FUNCTION_TRACE_MCOUNT_TEST
  11 + select HAVE_DYNAMIC_FTRACE
  12 + select HAVE_FTRACE_MCOUNT_RECORD
11 13 select USB_ARCH_HAS_EHCI
12 14 select ARCH_WANT_OPTIONAL_GPIOLIB
13 15  
arch/microblaze/include/asm/ftrace.h
... ... @@ -11,6 +11,17 @@
11 11 extern void ftrace_call_graph(void);
12 12 #endif
13 13  
  14 +#ifdef CONFIG_DYNAMIC_FTRACE
  15 +/* reloction of mcount call site is the same as the address */
  16 +static inline unsigned long ftrace_call_adjust(unsigned long addr)
  17 +{
  18 + return addr;
  19 +}
  20 +
  21 +struct dyn_arch_ftrace {
  22 +};
  23 +#endif /* CONFIG_DYNAMIC_FTRACE */
  24 +
14 25 #endif /* CONFIG_FUNCTION_TRACER */
15 26 #endif /* _ASM_MICROBLAZE_FTRACE */
arch/microblaze/kernel/Makefile
... ... @@ -27,7 +27,7 @@
27 27 obj-$(CONFIG_MODULES) += microblaze_ksyms.o module.o
28 28 obj-$(CONFIG_MMU) += misc.o
29 29 obj-$(CONFIG_STACKTRACE) += stacktrace.o
30   -obj-$(CONFIG_FUNCTION_TRACER) += mcount.o
  30 +obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o mcount.o
31 31  
32 32 obj-y += entry$(MMU).o
arch/microblaze/kernel/ftrace.c
  1 +/*
  2 + * Ftrace support for Microblaze.
  3 + *
  4 + * Copyright (C) 2009 Michal Simek <monstr@monstr.eu>
  5 + * Copyright (C) 2009 PetaLogix
  6 + *
  7 + * Based on MIPS and PowerPC ftrace code
  8 + *
  9 + * This file is subject to the terms and conditions of the GNU General Public
  10 + * License. See the file "COPYING" in the main directory of this archive
  11 + * for more details.
  12 + */
  13 +
  14 +#include <asm/cacheflush.h>
  15 +#include <linux/ftrace.h>
  16 +
  17 +#ifdef CONFIG_DYNAMIC_FTRACE
  18 +/* save value to addr - it is save to do it in asm */
  19 +static int ftrace_modify_code(unsigned long addr, unsigned int value)
  20 +{
  21 + int faulted = 0;
  22 +
  23 + __asm__ __volatile__(" 1: swi %2, %1, 0; \
  24 + addik %0, r0, 0; \
  25 + 2: \
  26 + .section .fixup, \"ax\"; \
  27 + 3: brid 2b; \
  28 + addik %0, r0, 1; \
  29 + .previous; \
  30 + .section __ex_table,\"a\"; \
  31 + .word 1b,3b; \
  32 + .previous;" \
  33 + : "=r" (faulted)
  34 + : "r" (addr), "r" (value)
  35 + );
  36 +
  37 + if (unlikely(faulted))
  38 + return -EFAULT;
  39 +
  40 + return 0;
  41 +}
  42 +
  43 +#define MICROBLAZE_NOP 0x80000000
  44 +#define MICROBLAZE_BRI 0xb800000C
  45 +
  46 +static unsigned int recorded; /* if save was or not */
  47 +static unsigned int imm; /* saving whole imm instruction */
  48 +
  49 +/* There are two approaches howto solve ftrace_make nop function - look below */
  50 +#undef USE_FTRACE_NOP
  51 +
  52 +#ifdef USE_FTRACE_NOP
  53 +static unsigned int bralid; /* saving whole bralid instruction */
  54 +#endif
  55 +
  56 +int ftrace_make_nop(struct module *mod,
  57 + struct dyn_ftrace *rec, unsigned long addr)
  58 +{
  59 + /* we have this part of code which we are working with
  60 + * b000c000 imm -16384
  61 + * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount>
  62 + * 80000000 or r0, r0, r0
  63 + *
  64 + * The first solution (!USE_FTRACE_NOP-could be called branch solution)
  65 + * b000c000 bri 12 (0xC - jump to any other instruction)
  66 + * b9fc8e30 bralid r15, -29136 // c0008e30 <_mcount>
  67 + * 80000000 or r0, r0, r0
  68 + * any other instruction
  69 + *
  70 + * The second solution (USE_FTRACE_NOP) - no jump just nops
  71 + * 80000000 or r0, r0, r0
  72 + * 80000000 or r0, r0, r0
  73 + * 80000000 or r0, r0, r0
  74 + */
  75 + int ret = 0;
  76 +
  77 + if (recorded == 0) {
  78 + recorded = 1;
  79 + imm = *(unsigned int *)rec->ip;
  80 + pr_debug("%s: imm:0x%x\n", __func__, imm);
  81 +#ifdef USE_FTRACE_NOP
  82 + bralid = *(unsigned int *)(rec->ip + 4);
  83 + pr_debug("%s: bralid 0x%x\n", __func__, bralid);
  84 +#endif /* USE_FTRACE_NOP */
  85 + }
  86 +
  87 +#ifdef USE_FTRACE_NOP
  88 + ret = ftrace_modify_code(rec->ip, MICROBLAZE_NOP);
  89 + ret += ftrace_modify_code(rec->ip + 4, MICROBLAZE_NOP);
  90 +#else /* USE_FTRACE_NOP */
  91 + ret = ftrace_modify_code(rec->ip, MICROBLAZE_BRI);
  92 +#endif /* USE_FTRACE_NOP */
  93 + return ret;
  94 +}
  95 +
  96 +static int ret_addr; /* initialized as 0 by default */
  97 +
  98 +/* I believe that first is called ftrace_make_nop before this function */
  99 +int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
  100 +{
  101 + int ret;
  102 + ret_addr = addr; /* saving where the barrier jump is */
  103 + pr_debug("%s: addr:0x%x, rec->ip: 0x%x, imm:0x%x\n",
  104 + __func__, (unsigned int)addr, (unsigned int)rec->ip, imm);
  105 + ret = ftrace_modify_code(rec->ip, imm);
  106 +#ifdef USE_FTRACE_NOP
  107 + pr_debug("%s: bralid:0x%x\n", __func__, bralid);
  108 + ret += ftrace_modify_code(rec->ip + 4, bralid);
  109 +#endif /* USE_FTRACE_NOP */
  110 + return ret;
  111 +}
  112 +
  113 +int __init ftrace_dyn_arch_init(void *data)
  114 +{
  115 + /* The return code is retured via data */
  116 + *(unsigned long *)data = 0;
  117 +
  118 + return 0;
  119 +}
  120 +
  121 +int ftrace_update_ftrace_func(ftrace_func_t func)
  122 +{
  123 + unsigned long ip = (unsigned long)(&ftrace_call);
  124 + unsigned int upper = (unsigned int)func;
  125 + unsigned int lower = (unsigned int)func;
  126 + int ret = 0;
  127 +
  128 + /* create proper saving to ftrace_call poll */
  129 + upper = 0xb0000000 + (upper >> 16); /* imm func_upper */
  130 + lower = 0x32800000 + (lower & 0xFFFF); /* addik r20, r0, func_lower */
  131 +
  132 + pr_debug("%s: func=0x%x, ip=0x%x, upper=0x%x, lower=0x%x\n",
  133 + __func__, (unsigned int)func, (unsigned int)ip, upper, lower);
  134 +
  135 + /* save upper and lower code */
  136 + ret = ftrace_modify_code(ip, upper);
  137 + ret += ftrace_modify_code(ip + 4, lower);
  138 +
  139 + /* We just need to remove the rtsd r15, 8 by NOP */
  140 + BUG_ON(!ret_addr);
  141 + if (ret_addr)
  142 + ret += ftrace_modify_code(ret_addr, MICROBLAZE_NOP);
  143 + else
  144 + ret = 1; /* fault */
  145 +
  146 + /* All changes are done - lets do caches consistent */
  147 + flush_icache();
  148 + return ret;
  149 +}
  150 +
  151 +#endif /* CONFIG_DYNAMIC_FTRACE */
arch/microblaze/kernel/mcount.S
... ... @@ -83,6 +83,12 @@
83 83 nop;
84 84  
85 85 ENTRY(_mcount)
  86 +#ifdef CONFIG_DYNAMIC_FTRACE
  87 +ENTRY(ftrace_caller)
  88 + /* MS: It is just barrier which is removed from C code */
  89 + rtsd r15, 8
  90 + nop
  91 +#endif /* CONFIG_DYNAMIC_FTRACE */
86 92 SAVE_REGS
87 93 swi r15, r1, 0;
88 94 /* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST begin of checking */
89 95  
... ... @@ -90,12 +96,19 @@
90 96 bneid r5, end;
91 97 nop;
92 98 /* MS: HAVE_FUNCTION_TRACE_MCOUNT_TEST end of checking */
  99 +#ifndef CONFIG_DYNAMIC_FTRACE
93 100 /* MS: test function trace if is taken or not */
94 101 lwi r20, r0, ftrace_trace_function;
95 102 addik r6, r0, ftrace_stub;
96 103 cmpu r5, r20, r6; /* ftrace_trace_function != ftrace_stub */
97 104 beqid r5, end; /* MS: not taken -> jump over */
98 105 nop;
  106 +#else /* CONFIG_DYNAMIC_FTRACE */
  107 +NOALIGN_ENTRY(ftrace_call)
  108 +/* instruction for setup imm FUNC_part1, addik r20, r0, FUNC_part2 */
  109 + nop
  110 + nop
  111 +#endif /* CONFIG_DYNAMIC_FTRACE */
99 112 /* static normal trace */
100 113 lwi r6, r1, 120; /* MS: load parent addr */
101 114 addik r5, r15, 0; /* MS: load current function addr */
scripts/recordmcount.pl
... ... @@ -295,6 +295,9 @@
295 295 $ld .= " -m elf64_sparc";
296 296 $cc .= " -m64";
297 297 $objcopy .= " -O elf64-sparc";
  298 +} elsif ($arch eq "microblaze") {
  299 + # Microblaze calls '_mcount' instead of plain 'mcount'.
  300 + $mcount_regex = "^\\s*([0-9a-fA-F]+):.*\\s_mcount\$";
298 301 } else {
299 302 die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
300 303 }