Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6f893fb2 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branches 'tracing/branch-tracer', 'tracing/fastboot', 'tracing/ftrace',...

Merge branches 'tracing/branch-tracer', 'tracing/fastboot', 'tracing/ftrace', 'tracing/function-return-tracer', 'tracing/power-tracer', 'tracing/powerpc', 'tracing/ring-buffer', 'tracing/stack-tracer' and 'tracing/urgent' into tracing/core
Loading
+15 −1
Original line number Original line Diff line number Diff line
@@ -324,7 +324,7 @@ output. To see what is available, simply cat the file:


  cat /debug/tracing/trace_options
  cat /debug/tracing/trace_options
  print-parent nosym-offset nosym-addr noverbose noraw nohex nobin \
  print-parent nosym-offset nosym-addr noverbose noraw nohex nobin \
 noblock nostacktrace nosched-tree
 noblock nostacktrace nosched-tree nouserstacktrace nosym-userobj


To disable one of the options, echo in the option prepended with "no".
To disable one of the options, echo in the option prepended with "no".


@@ -378,6 +378,20 @@ Here are the available options:
		When a trace is recorded, so is the stack of functions.
		When a trace is recorded, so is the stack of functions.
		This allows for back traces of trace sites.
		This allows for back traces of trace sites.


  userstacktrace - This option changes the trace.
		   It records a stacktrace of the current userspace thread.

  sym-userobj - when user stacktrace are enabled, look up which object the
		address belongs to, and print a relative address
		This is especially useful when ASLR is on, otherwise you don't
		get a chance to resolve the address to object/file/line after the app is no
		longer running

		The lookup is performed when you read trace,trace_pipe,latency_trace. Example:

		a.out-1623  [000] 40874.465068: /root/a.out[+0x480] <-/root/a.out[+0
x494] <- /root/a.out[+0x4a8] <- /lib/libc-2.7.so[+0x1e1a6]

  sched-tree - TBD (any users??)
  sched-tree - TBD (any users??)




+4 −2
Original line number Original line Diff line number Diff line
@@ -37,7 +37,7 @@ $ echo mmiotrace > /debug/tracing/current_tracer
$ cat /debug/tracing/trace_pipe > mydump.txt &
$ cat /debug/tracing/trace_pipe > mydump.txt &
Start X or whatever.
Start X or whatever.
$ echo "X is up" > /debug/tracing/trace_marker
$ echo "X is up" > /debug/tracing/trace_marker
$ echo none > /debug/tracing/current_tracer
$ echo nop > /debug/tracing/current_tracer
Check for lost events.
Check for lost events.




@@ -66,7 +66,7 @@ which action. It is recommended to place descriptive markers about what you
do.
do.


Shut down mmiotrace (requires root privileges):
Shut down mmiotrace (requires root privileges):
$ echo none > /debug/tracing/current_tracer
$ echo nop > /debug/tracing/current_tracer
The 'cat' process exits. If it does not, kill it by issuing 'fg' command and
The 'cat' process exits. If it does not, kill it by issuing 'fg' command and
pressing ctrl+c.
pressing ctrl+c.


@@ -81,7 +81,9 @@ are:
$ cat /debug/tracing/trace_entries
$ cat /debug/tracing/trace_entries
gives you a number. Approximately double this number and write it back, for
gives you a number. Approximately double this number and write it back, for
instance:
instance:
$ echo 0 > /debug/tracing/tracing_enabled
$ echo 128000 > /debug/tracing/trace_entries
$ echo 128000 > /debug/tracing/trace_entries
$ echo 1 > /debug/tracing/tracing_enabled
Then start again from the top.
Then start again from the top.


If you are doing a trace for a driver project, e.g. Nouveau, you should also
If you are doing a trace for a driver project, e.g. Nouveau, you should also
+13 −1
Original line number Original line Diff line number Diff line
@@ -7,7 +7,19 @@


#ifndef __ASSEMBLY__
#ifndef __ASSEMBLY__
extern void _mcount(void);
extern void _mcount(void);
#endif

#ifdef CONFIG_DYNAMIC_FTRACE
static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
       /* reloction of mcount call site is the same as the address */
       return addr;
}

struct dyn_arch_ftrace {
	struct module *mod;
};
#endif /*  CONFIG_DYNAMIC_FTRACE */
#endif /* __ASSEMBLY__ */


#endif
#endif


