mirror of https://gitee.com/openkylin/linux.git
powerpc64: port of the function graph tracer
This is a port of the function graph tracer that was written by Frederic Weisbecker for the x86. This only works for PPC64 at the moment and only for static tracing. PPC32 and dynamic function graph tracing support will come later. The trace produces a visual calling of functions: # tracer: function_graph # # CPU DURATION FUNCTION CALLS # | | | | | | | 0) 2.224 us | } 0) ! 271.024 us | } 0) ! 320.080 us | } 0) ! 324.656 us | } 0) ! 329.136 us | } 0) | .put_prev_task_fair() { 0) | .update_curr() { 0) 2.240 us | .update_min_vruntime(); 0) 6.512 us | } 0) 2.528 us | .__enqueue_entity(); 0) + 15.536 us | } 0) | .pick_next_task_fair() { 0) 2.032 us | .__pick_next_entity(); 0) 2.064 us | .__clear_buddies(); 0) | .set_next_entity() { 0) 2.672 us | .__dequeue_entity(); 0) 6.864 us | } Geoff Lavand tested on PS3. Tested-by: Geoff Levand <geoffrey.levand@am.sony.com> Acked-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Steven Rostedt <srostedt@redhat.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
parent
17be5b3ddf
commit
6794c78243
|
@ -111,6 +111,7 @@ config PPC
|
||||||
select HAVE_FTRACE_MCOUNT_RECORD
|
select HAVE_FTRACE_MCOUNT_RECORD
|
||||||
select HAVE_DYNAMIC_FTRACE
|
select HAVE_DYNAMIC_FTRACE
|
||||||
select HAVE_FUNCTION_TRACER
|
select HAVE_FUNCTION_TRACER
|
||||||
|
select HAVE_FUNCTION_GRAPH_TRACER if !DYNAMIC_FTRACE && PPC64
|
||||||
select ARCH_WANT_OPTIONAL_GPIOLIB
|
select ARCH_WANT_OPTIONAL_GPIOLIB
|
||||||
select HAVE_IDE
|
select HAVE_IDE
|
||||||
select HAVE_IOREMAP_PROT
|
select HAVE_IOREMAP_PROT
|
||||||
|
|
|
@ -18,12 +18,10 @@ CFLAGS_REMOVE_cputable.o = -pg -mno-sched-epilog
|
||||||
CFLAGS_REMOVE_prom_init.o = -pg -mno-sched-epilog
|
CFLAGS_REMOVE_prom_init.o = -pg -mno-sched-epilog
|
||||||
CFLAGS_REMOVE_btext.o = -pg -mno-sched-epilog
|
CFLAGS_REMOVE_btext.o = -pg -mno-sched-epilog
|
||||||
CFLAGS_REMOVE_prom.o = -pg -mno-sched-epilog
|
CFLAGS_REMOVE_prom.o = -pg -mno-sched-epilog
|
||||||
|
# do not trace tracer code
|
||||||
ifdef CONFIG_DYNAMIC_FTRACE
|
|
||||||
# dynamic ftrace setup.
|
|
||||||
CFLAGS_REMOVE_ftrace.o = -pg -mno-sched-epilog
|
CFLAGS_REMOVE_ftrace.o = -pg -mno-sched-epilog
|
||||||
endif
|
# timers used by tracing
|
||||||
|
CFLAGS_REMOVE_time.o = -pg -mno-sched-epilog
|
||||||
endif
|
endif
|
||||||
|
|
||||||
obj-y := cputable.o ptrace.o syscalls.o \
|
obj-y := cputable.o ptrace.o syscalls.o \
|
||||||
|
@ -95,6 +93,7 @@ obj-$(CONFIG_AUDIT) += audit.o
|
||||||
obj64-$(CONFIG_AUDIT) += compat_audit.o
|
obj64-$(CONFIG_AUDIT) += compat_audit.o
|
||||||
|
|
||||||
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
|
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
|
||||||
|
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
|
||||||
|
|
||||||
obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o
|
obj-$(CONFIG_8XX_MINIMAL_FPEMU) += softemu8xx.o
|
||||||
|
|
||||||
|
|
|
@ -931,13 +931,65 @@ _GLOBAL(_mcount)
|
||||||
ld r5,0(r5)
|
ld r5,0(r5)
|
||||||
mtctr r5
|
mtctr r5
|
||||||
bctrl
|
bctrl
|
||||||
|
|
||||||
nop
|
nop
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
b ftrace_graph_caller
|
||||||
|
#endif
|
||||||
ld r0, 128(r1)
|
ld r0, 128(r1)
|
||||||
mtlr r0
|
mtlr r0
|
||||||
addi r1, r1, 112
|
addi r1, r1, 112
|
||||||
_GLOBAL(ftrace_stub)
|
_GLOBAL(ftrace_stub)
|
||||||
blr
|
blr
|
||||||
|
|
||||||
#endif
|
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||||
#endif
|
|
||||||
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
ftrace_graph_caller:
|
||||||
|
/* load r4 with local address */
|
||||||
|
ld r4, 128(r1)
|
||||||
|
subi r4, r4, MCOUNT_INSN_SIZE
|
||||||
|
|
||||||
|
/* get the parent address */
|
||||||
|
ld r11, 112(r1)
|
||||||
|
addi r3, r11, 16
|
||||||
|
|
||||||
|
bl .prepare_ftrace_return
|
||||||
|
nop
|
||||||
|
|
||||||
|
ld r0, 128(r1)
|
||||||
|
mtlr r0
|
||||||
|
addi r1, r1, 112
|
||||||
|
blr
|
||||||
|
|
||||||
|
_GLOBAL(return_to_handler)
|
||||||
|
/* need to save return values */
|
||||||
|
std r4, -32(r1)
|
||||||
|
std r3, -24(r1)
|
||||||
|
/* save TOC */
|
||||||
|
std r2, -16(r1)
|
||||||
|
std r31, -8(r1)
|
||||||
|
mr r31, r1
|
||||||
|
stdu r1, -112(r1)
|
||||||
|
|
||||||
|
/* update the TOC */
|
||||||
|
LOAD_REG_IMMEDIATE(r4,ftrace_return_to_handler)
|
||||||
|
ld r2, 8(r4)
|
||||||
|
|
||||||
|
bl .ftrace_return_to_handler
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* return value has real return address */
|
||||||
|
mtlr r3
|
||||||
|
|
||||||
|
ld r1, 0(r1)
|
||||||
|
ld r4, -32(r1)
|
||||||
|
ld r3, -24(r1)
|
||||||
|
ld r2, -16(r1)
|
||||||
|
ld r31, -8(r1)
|
||||||
|
|
||||||
|
/* Jump back to real return address */
|
||||||
|
blr
|
||||||
|
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||||
|
#endif /* CONFIG_FUNCTION_TRACER */
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
*
|
*
|
||||||
* Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
|
* Thanks goes out to P.A. Semi, Inc for supplying me with a PPC64 box.
|
||||||
*
|
*
|
||||||
|
* Added function graph tracer code, taken from x86 that was written
|
||||||
|
* by Frederic Weisbecker, and ported to PPC by Steven Rostedt.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
@ -20,8 +23,6 @@
|
||||||
#include <asm/code-patching.h>
|
#include <asm/code-patching.h>
|
||||||
#include <asm/ftrace.h>
|
#include <asm/ftrace.h>
|
||||||
|
|
||||||
static unsigned int ftrace_nop = PPC_NOP_INSTR;
|
|
||||||
|
|
||||||
#ifdef CONFIG_PPC32
|
#ifdef CONFIG_PPC32
|
||||||
# define GET_ADDR(addr) addr
|
# define GET_ADDR(addr) addr
|
||||||
#else
|
#else
|
||||||
|
@ -29,6 +30,8 @@ static unsigned int ftrace_nop = PPC_NOP_INSTR;
|
||||||
# define GET_ADDR(addr) (*(unsigned long *)addr)
|
# define GET_ADDR(addr) (*(unsigned long *)addr)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||||
|
static unsigned int ftrace_nop = PPC_NOP_INSTR;
|
||||||
|
|
||||||
static unsigned int ftrace_calc_offset(long ip, long addr)
|
static unsigned int ftrace_calc_offset(long ip, long addr)
|
||||||
{
|
{
|
||||||
|
@ -525,3 +528,75 @@ int __init ftrace_dyn_arch_init(void *data)
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif /* CONFIG_DYNAMIC_FTRACE */
|
||||||
|
|
||||||
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Hook the return address and push it in the stack of return addrs
|
||||||
|
* in current thread info.
|
||||||
|
*/
|
||||||
|
void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
|
||||||
|
{
|
||||||
|
unsigned long old;
|
||||||
|
unsigned long long calltime;
|
||||||
|
int faulted;
|
||||||
|
struct ftrace_graph_ent trace;
|
||||||
|
unsigned long return_hooker = (unsigned long)
|
||||||
|
&return_to_handler;
|
||||||
|
|
||||||
|
if (unlikely(atomic_read(¤t->tracing_graph_pause)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
return_hooker = GET_ADDR(return_hooker);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Protect against fault, even if it shouldn't
|
||||||
|
* happen. This tool is too much intrusive to
|
||||||
|
* ignore such a protection.
|
||||||
|
*/
|
||||||
|
asm volatile(
|
||||||
|
"1: " PPC_LL "%[old], 0(%[parent])\n"
|
||||||
|
"2: " PPC_STL "%[return_hooker], 0(%[parent])\n"
|
||||||
|
" li %[faulted], 0\n"
|
||||||
|
"3:"
|
||||||
|
|
||||||
|
".section .fixup, \"ax\"\n"
|
||||||
|
"4: li %[faulted], 1\n"
|
||||||
|
" b 3b\n"
|
||||||
|
".previous\n"
|
||||||
|
|
||||||
|
".section __ex_table,\"a\"\n"
|
||||||
|
PPC_LONG_ALIGN "\n"
|
||||||
|
PPC_LONG "1b,4b\n"
|
||||||
|
PPC_LONG "2b,4b\n"
|
||||||
|
".previous"
|
||||||
|
|
||||||
|
: [old] "=r" (old), [faulted] "=r" (faulted)
|
||||||
|
: [parent] "r" (parent), [return_hooker] "r" (return_hooker)
|
||||||
|
: "memory"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (unlikely(faulted)) {
|
||||||
|
ftrace_graph_stop();
|
||||||
|
WARN_ON(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
calltime = cpu_clock(raw_smp_processor_id());
|
||||||
|
|
||||||
|
if (ftrace_push_return_trace(old, calltime,
|
||||||
|
self_addr, &trace.depth) == -EBUSY) {
|
||||||
|
*parent = old;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace.func = self_addr;
|
||||||
|
|
||||||
|
/* Only trace if the calling function expects to */
|
||||||
|
if (!ftrace_graph_entry(&trace)) {
|
||||||
|
current->curr_ret_stack--;
|
||||||
|
*parent = old;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include <linux/mqueue.h>
|
#include <linux/mqueue.h>
|
||||||
#include <linux/hardirq.h>
|
#include <linux/hardirq.h>
|
||||||
#include <linux/utsname.h>
|
#include <linux/utsname.h>
|
||||||
|
#include <linux/ftrace.h>
|
||||||
#include <linux/kernel_stat.h>
|
#include <linux/kernel_stat.h>
|
||||||
|
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
|
@ -1008,6 +1009,14 @@ void show_stack(struct task_struct *tsk, unsigned long *stack)
|
||||||
unsigned long sp, ip, lr, newsp;
|
unsigned long sp, ip, lr, newsp;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
int firstframe = 1;
|
int firstframe = 1;
|
||||||
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
int curr_frame = current->curr_ret_stack;
|
||||||
|
extern void return_to_handler(void);
|
||||||
|
unsigned long addr = (unsigned long)return_to_handler;
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
addr = *(unsigned long*)addr;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
sp = (unsigned long) stack;
|
sp = (unsigned long) stack;
|
||||||
if (tsk == NULL)
|
if (tsk == NULL)
|
||||||
|
@ -1030,6 +1039,13 @@ void show_stack(struct task_struct *tsk, unsigned long *stack)
|
||||||
ip = stack[STACK_FRAME_LR_SAVE];
|
ip = stack[STACK_FRAME_LR_SAVE];
|
||||||
if (!firstframe || ip != lr) {
|
if (!firstframe || ip != lr) {
|
||||||
printk("["REG"] ["REG"] %pS", sp, ip, (void *)ip);
|
printk("["REG"] ["REG"] %pS", sp, ip, (void *)ip);
|
||||||
|
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||||
|
if (ip == addr && curr_frame >= 0) {
|
||||||
|
printk(" (%pS)",
|
||||||
|
(void *)current->ret_stack[curr_frame].ret);
|
||||||
|
curr_frame--;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (firstframe)
|
if (firstframe)
|
||||||
printk(" (unreliable)");
|
printk(" (unreliable)");
|
||||||
printk("\n");
|
printk("\n");
|
||||||
|
|
|
@ -58,6 +58,7 @@ SECTIONS
|
||||||
SCHED_TEXT
|
SCHED_TEXT
|
||||||
LOCK_TEXT
|
LOCK_TEXT
|
||||||
KPROBES_TEXT
|
KPROBES_TEXT
|
||||||
|
IRQENTRY_TEXT
|
||||||
|
|
||||||
#ifdef CONFIG_PPC32
|
#ifdef CONFIG_PPC32
|
||||||
*(.got1)
|
*(.got1)
|
||||||
|
|
Loading…
Reference in New Issue