diff options
| -rw-r--r-- | arch/powerpc/kernel/ftrace.c | 2 | ||||
| -rw-r--r-- | arch/s390/kernel/ftrace.c | 2 | ||||
| -rw-r--r-- | arch/x86/Kconfig | 1 | ||||
| -rw-r--r-- | arch/x86/kernel/entry_32.S | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/entry_64.S | 2 | ||||
| -rw-r--r-- | arch/x86/kernel/ftrace.c | 6 | ||||
| -rw-r--r-- | include/linux/ftrace.h | 4 | ||||
| -rw-r--r-- | kernel/trace/Kconfig | 7 | ||||
| -rw-r--r-- | kernel/trace/trace_functions_graph.c | 36 |
9 files changed, 53 insertions, 9 deletions
diff --git a/arch/powerpc/kernel/ftrace.c b/arch/powerpc/kernel/ftrace.c index 2d182f119d1d..58d6a6109040 100644 --- a/arch/powerpc/kernel/ftrace.c +++ b/arch/powerpc/kernel/ftrace.c | |||
| @@ -605,7 +605,7 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | |||
| 605 | return; | 605 | return; |
| 606 | } | 606 | } |
| 607 | 607 | ||
| 608 | if (ftrace_push_return_trace(old, self_addr, &trace.depth) == -EBUSY) { | 608 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, 0) == -EBUSY) { |
| 609 | *parent = old; | 609 | *parent = old; |
| 610 | return; | 610 | return; |
| 611 | } | 611 | } |
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c index 82ddfd3a75af..3e298e64f0db 100644 --- a/arch/s390/kernel/ftrace.c +++ b/arch/s390/kernel/ftrace.c | |||
| @@ -190,7 +190,7 @@ unsigned long prepare_ftrace_return(unsigned long ip, unsigned long parent) | |||
| 190 | goto out; | 190 | goto out; |
| 191 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) | 191 | if (unlikely(atomic_read(¤t->tracing_graph_pause))) |
| 192 | goto out; | 192 | goto out; |
| 193 | if (ftrace_push_return_trace(parent, ip, &trace.depth) == -EBUSY) | 193 | if (ftrace_push_return_trace(parent, ip, &trace.depth, 0) == -EBUSY) |
| 194 | goto out; | 194 | goto out; |
| 195 | trace.func = ftrace_mcount_call_adjust(ip) & PSW_ADDR_INSN; | 195 | trace.func = ftrace_mcount_call_adjust(ip) & PSW_ADDR_INSN; |
| 196 | /* Only trace if the calling function expects to. */ | 196 | /* Only trace if the calling function expects to. */ |
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 356d2ec8e2fb..fcf12af07427 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
| @@ -33,6 +33,7 @@ config X86 | |||
| 33 | select HAVE_DYNAMIC_FTRACE | 33 | select HAVE_DYNAMIC_FTRACE |
| 34 | select HAVE_FUNCTION_TRACER | 34 | select HAVE_FUNCTION_TRACER |
| 35 | select HAVE_FUNCTION_GRAPH_TRACER | 35 | select HAVE_FUNCTION_GRAPH_TRACER |
| 36 | select HAVE_FUNCTION_GRAPH_FP_TEST | ||
| 36 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST | 37 | select HAVE_FUNCTION_TRACE_MCOUNT_TEST |
| 37 | select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE | 38 | select HAVE_FTRACE_NMI_ENTER if DYNAMIC_FTRACE |
| 38 | select HAVE_FTRACE_SYSCALLS | 39 | select HAVE_FTRACE_SYSCALLS |
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S index c929add475c9..0d4b28564c14 100644 --- a/arch/x86/kernel/entry_32.S +++ b/arch/x86/kernel/entry_32.S | |||
| @@ -1154,6 +1154,7 @@ ENTRY(ftrace_graph_caller) | |||
| 1154 | pushl %edx | 1154 | pushl %edx |
| 1155 | movl 0xc(%esp), %edx | 1155 | movl 0xc(%esp), %edx |
| 1156 | lea 0x4(%ebp), %eax | 1156 | lea 0x4(%ebp), %eax |
| 1157 | movl (%ebp), %ecx | ||
| 1157 | subl $MCOUNT_INSN_SIZE, %edx | 1158 | subl $MCOUNT_INSN_SIZE, %edx |
| 1158 | call prepare_ftrace_return | 1159 | call prepare_ftrace_return |
| 1159 | popl %edx | 1160 | popl %edx |
| @@ -1168,6 +1169,7 @@ return_to_handler: | |||
| 1168 | pushl %eax | 1169 | pushl %eax |
| 1169 | pushl %ecx | 1170 | pushl %ecx |
| 1170 | pushl %edx | 1171 | pushl %edx |
| 1172 | movl %ebp, %eax | ||
| 1171 | call ftrace_return_to_handler | 1173 | call ftrace_return_to_handler |
| 1172 | movl %eax, 0xc(%esp) | 1174 | movl %eax, 0xc(%esp) |
| 1173 | popl %edx | 1175 | popl %edx |
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index de74f0a3e0ed..c251be745107 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S | |||
| @@ -135,6 +135,7 @@ ENTRY(ftrace_graph_caller) | |||
| 135 | 135 | ||
| 136 | leaq 8(%rbp), %rdi | 136 | leaq 8(%rbp), %rdi |
| 137 | movq 0x38(%rsp), %rsi | 137 | movq 0x38(%rsp), %rsi |
| 138 | movq (%rbp), %rdx | ||
| 138 | subq $MCOUNT_INSN_SIZE, %rsi | 139 | subq $MCOUNT_INSN_SIZE, %rsi |
| 139 | 140 | ||
| 140 | call prepare_ftrace_return | 141 | call prepare_ftrace_return |
| @@ -150,6 +151,7 @@ GLOBAL(return_to_handler) | |||
| 150 | /* Save the return values */ | 151 | /* Save the return values */ |
| 151 | movq %rax, (%rsp) | 152 | movq %rax, (%rsp) |
| 152 | movq %rdx, 8(%rsp) | 153 | movq %rdx, 8(%rsp) |
| 154 | movq %rbp, %rdi | ||
| 153 | 155 | ||
| 154 | call ftrace_return_to_handler | 156 | call ftrace_return_to_handler |
| 155 | 157 | ||
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c index b79c5533c421..d94e1ea3b9fe 100644 --- a/arch/x86/kernel/ftrace.c +++ b/arch/x86/kernel/ftrace.c | |||
| @@ -408,7 +408,8 @@ int ftrace_disable_ftrace_graph_caller(void) | |||
| 408 | * Hook the return address and push it in the stack of return addrs | 408 | * Hook the return address and push it in the stack of return addrs |
| 409 | * in current thread info. | 409 | * in current thread info. |
| 410 | */ | 410 | */ |
| 411 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | 411 | void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr, |
| 412 | unsigned long frame_pointer) | ||
| 412 | { | 413 | { |
| 413 | unsigned long old; | 414 | unsigned long old; |
| 414 | int faulted; | 415 | int faulted; |
| @@ -453,7 +454,8 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) | |||
| 453 | return; | 454 | return; |
| 454 | } | 455 | } |
| 455 | 456 | ||
| 456 | if (ftrace_push_return_trace(old, self_addr, &trace.depth) == -EBUSY) { | 457 | if (ftrace_push_return_trace(old, self_addr, &trace.depth, |
| 458 | frame_pointer) == -EBUSY) { | ||
| 457 | *parent = old; | 459 | *parent = old; |
| 458 | return; | 460 | return; |
| 459 | } | 461 | } |
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 39b95c56587e..dc3b1328aaeb 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -362,6 +362,7 @@ struct ftrace_ret_stack { | |||
| 362 | unsigned long func; | 362 | unsigned long func; |
| 363 | unsigned long long calltime; | 363 | unsigned long long calltime; |
| 364 | unsigned long long subtime; | 364 | unsigned long long subtime; |
| 365 | unsigned long fp; | ||
| 365 | }; | 366 | }; |
| 366 | 367 | ||
| 367 | /* | 368 | /* |
| @@ -372,7 +373,8 @@ struct ftrace_ret_stack { | |||
| 372 | extern void return_to_handler(void); | 373 | extern void return_to_handler(void); |
| 373 | 374 | ||
| 374 | extern int | 375 | extern int |
| 375 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth); | 376 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, |
| 377 | unsigned long frame_pointer); | ||
| 376 | 378 | ||
| 377 | /* | 379 | /* |
| 378 | * Sometimes we don't want to trace a function with the function | 380 | * Sometimes we don't want to trace a function with the function |
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 1eac85253ce9..b17ed8787ded 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig | |||
| @@ -18,6 +18,13 @@ config HAVE_FUNCTION_TRACER | |||
| 18 | config HAVE_FUNCTION_GRAPH_TRACER | 18 | config HAVE_FUNCTION_GRAPH_TRACER |
| 19 | bool | 19 | bool |
| 20 | 20 | ||
| 21 | config HAVE_FUNCTION_GRAPH_FP_TEST | ||
| 22 | bool | ||
| 23 | help | ||
| 24 | An arch may pass in a unique value (frame pointer) to both the | ||
| 25 | entering and exiting of a function. On exit, the value is compared | ||
| 26 | and if it does not match, then it will panic the kernel. | ||
| 27 | |||
| 21 | config HAVE_FUNCTION_TRACE_MCOUNT_TEST | 28 | config HAVE_FUNCTION_TRACE_MCOUNT_TEST |
| 22 | bool | 29 | bool |
| 23 | help | 30 | help |
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 8b592418d8b2..d2249abafb53 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c | |||
| @@ -57,7 +57,8 @@ static struct tracer_flags tracer_flags = { | |||
| 57 | 57 | ||
| 58 | /* Add a function return address to the trace stack on thread info.*/ | 58 | /* Add a function return address to the trace stack on thread info.*/ |
| 59 | int | 59 | int |
| 60 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth) | 60 | ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth, |
| 61 | unsigned long frame_pointer) | ||
| 61 | { | 62 | { |
| 62 | unsigned long long calltime; | 63 | unsigned long long calltime; |
| 63 | int index; | 64 | int index; |
| @@ -85,6 +86,7 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth) | |||
| 85 | current->ret_stack[index].func = func; | 86 | current->ret_stack[index].func = func; |
| 86 | current->ret_stack[index].calltime = calltime; | 87 | current->ret_stack[index].calltime = calltime; |
| 87 | current->ret_stack[index].subtime = 0; | 88 | current->ret_stack[index].subtime = 0; |
| 89 | current->ret_stack[index].fp = frame_pointer; | ||
| 88 | *depth = index; | 90 | *depth = index; |
| 89 | 91 | ||
| 90 | return 0; | 92 | return 0; |
| @@ -92,7 +94,8 @@ ftrace_push_return_trace(unsigned long ret, unsigned long func, int *depth) | |||
| 92 | 94 | ||
| 93 | /* Retrieve a function return address to the trace stack on thread info.*/ | 95 | /* Retrieve a function return address to the trace stack on thread info.*/ |
| 94 | static void | 96 | static void |
| 95 | ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) | 97 | ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret, |
| 98 | unsigned long frame_pointer) | ||
| 96 | { | 99 | { |
| 97 | int index; | 100 | int index; |
| 98 | 101 | ||
| @@ -106,6 +109,31 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) | |||
| 106 | return; | 109 | return; |
| 107 | } | 110 | } |
| 108 | 111 | ||
| 112 | #ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST | ||
| 113 | /* | ||
| 114 | * The arch may choose to record the frame pointer used | ||
| 115 | * and check it here to make sure that it is what we expect it | ||
| 116 | * to be. If gcc does not set the place holder of the return | ||
| 117 | * address in the frame pointer, and does a copy instead, then | ||
| 118 | * the function graph trace will fail. This test detects this | ||
| 119 | * case. | ||
| 120 | * | ||
| 121 | * Currently, x86_32 with optimize for size (-Os) makes the latest | ||
| 122 | * gcc do the above. | ||
| 123 | */ | ||
| 124 | if (unlikely(current->ret_stack[index].fp != frame_pointer)) { | ||
| 125 | ftrace_graph_stop(); | ||
| 126 | WARN(1, "Bad frame pointer: expected %lx, received %lx\n" | ||
| 127 | " from func %pF return to %lx\n", | ||
| 128 | current->ret_stack[index].fp, | ||
| 129 | frame_pointer, | ||
| 130 | (void *)current->ret_stack[index].func, | ||
| 131 | current->ret_stack[index].ret); | ||
| 132 | *ret = (unsigned long)panic; | ||
| 133 | return; | ||
| 134 | } | ||
| 135 | #endif | ||
| 136 | |||
| 109 | *ret = current->ret_stack[index].ret; | 137 | *ret = current->ret_stack[index].ret; |
| 110 | trace->func = current->ret_stack[index].func; | 138 | trace->func = current->ret_stack[index].func; |
| 111 | trace->calltime = current->ret_stack[index].calltime; | 139 | trace->calltime = current->ret_stack[index].calltime; |
| @@ -117,12 +145,12 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) | |||
| 117 | * Send the trace to the ring-buffer. | 145 | * Send the trace to the ring-buffer. |
| 118 | * @return the original return address. | 146 | * @return the original return address. |
| 119 | */ | 147 | */ |
| 120 | unsigned long ftrace_return_to_handler(void) | 148 | unsigned long ftrace_return_to_handler(unsigned long frame_pointer) |
| 121 | { | 149 | { |
| 122 | struct ftrace_graph_ret trace; | 150 | struct ftrace_graph_ret trace; |
| 123 | unsigned long ret; | 151 | unsigned long ret; |
| 124 | 152 | ||
| 125 | ftrace_pop_return_trace(&trace, &ret); | 153 | ftrace_pop_return_trace(&trace, &ret, frame_pointer); |
| 126 | trace.rettime = trace_clock_local(); | 154 | trace.rettime = trace_clock_local(); |
| 127 | ftrace_graph_return(&trace); | 155 | ftrace_graph_return(&trace); |
| 128 | barrier(); | 156 | barrier(); |