+15 −1
Original line number Original line Diff line number Diff line
@@ -34,11 +34,19 @@ struct mod_arch_specific {
#ifdef __powerpc64__
#ifdef __powerpc64__
	unsigned int stubs_section;	/* Index of stubs section in module */
	unsigned int stubs_section;	/* Index of stubs section in module */
	unsigned int toc_section;	/* What section is the TOC? */
	unsigned int toc_section;	/* What section is the TOC? */
#else
#ifdef CONFIG_DYNAMIC_FTRACE
	unsigned long toc;
	unsigned long tramp;
#endif

#else /* powerpc64 */
	/* Indices of PLT sections within module. */
	/* Indices of PLT sections within module. */
	unsigned int core_plt_section;
	unsigned int core_plt_section;
	unsigned int init_plt_section;
	unsigned int init_plt_section;
#ifdef CONFIG_DYNAMIC_FTRACE
	unsigned long tramp;
#endif
#endif
#endif /* powerpc64 */


	/* List of BUG addresses, source line numbers and filenames */
	/* List of BUG addresses, source line numbers and filenames */
	struct list_head bug_list;
	struct list_head bug_list;
@@ -68,6 +76,12 @@ struct mod_arch_specific {
#    endif	/* MODULE */
#    endif	/* MODULE */
#endif
#endif


#ifdef CONFIG_DYNAMIC_FTRACE
#    ifdef MODULE
	asm(".section .ftrace.tramp,\"ax\",@nobits; .align 3; .previous");
#    endif	/* MODULE */
#endif



struct exception_table_entry;
struct exception_table_entry;
void sort_ex_table(struct exception_table_entry *start,
void sort_ex_table(struct exception_table_entry *start,
+433 −40
Original line number Original line Diff line number Diff line
@@ -9,22 +9,30 @@


#include <linux/spinlock.h>
#include <linux/spinlock.h>
#include <linux/hardirq.h>
#include <linux/hardirq.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/ftrace.h>
#include <linux/ftrace.h>
#include <linux/percpu.h>
#include <linux/percpu.h>
#include <linux/init.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/list.h>


#include <asm/cacheflush.h>
#include <asm/cacheflush.h>
#include <asm/code-patching.h>
#include <asm/ftrace.h>
#include <asm/ftrace.h>


#if 0
#define DEBUGP printk
#else
#define DEBUGP(fmt , ...)	do { } while (0)
#endif


static unsigned int ftrace_nop = 0x60000000;
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
/* PowerPC64's functions are data that points to the functions */
/* PowerPC64's functions are data that points to the functions */
# define GET_ADDR(addr) *(unsigned long *)addr
# define GET_ADDR(addr) (*(unsigned long *)addr)
#endif
#endif




@@ -33,12 +41,12 @@ static unsigned int ftrace_calc_offset(long ip, long addr)
	return (int)(addr - ip);
	return (int)(addr - ip);
}
}


unsigned char *ftrace_nop_replace(void)
static unsigned char *ftrace_nop_replace(void)
{
{
	return (char *)&ftrace_nop;
	return (char *)&ftrace_nop;
}
}


unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
static unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
{
{
	static unsigned int op;
	static unsigned int op;


@@ -68,49 +76,434 @@ unsigned char *ftrace_call_replace(unsigned long ip, unsigned long addr)
# define _ASM_PTR	" .long "
# define _ASM_PTR	" .long "
#endif
#endif


int
static int
ftrace_modify_code(unsigned long ip, unsigned char *old_code,
ftrace_modify_code(unsigned long ip, unsigned char *old_code,
		   unsigned char *new_code)
		   unsigned char *new_code)
{
{
	unsigned replaced;
	unsigned char replaced[MCOUNT_INSN_SIZE];
	unsigned old = *(unsigned *)old_code;
	unsigned new = *(unsigned *)new_code;
	int faulted = 0;


	/*
	/*
	 * Note: Due to modules and __init, code can
	 * Note: Due to modules and __init, code can
	 *  disappear and change, we need to protect against faulting
	 *  disappear and change, we need to protect against faulting
	 *  as well as code changing.
	 *  as well as code changing. We do this by using the
	 *  probe_kernel_* functions.
	 *
	 *
	 * No real locking needed, this code is run through
	 * No real locking needed, this code is run through
	 * kstop_machine.
	 * kstop_machine, or before SMP starts.
	 */
	 */
	asm volatile (

		"1: lwz		%1, 0(%2)\n"
	/* read the text we want to modify */
		"   cmpw	%1, %5\n"
	if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
		"   bne		2f\n"
		return -EFAULT;
		"   stwu	%3, 0(%2)\n"

		"2:\n"
	/* Make sure it is what we expect it to be */
		".section .fixup, \"ax\"\n"
	if (memcmp(replaced, old_code, MCOUNT_INSN_SIZE) != 0)
		"3:	li %0, 1\n"
		return -EINVAL;
		"	b 2b\n"

		".previous\n"
	/* replace the text with the new text */
		".section __ex_table,\"a\"\n"
	if (probe_kernel_write((void *)ip, new_code, MCOUNT_INSN_SIZE))
		_ASM_ALIGN "\n"
		return -EPERM;
		_ASM_PTR "1b, 3b\n"

		".previous"
		: "=r"(faulted), "=r"(replaced)
		: "r"(ip), "r"(new),
		  "0"(faulted), "r"(old)
		: "memory");

	if (replaced != old && replaced != new)
		faulted = 2;

	if (!faulted)
	flush_icache_range(ip, ip + 8);
	flush_icache_range(ip, ip + 8);


	return faulted;
	return 0;
}

/*
 * Helper functions that are the same for both PPC64 and PPC32.
 */
static int test_24bit_addr(unsigned long ip, unsigned long addr)
{
	long diff;

	/*
	 * Can we get to addr from ip in 24 bits?
	 *  (26 really, since we mulitply by 4 for 4 byte alignment)
	 */
	diff = addr - ip;

	/*
	 * Return true if diff is less than 1 << 25
	 *  and greater than -1 << 26.
	 */
	return (diff < (1 << 25)) && (diff > (-1 << 26));
}

static int is_bl_op(unsigned int op)
{
	return (op & 0xfc000003) == 0x48000001;
}

static int test_offset(unsigned long offset)
{
	return (offset + 0x2000000 > 0x3ffffff) || ((offset & 3) != 0);
}

static unsigned long find_bl_target(unsigned long ip, unsigned int op)
{
	static int offset;

	offset = (op & 0x03fffffc);
	/* make it signed */
	if (offset & 0x02000000)
		offset |= 0xfe000000;

	return ip + (long)offset;
}

static unsigned int branch_offset(unsigned long offset)
{
	/* return "bl ip+offset" */
	return 0x48000001 | (offset & 0x03fffffc);
}

#ifdef CONFIG_PPC64
static int
__ftrace_make_nop(struct module *mod,
		  struct dyn_ftrace *rec, unsigned long addr)
{
	unsigned char replaced[MCOUNT_INSN_SIZE * 2];
	unsigned int *op = (unsigned *)&replaced;
	unsigned char jmp[8];
	unsigned long *ptr = (unsigned long *)&jmp;
	unsigned long ip = rec->ip;
	unsigned long tramp;
	int offset;

	/* read where this goes */
	if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
		return -EFAULT;

	/* Make sure that that this is still a 24bit jump */
	if (!is_bl_op(*op)) {
		printk(KERN_ERR "Not expected bl: opcode is %x\n", *op);
		return -EINVAL;
	}

	/* lets find where the pointer goes */
	tramp = find_bl_target(ip, *op);

	/*
	 * On PPC64 the trampoline looks like:
	 * 0x3d, 0x82, 0x00, 0x00,    addis   r12,r2, <high>
	 * 0x39, 0x8c, 0x00, 0x00,    addi    r12,r12, <low>
	 *   Where the bytes 2,3,6 and 7 make up the 32bit offset
	 *   to the TOC that holds the pointer.
	 *   to jump to.
	 * 0xf8, 0x41, 0x00, 0x28,    std     r2,40(r1)
	 * 0xe9, 0x6c, 0x00, 0x20,    ld      r11,32(r12)
	 *   The actually address is 32 bytes from the offset
	 *   into the TOC.
	 * 0xe8, 0x4c, 0x00, 0x28,    ld      r2,40(r12)
	 */

	DEBUGP("ip:%lx jumps to %lx r2: %lx", ip, tramp, mod->arch.toc);

	/* Find where the trampoline jumps to */
	if (probe_kernel_read(jmp, (void *)tramp, 8)) {
		printk(KERN_ERR "Failed to read %lx\n", tramp);
		return -EFAULT;
	}

	DEBUGP(" %08x %08x",
	       (unsigned)(*ptr >> 32),
	       (unsigned)*ptr);

	offset = (unsigned)jmp[2] << 24 |
		(unsigned)jmp[3] << 16 |
		(unsigned)jmp[6] << 8 |
		(unsigned)jmp[7];

	DEBUGP(" %x ", offset);

	/* get the address this jumps too */
	tramp = mod->arch.toc + offset + 32;
	DEBUGP("toc: %lx", tramp);

	if (probe_kernel_read(jmp, (void *)tramp, 8)) {
		printk(KERN_ERR "Failed to read %lx\n", tramp);
		return -EFAULT;
	}

	DEBUGP(" %08x %08x\n",
	       (unsigned)(*ptr >> 32),
	       (unsigned)*ptr);

	/* This should match what was called */
	if (*ptr != GET_ADDR(addr)) {
		printk(KERN_ERR "addr does not match %lx\n", *ptr);
		return -EINVAL;
	}

	/*
	 * We want to nop the line, but the next line is
	 *  0xe8, 0x41, 0x00, 0x28   ld r2,40(r1)
	 * This needs to be turned to a nop too.
	 */
	if (probe_kernel_read(replaced, (void *)(ip+4), MCOUNT_INSN_SIZE))
		return -EFAULT;

	if (*op != 0xe8410028) {
		printk(KERN_ERR "Next line is not ld! (%08x)\n", *op);
		return -EINVAL;
	}

	/*
	 * Milton Miller pointed out that we can not blindly do nops.
	 * If a task was preempted when calling a trace function,
	 * the nops will remove the way to restore the TOC in r2
	 * and the r2 TOC will get corrupted.
	 */

	/*
	 * Replace:
	 *   bl <tramp>  <==== will be replaced with "b 1f"
	 *   ld r2,40(r1)
	 *  1:
	 */
	op[0] = 0x48000008;	/* b +8 */

	if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE))
		return -EPERM;

	return 0;
}

#else /* !PPC64 */
static int
__ftrace_make_nop(struct module *mod,
		  struct dyn_ftrace *rec, unsigned long addr)
{
	unsigned char replaced[MCOUNT_INSN_SIZE];
	unsigned int *op = (unsigned *)&replaced;
	unsigned char jmp[8];
	unsigned int *ptr = (unsigned int *)&jmp;
	unsigned long ip = rec->ip;
	unsigned long tramp;
	int offset;

	if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
		return -EFAULT;

	/* Make sure that that this is still a 24bit jump */
	if (!is_bl_op(*op)) {
		printk(KERN_ERR "Not expected bl: opcode is %x\n", *op);
		return -EINVAL;
	}

	/* lets find where the pointer goes */
	tramp = find_bl_target(ip, *op);

	/*
	 * On PPC32 the trampoline looks like:
	 * lis r11,sym@ha
	 * addi r11,r11,sym@l
	 * mtctr r11
	 * bctr
	 */

	DEBUGP("ip:%lx jumps to %lx", ip, tramp);

	/* Find where the trampoline jumps to */
	if (probe_kernel_read(jmp, (void *)tramp, 8)) {
		printk(KERN_ERR "Failed to read %lx\n", tramp);
		return -EFAULT;
	}

	DEBUGP(" %08x %08x ", ptr[0], ptr[1]);

	tramp = (ptr[1] & 0xffff) |
		((ptr[0] & 0xffff) << 16);
	if (tramp & 0x8000)
		tramp -= 0x10000;

	DEBUGP(" %x ", tramp);

	if (tramp != addr) {
		printk(KERN_ERR
		       "Trampoline location %08lx does not match addr\n",
		       tramp);
		return -EINVAL;
	}

	op[0] = PPC_NOP_INSTR;

	if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE))
		return -EPERM;

	return 0;
}
#endif /* PPC64 */

int ftrace_make_nop(struct module *mod,
		    struct dyn_ftrace *rec, unsigned long addr)
{
	unsigned char *old, *new;
	unsigned long ip = rec->ip;

	/*
	 * If the calling address is more that 24 bits away,
	 * then we had to use a trampoline to make the call.
	 * Otherwise just update the call site.
	 */
	if (test_24bit_addr(ip, addr)) {
		/* within range */
		old = ftrace_call_replace(ip, addr);
		new = ftrace_nop_replace();
		return ftrace_modify_code(ip, old, new);
	}

	/*
	 * Out of range jumps are called from modules.
	 * We should either already have a pointer to the module
	 * or it has been passed in.
	 */
	if (!rec->arch.mod) {
		if (!mod) {
			printk(KERN_ERR "No module loaded addr=%lx\n",
			       addr);
			return -EFAULT;
		}
		rec->arch.mod = mod;
	} else if (mod) {
		if (mod != rec->arch.mod) {
			printk(KERN_ERR
			       "Record mod %p not equal to passed in mod %p\n",
			       rec->arch.mod, mod);
			return -EINVAL;
		}
		/* nothing to do if mod == rec->arch.mod */
	} else
		mod = rec->arch.mod;

	return __ftrace_make_nop(mod, rec, addr);

}

#ifdef CONFIG_PPC64
static int
__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
	unsigned char replaced[MCOUNT_INSN_SIZE * 2];
	unsigned int *op = (unsigned *)&replaced;
	unsigned long ip = rec->ip;
	unsigned long offset;

	/* read where this goes */
	if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE * 2))
		return -EFAULT;

	/*
	 * It should be pointing to two nops or
	 *  b +8; ld r2,40(r1)
	 */
	if (((op[0] != 0x48000008) || (op[1] != 0xe8410028)) &&
	    ((op[0] != PPC_NOP_INSTR) || (op[1] != PPC_NOP_INSTR))) {
		printk(KERN_ERR "Expected NOPs but have %x %x\n", op[0], op[1]);
		return -EINVAL;
	}

	/* If we never set up a trampoline to ftrace_caller, then bail */
	if (!rec->arch.mod->arch.tramp) {
		printk(KERN_ERR "No ftrace trampoline\n");
		return -EINVAL;
	}

	/* now calculate a jump to the ftrace caller trampoline */
	offset = rec->arch.mod->arch.tramp - ip;

	if (test_offset(offset)) {
		printk(KERN_ERR "REL24 %li out of range!\n",
		       (long int)offset);
		return -EINVAL;
	}

	/* Set to "bl addr" */
	op[0] = branch_offset(offset);
	/* ld r2,40(r1) */
	op[1] = 0xe8410028;

	DEBUGP("write to %lx\n", rec->ip);

	if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE * 2))
		return -EPERM;

	return 0;
}
#else
static int
__ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
	unsigned char replaced[MCOUNT_INSN_SIZE];
	unsigned int *op = (unsigned *)&replaced;
	unsigned long ip = rec->ip;
	unsigned long offset;

	/* read where this goes */
	if (probe_kernel_read(replaced, (void *)ip, MCOUNT_INSN_SIZE))
		return -EFAULT;

	/* It should be pointing to a nop */
	if (op[0] != PPC_NOP_INSTR) {
		printk(KERN_ERR "Expected NOP but have %x\n", op[0]);
		return -EINVAL;
	}

	/* If we never set up a trampoline to ftrace_caller, then bail */
	if (!rec->arch.mod->arch.tramp) {
		printk(KERN_ERR "No ftrace trampoline\n");
		return -EINVAL;
	}

	/* now calculate a jump to the ftrace caller trampoline */
	offset = rec->arch.mod->arch.tramp - ip;

	if (test_offset(offset)) {
		printk(KERN_ERR "REL24 %li out of range!\n",
		       (long int)offset);
		return -EINVAL;
	}

	/* Set to "bl addr" */
	op[0] = branch_offset(offset);

	DEBUGP("write to %lx\n", rec->ip);

	if (probe_kernel_write((void *)ip, replaced, MCOUNT_INSN_SIZE))
		return -EPERM;

	return 0;
}
#endif /* CONFIG_PPC64 */

int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
{
	unsigned char *old, *new;
	unsigned long ip = rec->ip;

	/*
	 * If the calling address is more that 24 bits away,
	 * then we had to use a trampoline to make the call.
	 * Otherwise just update the call site.
	 */
	if (test_24bit_addr(ip, addr)) {
		/* within range */
		old = ftrace_nop_replace();
		new = ftrace_call_replace(ip, addr);
		return ftrace_modify_code(ip, old, new);
	}

	/*
	 * Out of range jumps are called from modules.
	 * Being that we are converting from nop, it had better
	 * already have a module defined.
	 */
	if (!rec->arch.mod) {
		printk(KERN_ERR "No module loaded\n");
		return -EINVAL;
	}

	return __ftrace_make_call(rec, addr);
}
}


int ftrace_update_ftrace_func(ftrace_func_t func)
int ftrace_update_ftrace_func(ftrace_func_t func)
@@ -128,10 +521,10 @@ int ftrace_update_ftrace_func(ftrace_func_t func)


int __init ftrace_dyn_arch_init(void *data)
int __init ftrace_dyn_arch_init(void *data)
{
{
	/* This is running in kstop_machine */
	/* caller expects data to be zero */
	unsigned long *p = data;


	ftrace_mcount_set(data);
	*p = 0;


	return 0;
	return 0;
}
}
Loading