Commit b29144c3 authored by Ingo Molnar's avatar Ingo Molnar

Merge branches 'tracing/ftrace' and 'tracing/function-graph-tracer' into tracing/core

...@@ -31,7 +31,7 @@ CFLAGS_tsc.o := $(nostackp) ...@@ -31,7 +31,7 @@ CFLAGS_tsc.o := $(nostackp)
obj-y := process_$(BITS).o signal_$(BITS).o entry_$(BITS).o obj-y := process_$(BITS).o signal_$(BITS).o entry_$(BITS).o
obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o obj-y += traps.o irq.o irq_$(BITS).o dumpstack_$(BITS).o
obj-y += time_$(BITS).o ioport.o ldt.o obj-y += time_$(BITS).o ioport.o ldt.o dumpstack.o
obj-y += setup.o i8259.o irqinit_$(BITS).o setup_percpu.o obj-y += setup.o i8259.o irqinit_$(BITS).o setup_percpu.o
obj-$(CONFIG_X86_VISWS) += visws_quirks.o obj-$(CONFIG_X86_VISWS) += visws_quirks.o
obj-$(CONFIG_X86_32) += probe_roms_32.o obj-$(CONFIG_X86_32) += probe_roms_32.o
......
/*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
*/
#include <linux/kallsyms.h>
#include <linux/kprobes.h>
#include <linux/uaccess.h>
#include <linux/utsname.h>
#include <linux/hardirq.h>
#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/kexec.h>
#include <linux/bug.h>
#include <linux/nmi.h>
#include <linux/sysfs.h>
#include <asm/stacktrace.h>
#include "dumpstack.h"
int panic_on_unrecovered_nmi;
unsigned int code_bytes = 64;
int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE;
static int die_counter;
void printk_address(unsigned long address, int reliable)
{
printk(" [<%p>] %s%pS\n", (void *) address,
reliable ? "" : "? ", (void *) address);
}
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static void
print_ftrace_graph_addr(unsigned long addr, void *data,
const struct stacktrace_ops *ops,
struct thread_info *tinfo, int *graph)
{
struct task_struct *task = tinfo->task;
unsigned long ret_addr;
int index = task->curr_ret_stack;
if (addr != (unsigned long)return_to_handler)
return;
if (!task->ret_stack || index < *graph)
return;
index -= *graph;
ret_addr = task->ret_stack[index].ret;
ops->address(data, ret_addr, 1);
(*graph)++;
}
#else
static inline void
print_ftrace_graph_addr(unsigned long addr, void *data,
const struct stacktrace_ops *ops,
struct thread_info *tinfo, int *graph)
{ }
#endif
/*
* x86-64 can have up to three kernel stacks:
* process stack
* interrupt stack
* severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
*/
static inline int valid_stack_ptr(struct thread_info *tinfo,
void *p, unsigned int size, void *end)
{
void *t = tinfo;
if (end) {
if (p < end && p >= (end-THREAD_SIZE))
return 1;
else
return 0;
}
return p > t && p < t + THREAD_SIZE - size;
}
unsigned long
print_context_stack(struct thread_info *tinfo,
unsigned long *stack, unsigned long bp,
const struct stacktrace_ops *ops, void *data,
unsigned long *end, int *graph)
{
struct stack_frame *frame = (struct stack_frame *)bp;
while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
unsigned long addr;
addr = *stack;
if (__kernel_text_address(addr)) {
if ((unsigned long) stack == bp + sizeof(long)) {
ops->address(data, addr, 1);
frame = frame->next_frame;
bp = (unsigned long) frame;
} else {
ops->address(data, addr, bp == 0);
}
print_ftrace_graph_addr(addr, data, ops, tinfo, graph);
}
stack++;
}
return bp;
}
static void
print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
{
printk(data);
print_symbol(msg, symbol);
printk("\n");
}
static void print_trace_warning(void *data, char *msg)
{
printk("%s%s\n", (char *)data, msg);
}
static int print_trace_stack(void *data, char *name)
{
printk("%s <%s> ", (char *)data, name);
return 0;
}
/*
* Print one address/symbol entries per line.
*/
static void print_trace_address(void *data, unsigned long addr, int reliable)
{
touch_nmi_watchdog();
printk(data);
printk_address(addr, reliable);
}
static const struct stacktrace_ops print_trace_ops = {
.warning = print_trace_warning,
.warning_symbol = print_trace_warning_symbol,
.stack = print_trace_stack,
.address = print_trace_address,
};
void
show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp, char *log_lvl)
{
printk("%sCall Trace:\n", log_lvl);
dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
}
void show_trace(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp)
{
show_trace_log_lvl(task, regs, stack, bp, "");
}
void show_stack(struct task_struct *task, unsigned long *sp)
{
show_stack_log_lvl(task, NULL, sp, 0, "");
}
/*
* The architecture-independent dump_stack generator
*/
void dump_stack(void)
{
unsigned long bp = 0;
unsigned long stack;
#ifdef CONFIG_FRAME_POINTER
if (!bp)
get_bp(bp);
#endif
printk("Pid: %d, comm: %.20s %s %s %.*s\n",
current->pid, current->comm, print_tainted(),
init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
show_trace(NULL, NULL, &stack, bp);
}
EXPORT_SYMBOL(dump_stack);
static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
static int die_owner = -1;
static unsigned int die_nest_count;
unsigned __kprobes long oops_begin(void)
{
int cpu;
unsigned long flags;
oops_enter();
/* racy, but better than risking deadlock. */
raw_local_irq_save(flags);
cpu = smp_processor_id();
if (!__raw_spin_trylock(&die_lock)) {
if (cpu == die_owner)
/* nested oops. should stop eventually */;
else
__raw_spin_lock(&die_lock);
}
die_nest_count++;
die_owner = cpu;
console_verbose();
bust_spinlocks(1);
return flags;
}
void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
{
if (regs && kexec_should_crash(current))
crash_kexec(regs);
bust_spinlocks(0);
die_owner = -1;
add_taint(TAINT_DIE);
die_nest_count--;
if (!die_nest_count)
/* Nest count reaches zero, release the lock. */
__raw_spin_unlock(&die_lock);
raw_local_irq_restore(flags);
oops_exit();
if (!signr)
return;
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
do_exit(signr);
}
int __kprobes __die(const char *str, struct pt_regs *regs, long err)
{
#ifdef CONFIG_X86_32
unsigned short ss;
unsigned long sp;
#endif
printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
#ifdef CONFIG_PREEMPT
printk("PREEMPT ");
#endif
#ifdef CONFIG_SMP
printk("SMP ");
#endif
#ifdef CONFIG_DEBUG_PAGEALLOC
printk("DEBUG_PAGEALLOC");
#endif
printk("\n");
sysfs_printk_last_file();
if (notify_die(DIE_OOPS, str, regs, err,
current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
return 1;
show_registers(regs);
#ifdef CONFIG_X86_32
sp = (unsigned long) (&regs->sp);
savesegment(ss, ss);
if (user_mode(regs)) {
sp = regs->sp;
ss = regs->ss & 0xffff;
}
printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip);
print_symbol("%s", regs->ip);
printk(" SS:ESP %04x:%08lx\n", ss, sp);
#else
/* Executive summary in case the oops scrolled away */
printk(KERN_ALERT "RIP ");
printk_address(regs->ip, 1);
printk(" RSP <%016lx>\n", regs->sp);
#endif
return 0;
}
/*
* This is gone through when something in the kernel has done something bad
* and is about to be terminated:
*/
void die(const char *str, struct pt_regs *regs, long err)
{
unsigned long flags = oops_begin();
int sig = SIGSEGV;
if (!user_mode_vm(regs))
report_bug(regs->ip, regs);
if (__die(str, regs, err))
sig = 0;
oops_end(flags, regs, sig);
}
void notrace __kprobes
die_nmi(char *str, struct pt_regs *regs, int do_panic)
{
unsigned long flags;
if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
return;
/*
* We are in trouble anyway, lets at least try
* to get a message out.
*/
flags = oops_begin();
printk(KERN_EMERG "%s", str);
printk(" on CPU%d, ip %08lx, registers:\n",
smp_processor_id(), regs->ip);
show_registers(regs);
oops_end(flags, regs, 0);
if (do_panic || panic_on_oops)
panic("Non maskable interrupt");
nmi_exit();
local_irq_enable();
do_exit(SIGBUS);
}
static int __init oops_setup(char *s)
{
if (!s)
return -EINVAL;
if (!strcmp(s, "panic"))
panic_on_oops = 1;
return 0;
}
early_param("oops", oops_setup);
static int __init kstack_setup(char *s)
{
if (!s)
return -EINVAL;
kstack_depth_to_print = simple_strtoul(s, NULL, 0);
return 0;
}
early_param("kstack", kstack_setup);
static int __init code_bytes_setup(char *s)
{
code_bytes = simple_strtoul(s, NULL, 0);
if (code_bytes > 8192)
code_bytes = 8192;
return 1;
}
__setup("code_bytes=", code_bytes_setup);
/*
* Copyright (C) 1991, 1992 Linus Torvalds
* Copyright (C) 2000, 2001, 2002 Andi Kleen, SuSE Labs
*/
#ifndef DUMPSTACK_H
#define DUMPSTACK_H
#ifdef CONFIG_X86_32
#define STACKSLOTS_PER_LINE 8
#define get_bp(bp) asm("movl %%ebp, %0" : "=r" (bp) :)
#else
#define STACKSLOTS_PER_LINE 4
#define get_bp(bp) asm("movq %%rbp, %0" : "=r" (bp) :)
#endif
extern unsigned long
print_context_stack(struct thread_info *tinfo,
unsigned long *stack, unsigned long bp,
const struct stacktrace_ops *ops, void *data,
unsigned long *end, int *graph);
extern void
show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp, char *log_lvl);
extern void
show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
unsigned long *sp, unsigned long bp, char *log_lvl);
extern unsigned int code_bytes;
extern int kstack_depth_to_print;
/* The form of the top of the frame on the stack */
struct stack_frame {
struct stack_frame *next_frame;
unsigned long return_address;
};
#endif
...@@ -17,69 +17,14 @@ ...@@ -17,69 +17,14 @@
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
#define STACKSLOTS_PER_LINE 8 #include "dumpstack.h"
#define get_bp(bp) asm("movl %%ebp, %0" : "=r" (bp) :)
int panic_on_unrecovered_nmi;
int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE;
static unsigned int code_bytes = 64;
static int die_counter;
void printk_address(unsigned long address, int reliable)
{
printk(" [<%p>] %s%pS\n", (void *) address,
reliable ? "" : "? ", (void *) address);
}
static inline int valid_stack_ptr(struct thread_info *tinfo,
void *p, unsigned int size, void *end)
{
void *t = tinfo;
if (end) {
if (p < end && p >= (end-THREAD_SIZE))
return 1;
else
return 0;
}
return p > t && p < t + THREAD_SIZE - size;
}
/* The form of the top of the frame on the stack */
struct stack_frame {
struct stack_frame *next_frame;
unsigned long return_address;
};
static inline unsigned long
print_context_stack(struct thread_info *tinfo,
unsigned long *stack, unsigned long bp,
const struct stacktrace_ops *ops, void *data,
unsigned long *end)
{
struct stack_frame *frame = (struct stack_frame *)bp;
while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
unsigned long addr;
addr = *stack;
if (__kernel_text_address(addr)) {
if ((unsigned long) stack == bp + sizeof(long)) {
ops->address(data, addr, 1);
frame = frame->next_frame;
bp = (unsigned long) frame;
} else {
ops->address(data, addr, bp == 0);
}
}
stack++;
}
return bp;
}
void dump_trace(struct task_struct *task, struct pt_regs *regs, void dump_trace(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp, unsigned long *stack, unsigned long bp,
const struct stacktrace_ops *ops, void *data) const struct stacktrace_ops *ops, void *data)
{ {
int graph = 0;
if (!task) if (!task)
task = current; task = current;
...@@ -107,7 +52,8 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, ...@@ -107,7 +52,8 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
context = (struct thread_info *) context = (struct thread_info *)
((unsigned long)stack & (~(THREAD_SIZE - 1))); ((unsigned long)stack & (~(THREAD_SIZE - 1)));
bp = print_context_stack(context, stack, bp, ops, data, NULL); bp = print_context_stack(context, stack, bp, ops,
data, NULL, &graph);
stack = (unsigned long *)context->previous_esp; stack = (unsigned long *)context->previous_esp;
if (!stack) if (!stack)
...@@ -119,57 +65,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, ...@@ -119,57 +65,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
} }
EXPORT_SYMBOL(dump_trace); EXPORT_SYMBOL(dump_trace);
static void void
print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
{
printk(data);
print_symbol(msg, symbol);
printk("\n");
}
static void print_trace_warning(void *data, char *msg)
{
printk("%s%s\n", (char *)data, msg);
}
static int print_trace_stack(void *data, char *name)
{
printk("%s <%s> ", (char *)data, name);
return 0;
}
/*
* Print one address/symbol entries per line.
*/
static void print_trace_address(void *data, unsigned long addr, int reliable)
{
touch_nmi_watchdog();
printk(data);
printk_address(addr, reliable);
}
static const struct stacktrace_ops print_trace_ops = {
.warning = print_trace_warning,
.warning_symbol = print_trace_warning_symbol,
.stack = print_trace_stack,
.address = print_trace_address,
};
static void
show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp, char *log_lvl)
{
printk("%sCall Trace:\n", log_lvl);
dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
}
void show_trace(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp)
{
show_trace_log_lvl(task, regs, stack, bp, "");
}
static void
show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
unsigned long *sp, unsigned long bp, char *log_lvl) unsigned long *sp, unsigned long bp, char *log_lvl)
{ {
...@@ -196,33 +92,6 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, ...@@ -196,33 +92,6 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
show_trace_log_lvl(task, regs, sp, bp, log_lvl); show_trace_log_lvl(task, regs, sp, bp, log_lvl);
} }
void show_stack(struct task_struct *task, unsigned long *sp)
{
show_stack_log_lvl(task, NULL, sp, 0, "");
}
/*
* The architecture-independent dump_stack generator
*/
void dump_stack(void)
{
unsigned long bp = 0;
unsigned long stack;
#ifdef CONFIG_FRAME_POINTER
if (!bp)
get_bp(bp);
#endif
printk("Pid: %d, comm: %.20s %s %s %.*s\n",
current->pid, current->comm, print_tainted(),
init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
show_trace(NULL, NULL, &stack, bp);
}
EXPORT_SYMBOL(dump_stack);
void show_registers(struct pt_regs *regs) void show_registers(struct pt_regs *regs)
{ {
...@@ -283,167 +152,3 @@ int is_valid_bugaddr(unsigned long ip) ...@@ -283,167 +152,3 @@ int is_valid_bugaddr(unsigned long ip)
return ud2 == 0x0b0f; return ud2 == 0x0b0f;
} }
static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
static int die_owner = -1;
static unsigned int die_nest_count;
unsigned __kprobes long oops_begin(void)
{
unsigned long flags;
oops_enter();
if (die_owner != raw_smp_processor_id()) {
console_verbose();
raw_local_irq_save(flags);
__raw_spin_lock(&die_lock);
die_owner = smp_processor_id();
die_nest_count = 0;
bust_spinlocks(1);
} else {
raw_local_irq_save(flags);
}
die_nest_count++;
return flags;
}
void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
{
bust_spinlocks(0);
die_owner = -1;
add_taint(TAINT_DIE);
__raw_spin_unlock(&die_lock);
raw_local_irq_restore(flags);
if (!regs)
return;
if (kexec_should_crash(current))
crash_kexec(regs);
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
oops_exit();
do_exit(signr);
}
int __kprobes __die(const char *str, struct pt_regs *regs, long err)
{
unsigned short ss;
unsigned long sp;
printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
#ifdef CONFIG_PREEMPT
printk("PREEMPT ");
#endif
#ifdef CONFIG_SMP
printk("SMP ");
#endif
#ifdef CONFIG_DEBUG_PAGEALLOC
printk("DEBUG_PAGEALLOC");
#endif
printk("\n");
sysfs_printk_last_file();
if (notify_die(DIE_OOPS, str, regs, err,
current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
return 1;
show_registers(regs);
/* Executive summary in case the oops scrolled away */
sp = (unsigned long) (&regs->sp);
savesegment(ss, ss);
if (user_mode(regs)) {
sp = regs->sp;
ss = regs->ss & 0xffff;
}
printk(KERN_EMERG "EIP: [<%08lx>] ", regs->ip);
print_symbol("%s", regs->ip);
printk(" SS:ESP %04x:%08lx\n", ss, sp);
return 0;
}
/*
* This is gone through when something in the kernel has done something bad
* and is about to be terminated:
*/
void die(const char *str, struct pt_regs *regs, long err)
{
unsigned long flags = oops_begin();
if (die_nest_count < 3) {
report_bug(regs->ip, regs);
if (__die(str, regs, err))
regs = NULL;
} else {
printk(KERN_EMERG "Recursive die() failure, output suppressed\n");
}
oops_end(flags, regs, SIGSEGV);
}
static DEFINE_SPINLOCK(nmi_print_lock);
void notrace __kprobes
die_nmi(char *str, struct pt_regs *regs, int do_panic)
{
if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
return;
spin_lock(&nmi_print_lock);
/*
* We are in trouble anyway, lets at least try
* to get a message out:
*/
bust_spinlocks(1);
printk(KERN_EMERG "%s", str);
printk(" on CPU%d, ip %08lx, registers:\n",
smp_processor_id(), regs->ip);
show_registers(regs);
if (do_panic)
panic("Non maskable interrupt");
console_silent();
spin_unlock(&nmi_print_lock);
/*
* If we are in kernel we are probably nested up pretty bad
* and might aswell get out now while we still can:
*/
if (!user_mode_vm(regs)) {
current->thread.trap_no = 2;
crash_kexec(regs);
}
bust_spinlocks(0);
do_exit(SIGSEGV);
}
static int __init oops_setup(char *s)
{
if (!s)
return -EINVAL;
if (!strcmp(s, "panic"))
panic_on_oops = 1;
return 0;
}
early_param("oops", oops_setup);
static int __init kstack_setup(char *s)
{
if (!s)
return -EINVAL;
kstack_depth_to_print = simple_strtoul(s, NULL, 0);
return 0;
}
early_param("kstack", kstack_setup);
static int __init code_bytes_setup(char *s)
{
code_bytes = simple_strtoul(s, NULL, 0);
if (code_bytes > 8192)
code_bytes = 8192;
return 1;
}
__setup("code_bytes=", code_bytes_setup);
...@@ -17,19 +17,7 @@ ...@@ -17,19 +17,7 @@
#include <asm/stacktrace.h> #include <asm/stacktrace.h>
#define STACKSLOTS_PER_LINE 4 #include "dumpstack.h"
#define get_bp(bp) asm("movq %%rbp, %0" : "=r" (bp) :)
int panic_on_unrecovered_nmi;
int kstack_depth_to_print = 3 * STACKSLOTS_PER_LINE;
static unsigned int code_bytes = 64;
static int die_counter;
void printk_address(unsigned long address, int reliable)
{
printk(" [<%p>] %s%pS\n", (void *) address,
reliable ? "" : "? ", (void *) address);
}
static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
unsigned *usedp, char **idp) unsigned *usedp, char **idp)
...@@ -113,51 +101,6 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack, ...@@ -113,51 +101,6 @@ static unsigned long *in_exception_stack(unsigned cpu, unsigned long stack,
* severe exception (double fault, nmi, stack fault, debug, mce) hardware stack * severe exception (double fault, nmi, stack fault, debug, mce) hardware stack
*/ */
static inline int valid_stack_ptr(struct thread_info *tinfo,
void *p, unsigned int size, void *end)
{
void *t = tinfo;
if (end) {
if (p < end && p >= (end-THREAD_SIZE))
return 1;
else
return 0;
}
return p > t && p < t + THREAD_SIZE - size;
}
/* The form of the top of the frame on the stack */
struct stack_frame {
struct stack_frame *next_frame;
unsigned long return_address;
};
static inline unsigned long
print_context_stack(struct thread_info *tinfo,
unsigned long *stack, unsigned long bp,
const struct stacktrace_ops *ops, void *data,
unsigned long *end)
{
struct stack_frame *frame = (struct stack_frame *)bp;
while (valid_stack_ptr(tinfo, stack, sizeof(*stack), end)) {
unsigned long addr;
addr = *stack;
if (__kernel_text_address(addr)) {
if ((unsigned long) stack == bp + sizeof(long)) {
ops->address(data, addr, 1);
frame = frame->next_frame;
bp = (unsigned long) frame;
} else {
ops->address(data, addr, bp == 0);
}
}
stack++;
}
return bp;
}
void dump_trace(struct task_struct *task, struct pt_regs *regs, void dump_trace(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp, unsigned long *stack, unsigned long bp,
const struct stacktrace_ops *ops, void *data) const struct stacktrace_ops *ops, void *data)
...@@ -166,6 +109,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, ...@@ -166,6 +109,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr; unsigned long *irqstack_end = (unsigned long *)cpu_pda(cpu)->irqstackptr;
unsigned used = 0; unsigned used = 0;
struct thread_info *tinfo; struct thread_info *tinfo;
int graph = 0;
if (!task) if (!task)
task = current; task = current;
...@@ -206,7 +150,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, ...@@ -206,7 +150,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
break; break;
bp = print_context_stack(tinfo, stack, bp, ops, bp = print_context_stack(tinfo, stack, bp, ops,
data, estack_end); data, estack_end, &graph);
ops->stack(data, "<EOE>"); ops->stack(data, "<EOE>");
/* /*
* We link to the next stack via the * We link to the next stack via the
...@@ -225,7 +169,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, ...@@ -225,7 +169,7 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
if (ops->stack(data, "IRQ") < 0) if (ops->stack(data, "IRQ") < 0)
break; break;
bp = print_context_stack(tinfo, stack, bp, bp = print_context_stack(tinfo, stack, bp,
ops, data, irqstack_end); ops, data, irqstack_end, &graph);
/* /*
* We link to the next stack (which would be * We link to the next stack (which would be
* the process stack normally) the last * the process stack normally) the last
...@@ -243,62 +187,12 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs, ...@@ -243,62 +187,12 @@ void dump_trace(struct task_struct *task, struct pt_regs *regs,
/* /*
* This handles the process stack: * This handles the process stack:
*/ */
bp = print_context_stack(tinfo, stack, bp, ops, data, NULL); bp = print_context_stack(tinfo, stack, bp, ops, data, NULL, &graph);
put_cpu(); put_cpu();
} }
EXPORT_SYMBOL(dump_trace); EXPORT_SYMBOL(dump_trace);
static void void
print_trace_warning_symbol(void *data, char *msg, unsigned long symbol)
{
printk(data);
print_symbol(msg, symbol);
printk("\n");
}
static void print_trace_warning(void *data, char *msg)
{
printk("%s%s\n", (char *)data, msg);
}
static int print_trace_stack(void *data, char *name)
{
printk("%s <%s> ", (char *)data, name);
return 0;
}
/*
* Print one address/symbol entries per line.
*/
static void print_trace_address(void *data, unsigned long addr, int reliable)
{
touch_nmi_watchdog();
printk(data);
printk_address(addr, reliable);
}
static const struct stacktrace_ops print_trace_ops = {
.warning = print_trace_warning,
.warning_symbol = print_trace_warning_symbol,
.stack = print_trace_stack,
.address = print_trace_address,
};
static void
show_trace_log_lvl(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp, char *log_lvl)
{
printk("%sCall Trace:\n", log_lvl);
dump_trace(task, regs, stack, bp, &print_trace_ops, log_lvl);
}
void show_trace(struct task_struct *task, struct pt_regs *regs,
unsigned long *stack, unsigned long bp)
{
show_trace_log_lvl(task, regs, stack, bp, "");
}
static void
show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
unsigned long *sp, unsigned long bp, char *log_lvl) unsigned long *sp, unsigned long bp, char *log_lvl)
{ {
...@@ -342,33 +236,6 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs, ...@@ -342,33 +236,6 @@ show_stack_log_lvl(struct task_struct *task, struct pt_regs *regs,
show_trace_log_lvl(task, regs, sp, bp, log_lvl); show_trace_log_lvl(task, regs, sp, bp, log_lvl);
} }
void show_stack(struct task_struct *task, unsigned long *sp)
{
show_stack_log_lvl(task, NULL, sp, 0, "");
}
/*
* The architecture-independent dump_stack generator
*/
void dump_stack(void)
{
unsigned long bp = 0;
unsigned long stack;
#ifdef CONFIG_FRAME_POINTER
if (!bp)
get_bp(bp);
#endif
printk("Pid: %d, comm: %.20s %s %s %.*s\n",
current->pid, current->comm, print_tainted(),
init_utsname()->release,
(int)strcspn(init_utsname()->version, " "),
init_utsname()->version);
show_trace(NULL, NULL, &stack, bp);
}
EXPORT_SYMBOL(dump_stack);
void show_registers(struct pt_regs *regs) void show_registers(struct pt_regs *regs)
{ {
int i; int i;
...@@ -429,147 +296,3 @@ int is_valid_bugaddr(unsigned long ip) ...@@ -429,147 +296,3 @@ int is_valid_bugaddr(unsigned long ip)
return ud2 == 0x0b0f; return ud2 == 0x0b0f;
} }
static raw_spinlock_t die_lock = __RAW_SPIN_LOCK_UNLOCKED;
static int die_owner = -1;
static unsigned int die_nest_count;
unsigned __kprobes long oops_begin(void)
{
int cpu;
unsigned long flags;
oops_enter();
/* racy, but better than risking deadlock. */
raw_local_irq_save(flags);
cpu = smp_processor_id();
if (!__raw_spin_trylock(&die_lock)) {
if (cpu == die_owner)
/* nested oops. should stop eventually */;
else
__raw_spin_lock(&die_lock);
}
die_nest_count++;
die_owner = cpu;
console_verbose();
bust_spinlocks(1);
return flags;
}
void __kprobes oops_end(unsigned long flags, struct pt_regs *regs, int signr)
{
die_owner = -1;
bust_spinlocks(0);
die_nest_count--;
if (!die_nest_count)
/* Nest count reaches zero, release the lock. */
__raw_spin_unlock(&die_lock);
raw_local_irq_restore(flags);
if (!regs) {
oops_exit();
return;
}
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
oops_exit();
do_exit(signr);
}
int __kprobes __die(const char *str, struct pt_regs *regs, long err)
{
printk(KERN_EMERG "%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
#ifdef CONFIG_PREEMPT
printk("PREEMPT ");
#endif
#ifdef CONFIG_SMP
printk("SMP ");
#endif
#ifdef CONFIG_DEBUG_PAGEALLOC
printk("DEBUG_PAGEALLOC");
#endif
printk("\n");
sysfs_printk_last_file();
if (notify_die(DIE_OOPS, str, regs, err,
current->thread.trap_no, SIGSEGV) == NOTIFY_STOP)
return 1;
show_registers(regs);
add_taint(TAINT_DIE);
/* Executive summary in case the oops scrolled away */
printk(KERN_ALERT "RIP ");
printk_address(regs->ip, 1);
printk(" RSP <%016lx>\n", regs->sp);
if (kexec_should_crash(current))
crash_kexec(regs);
return 0;
}
void die(const char *str, struct pt_regs *regs, long err)
{
unsigned long flags = oops_begin();
if (!user_mode(regs))
report_bug(regs->ip, regs);
if (__die(str, regs, err))
regs = NULL;
oops_end(flags, regs, SIGSEGV);
}
notrace __kprobes void
die_nmi(char *str, struct pt_regs *regs, int do_panic)
{
unsigned long flags;
if (notify_die(DIE_NMIWATCHDOG, str, regs, 0, 2, SIGINT) == NOTIFY_STOP)
return;
flags = oops_begin();
/*
* We are in trouble anyway, lets at least try
* to get a message out.
*/
printk(KERN_EMERG "%s", str);
printk(" on CPU%d, ip %08lx, registers:\n",
smp_processor_id(), regs->ip);
show_registers(regs);
if (kexec_should_crash(current))
crash_kexec(regs);
if (do_panic || panic_on_oops)
panic("Non maskable interrupt");
oops_end(flags, NULL, SIGBUS);
nmi_exit();
local_irq_enable();
do_exit(SIGBUS);
}
static int __init oops_setup(char *s)
{
if (!s)
return -EINVAL;
if (!strcmp(s, "panic"))
panic_on_oops = 1;
return 0;
}
early_param("oops", oops_setup);
static int __init kstack_setup(char *s)
{
if (!s)
return -EINVAL;
kstack_depth_to_print = simple_strtoul(s, NULL, 0);
return 0;
}
early_param("kstack", kstack_setup);
static int __init code_bytes_setup(char *s)
{
code_bytes = simple_strtoul(s, NULL, 0);
if (code_bytes > 8192)
code_bytes = 8192;
return 1;
}
__setup("code_bytes=", code_bytes_setup);
...@@ -1196,6 +1196,9 @@ ENTRY(mcount) ...@@ -1196,6 +1196,9 @@ ENTRY(mcount)
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
cmpl $ftrace_stub, ftrace_graph_return cmpl $ftrace_stub, ftrace_graph_return
jnz ftrace_graph_caller jnz ftrace_graph_caller
cmpl $ftrace_graph_entry_stub, ftrace_graph_entry
jnz ftrace_graph_caller
#endif #endif
.globl ftrace_stub .globl ftrace_stub
ftrace_stub: ftrace_stub:
...@@ -1230,6 +1233,7 @@ ENTRY(ftrace_graph_caller) ...@@ -1230,6 +1233,7 @@ ENTRY(ftrace_graph_caller)
pushl %edx pushl %edx
movl 0xc(%esp), %edx movl 0xc(%esp), %edx
lea 0x4(%ebp), %eax lea 0x4(%ebp), %eax
subl $MCOUNT_INSN_SIZE, %edx
call prepare_ftrace_return call prepare_ftrace_return
popl %edx popl %edx
popl %ecx popl %ecx
......
...@@ -120,6 +120,9 @@ ENTRY(mcount) ...@@ -120,6 +120,9 @@ ENTRY(mcount)
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
cmpq $ftrace_stub, ftrace_graph_return cmpq $ftrace_stub, ftrace_graph_return
jnz ftrace_graph_caller jnz ftrace_graph_caller
cmpq $ftrace_graph_entry_stub, ftrace_graph_entry
jnz ftrace_graph_caller
#endif #endif
.globl ftrace_stub .globl ftrace_stub
...@@ -173,6 +176,7 @@ ENTRY(ftrace_graph_caller) ...@@ -173,6 +176,7 @@ ENTRY(ftrace_graph_caller)
leaq 8(%rbp), %rdi leaq 8(%rbp), %rdi
movq 0x38(%rsp), %rsi movq 0x38(%rsp), %rsi
subq $MCOUNT_INSN_SIZE, %rsi
call prepare_ftrace_return call prepare_ftrace_return
......
...@@ -420,12 +420,23 @@ static void pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret) ...@@ -420,12 +420,23 @@ static void pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret)
int index; int index;
index = current->curr_ret_stack; index = current->curr_ret_stack;
if (unlikely(index < 0)) {
ftrace_graph_stop();
WARN_ON(1);
/* Might as well panic, otherwise we have no where to go */
*ret = (unsigned long)panic;
return;
}
*ret = current->ret_stack[index].ret; *ret = current->ret_stack[index].ret;
trace->func = current->ret_stack[index].func; trace->func = current->ret_stack[index].func;
trace->calltime = current->ret_stack[index].calltime; trace->calltime = current->ret_stack[index].calltime;
trace->overrun = atomic_read(&current->trace_overrun); trace->overrun = atomic_read(&current->trace_overrun);
trace->depth = index; trace->depth = index;
barrier();
current->curr_ret_stack--; current->curr_ret_stack--;
} }
/* /*
...@@ -441,6 +452,13 @@ unsigned long ftrace_return_to_handler(void) ...@@ -441,6 +452,13 @@ unsigned long ftrace_return_to_handler(void)
trace.rettime = cpu_clock(raw_smp_processor_id()); trace.rettime = cpu_clock(raw_smp_processor_id());
ftrace_graph_return(&trace); ftrace_graph_return(&trace);
if (unlikely(!ret)) {
ftrace_graph_stop();
WARN_ON(1);
/* Might as well panic. What else to do? */
ret = (unsigned long)panic;
}
return ret; return ret;
} }
...@@ -467,28 +485,16 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) ...@@ -467,28 +485,16 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
* ignore such a protection. * ignore such a protection.
*/ */
asm volatile( asm volatile(
#ifdef CONFIG_X86_64 "1: " _ASM_MOV " (%[parent_old]), %[old]\n"
"1: movq (%[parent_old]), %[old]\n" "2: " _ASM_MOV " %[return_hooker], (%[parent_replaced])\n"
"2: movq %[return_hooker], (%[parent_replaced])\n"
#else
"1: movl (%[parent_old]), %[old]\n"
"2: movl %[return_hooker], (%[parent_replaced])\n"
#endif
" movl $0, %[faulted]\n" " movl $0, %[faulted]\n"
".section .fixup, \"ax\"\n" ".section .fixup, \"ax\"\n"
"3: movl $1, %[faulted]\n" "3: movl $1, %[faulted]\n"
".previous\n" ".previous\n"
".section __ex_table, \"a\"\n" _ASM_EXTABLE(1b, 3b)
#ifdef CONFIG_X86_64 _ASM_EXTABLE(2b, 3b)
" .quad 1b, 3b\n"
" .quad 2b, 3b\n"
#else
" .long 1b, 3b\n"
" .long 2b, 3b\n"
#endif
".previous\n"
: [parent_replaced] "=r" (parent), [old] "=r" (old), : [parent_replaced] "=r" (parent), [old] "=r" (old),
[faulted] "=r" (faulted) [faulted] "=r" (faulted)
...@@ -496,14 +502,16 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) ...@@ -496,14 +502,16 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
: "memory" : "memory"
); );
if (WARN_ON(faulted)) { if (unlikely(faulted)) {
unregister_ftrace_graph(); ftrace_graph_stop();
WARN_ON(1);
return; return;
} }
if (WARN_ON(!__kernel_text_address(old))) { if (unlikely(!__kernel_text_address(old))) {
unregister_ftrace_graph(); ftrace_graph_stop();
*parent = old; *parent = old;
WARN_ON(1);
return; return;
} }
...@@ -516,7 +524,11 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr) ...@@ -516,7 +524,11 @@ void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr)
} }
trace.func = self_addr; trace.func = self_addr;
ftrace_graph_entry(&trace);
/* Only trace if the calling function expects to */
if (!ftrace_graph_entry(&trace)) {
current->curr_ret_stack--;
*parent = old;
}
} }
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
...@@ -413,6 +413,7 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs, ...@@ -413,6 +413,7 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
unsigned long error_code) unsigned long error_code)
{ {
unsigned long flags = oops_begin(); unsigned long flags = oops_begin();
int sig = SIGKILL;
struct task_struct *tsk; struct task_struct *tsk;
printk(KERN_ALERT "%s: Corrupted page table at address %lx\n", printk(KERN_ALERT "%s: Corrupted page table at address %lx\n",
...@@ -423,8 +424,8 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs, ...@@ -423,8 +424,8 @@ static noinline void pgtable_bad(unsigned long address, struct pt_regs *regs,
tsk->thread.trap_no = 14; tsk->thread.trap_no = 14;
tsk->thread.error_code = error_code; tsk->thread.error_code = error_code;
if (__die("Bad pagetable", regs, error_code)) if (__die("Bad pagetable", regs, error_code))
regs = NULL; sig = 0;
oops_end(flags, regs, SIGKILL); oops_end(flags, regs, sig);
} }
#endif #endif
...@@ -590,6 +591,7 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) ...@@ -590,6 +591,7 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
int fault; int fault;
#ifdef CONFIG_X86_64 #ifdef CONFIG_X86_64
unsigned long flags; unsigned long flags;
int sig;
#endif #endif
tsk = current; tsk = current;
...@@ -849,11 +851,12 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) ...@@ -849,11 +851,12 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code)
bust_spinlocks(0); bust_spinlocks(0);
do_exit(SIGKILL); do_exit(SIGKILL);
#else #else
sig = SIGKILL;
if (__die("Oops", regs, error_code)) if (__die("Oops", regs, error_code))
regs = NULL; sig = 0;
/* Executive summary in case the body of the oops scrolled away */ /* Executive summary in case the body of the oops scrolled away */
printk(KERN_EMERG "CR2: %016lx\n", address); printk(KERN_EMERG "CR2: %016lx\n", address);
oops_end(flags, regs, SIGKILL); oops_end(flags, regs, sig);
#endif #endif
/* /*
......
...@@ -371,11 +371,13 @@ struct ftrace_graph_ret { ...@@ -371,11 +371,13 @@ struct ftrace_graph_ret {
#define FTRACE_RETSTACK_ALLOC_SIZE 32 #define FTRACE_RETSTACK_ALLOC_SIZE 32
/* Type of the callback handlers for tracing function graph*/ /* Type of the callback handlers for tracing function graph*/
typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */ typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */
typedef void (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */ typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */
extern int register_ftrace_graph(trace_func_graph_ret_t retfunc, extern int register_ftrace_graph(trace_func_graph_ret_t retfunc,
trace_func_graph_ent_t entryfunc); trace_func_graph_ent_t entryfunc);
extern void ftrace_graph_stop(void);
/* The current handlers in use */ /* The current handlers in use */
extern trace_func_graph_ret_t ftrace_graph_return; extern trace_func_graph_ret_t ftrace_graph_return;
extern trace_func_graph_ent_t ftrace_graph_entry; extern trace_func_graph_ent_t ftrace_graph_entry;
......
...@@ -124,6 +124,11 @@ void tracing_on(void); ...@@ -124,6 +124,11 @@ void tracing_on(void);
void tracing_off(void); void tracing_off(void);
void tracing_off_permanent(void); void tracing_off_permanent(void);
void *ring_buffer_alloc_read_page(struct ring_buffer *buffer);
void ring_buffer_free_read_page(struct ring_buffer *buffer, void *data);
int ring_buffer_read_page(struct ring_buffer *buffer,
void **data_page, int cpu, int full);
enum ring_buffer_flags { enum ring_buffer_flags {
RB_FL_OVERWRITE = 1 << 0, RB_FL_OVERWRITE = 1 << 0,
}; };
......
...@@ -1137,6 +1137,8 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1137,6 +1137,8 @@ static struct task_struct *copy_process(unsigned long clone_flags,
} }
} }
ftrace_graph_init_task(p);
p->pid = pid_nr(pid); p->pid = pid_nr(pid);
p->tgid = p->pid; p->tgid = p->pid;
if (clone_flags & CLONE_THREAD) if (clone_flags & CLONE_THREAD)
...@@ -1145,7 +1147,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1145,7 +1147,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
if (current->nsproxy != p->nsproxy) { if (current->nsproxy != p->nsproxy) {
retval = ns_cgroup_clone(p, pid); retval = ns_cgroup_clone(p, pid);
if (retval) if (retval)
goto bad_fork_free_pid; goto bad_fork_free_graph;
} }
p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL; p->set_child_tid = (clone_flags & CLONE_CHILD_SETTID) ? child_tidptr : NULL;
...@@ -1238,7 +1240,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1238,7 +1240,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
spin_unlock(&current->sighand->siglock); spin_unlock(&current->sighand->siglock);
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
retval = -ERESTARTNOINTR; retval = -ERESTARTNOINTR;
goto bad_fork_free_pid; goto bad_fork_free_graph;
} }
if (clone_flags & CLONE_THREAD) { if (clone_flags & CLONE_THREAD) {
...@@ -1271,11 +1273,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1271,11 +1273,12 @@ static struct task_struct *copy_process(unsigned long clone_flags,
total_forks++; total_forks++;
spin_unlock(&current->sighand->siglock); spin_unlock(&current->sighand->siglock);
write_unlock_irq(&tasklist_lock); write_unlock_irq(&tasklist_lock);
ftrace_graph_init_task(p);
proc_fork_connector(p); proc_fork_connector(p);
cgroup_post_fork(p); cgroup_post_fork(p);
return p; return p;
bad_fork_free_graph:
ftrace_graph_exit_task(p);
bad_fork_free_pid: bad_fork_free_pid:
if (pid != &init_struct_pid) if (pid != &init_struct_pid)
free_pid(pid); free_pid(pid);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* Thanks to Arjan van de Ven for coming up with the initial idea of * Thanks to Arjan van de Ven for coming up with the initial idea of
* mapping lock dependencies runtime. * mapping lock dependencies runtime.
*/ */
#define DISABLE_BRANCH_PROFILING
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/delay.h> #include <linux/delay.h>
......
...@@ -67,6 +67,7 @@ config FUNCTION_GRAPH_TRACER ...@@ -67,6 +67,7 @@ config FUNCTION_GRAPH_TRACER
bool "Kernel Function Graph Tracer" bool "Kernel Function Graph Tracer"
depends on HAVE_FUNCTION_GRAPH_TRACER depends on HAVE_FUNCTION_GRAPH_TRACER
depends on FUNCTION_TRACER depends on FUNCTION_TRACER
default y
help help
Enable the kernel to trace a function at both its return Enable the kernel to trace a function at both its return
and its entry. and its entry.
......
...@@ -1636,11 +1636,15 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, ...@@ -1636,11 +1636,15 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
static atomic_t ftrace_graph_active; static atomic_t ftrace_graph_active;
int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace)
{
return 0;
}
/* The callbacks that hook a function */ /* The callbacks that hook a function */
trace_func_graph_ret_t ftrace_graph_return = trace_func_graph_ret_t ftrace_graph_return =
(trace_func_graph_ret_t)ftrace_stub; (trace_func_graph_ret_t)ftrace_stub;
trace_func_graph_ent_t ftrace_graph_entry = trace_func_graph_ent_t ftrace_graph_entry = ftrace_graph_entry_stub;
(trace_func_graph_ent_t)ftrace_stub;
/* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */ /* Try to assign a return stack array on FTRACE_RETSTACK_ALLOC_SIZE tasks. */
static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list) static int alloc_retstack_tasklist(struct ftrace_ret_stack **ret_stack_list)
...@@ -1738,7 +1742,7 @@ void unregister_ftrace_graph(void) ...@@ -1738,7 +1742,7 @@ void unregister_ftrace_graph(void)
atomic_dec(&ftrace_graph_active); atomic_dec(&ftrace_graph_active);
ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub;
ftrace_graph_entry = (trace_func_graph_ent_t)ftrace_stub; ftrace_graph_entry = ftrace_graph_entry_stub;
ftrace_shutdown(FTRACE_STOP_FUNC_RET); ftrace_shutdown(FTRACE_STOP_FUNC_RET);
mutex_unlock(&ftrace_sysctl_lock); mutex_unlock(&ftrace_sysctl_lock);
...@@ -1769,5 +1773,10 @@ void ftrace_graph_exit_task(struct task_struct *t) ...@@ -1769,5 +1773,10 @@ void ftrace_graph_exit_task(struct task_struct *t)
kfree(ret_stack); kfree(ret_stack);
} }
void ftrace_graph_stop(void)
{
ftrace_stop();
}
#endif #endif
This diff is collapsed.
...@@ -1200,7 +1200,7 @@ function_trace_call(unsigned long ip, unsigned long parent_ip) ...@@ -1200,7 +1200,7 @@ function_trace_call(unsigned long ip, unsigned long parent_ip)
} }
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
void trace_graph_entry(struct ftrace_graph_ent *trace) int trace_graph_entry(struct ftrace_graph_ent *trace)
{ {
struct trace_array *tr = &global_trace; struct trace_array *tr = &global_trace;
struct trace_array_cpu *data; struct trace_array_cpu *data;
...@@ -1209,7 +1209,7 @@ void trace_graph_entry(struct ftrace_graph_ent *trace) ...@@ -1209,7 +1209,7 @@ void trace_graph_entry(struct ftrace_graph_ent *trace)
int cpu; int cpu;
int pc; int pc;
raw_local_irq_save(flags); local_irq_save(flags);
cpu = raw_smp_processor_id(); cpu = raw_smp_processor_id();
data = tr->data[cpu]; data = tr->data[cpu];
disabled = atomic_inc_return(&data->disabled); disabled = atomic_inc_return(&data->disabled);
...@@ -1218,7 +1218,9 @@ void trace_graph_entry(struct ftrace_graph_ent *trace) ...@@ -1218,7 +1218,9 @@ void trace_graph_entry(struct ftrace_graph_ent *trace)
__trace_graph_entry(tr, data, trace, flags, pc); __trace_graph_entry(tr, data, trace, flags, pc);
} }
atomic_dec(&data->disabled); atomic_dec(&data->disabled);
raw_local_irq_restore(flags); local_irq_restore(flags);
return 1;
} }
void trace_graph_return(struct ftrace_graph_ret *trace) void trace_graph_return(struct ftrace_graph_ret *trace)
...@@ -1230,7 +1232,7 @@ void trace_graph_return(struct ftrace_graph_ret *trace) ...@@ -1230,7 +1232,7 @@ void trace_graph_return(struct ftrace_graph_ret *trace)
int cpu; int cpu;
int pc; int pc;
raw_local_irq_save(flags); local_irq_save(flags);
cpu = raw_smp_processor_id(); cpu = raw_smp_processor_id();
data = tr->data[cpu]; data = tr->data[cpu];
disabled = atomic_inc_return(&data->disabled); disabled = atomic_inc_return(&data->disabled);
...@@ -1239,7 +1241,7 @@ void trace_graph_return(struct ftrace_graph_ret *trace) ...@@ -1239,7 +1241,7 @@ void trace_graph_return(struct ftrace_graph_ret *trace)
__trace_graph_return(tr, data, trace, flags, pc); __trace_graph_return(tr, data, trace, flags, pc);
} }
atomic_dec(&data->disabled); atomic_dec(&data->disabled);
raw_local_irq_restore(flags); local_irq_restore(flags);
} }
#endif /* CONFIG_FUNCTION_GRAPH_TRACER */ #endif /* CONFIG_FUNCTION_GRAPH_TRACER */
...@@ -2645,7 +2647,7 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf, ...@@ -2645,7 +2647,7 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf,
if (err) if (err)
goto err_unlock; goto err_unlock;
raw_local_irq_disable(); local_irq_disable();
__raw_spin_lock(&ftrace_max_lock); __raw_spin_lock(&ftrace_max_lock);
for_each_tracing_cpu(cpu) { for_each_tracing_cpu(cpu) {
/* /*
...@@ -2662,7 +2664,7 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf, ...@@ -2662,7 +2664,7 @@ tracing_cpumask_write(struct file *filp, const char __user *ubuf,
} }
} }
__raw_spin_unlock(&ftrace_max_lock); __raw_spin_unlock(&ftrace_max_lock);
raw_local_irq_enable(); local_irq_enable();
tracing_cpumask = tracing_cpumask_new; tracing_cpumask = tracing_cpumask_new;
......
...@@ -412,7 +412,7 @@ void trace_function(struct trace_array *tr, ...@@ -412,7 +412,7 @@ void trace_function(struct trace_array *tr,
unsigned long flags, int pc); unsigned long flags, int pc);
void trace_graph_return(struct ftrace_graph_ret *trace); void trace_graph_return(struct ftrace_graph_ret *trace);
void trace_graph_entry(struct ftrace_graph_ent *trace); int trace_graph_entry(struct ftrace_graph_ent *trace);
void trace_bts(struct trace_array *tr, void trace_bts(struct trace_array *tr,
unsigned long from, unsigned long from,
unsigned long to); unsigned long to);
......
...@@ -42,7 +42,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect) ...@@ -42,7 +42,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
if (unlikely(!tr)) if (unlikely(!tr))
return; return;
raw_local_irq_save(flags); local_irq_save(flags);
cpu = raw_smp_processor_id(); cpu = raw_smp_processor_id();
if (atomic_inc_return(&tr->data[cpu]->disabled) != 1) if (atomic_inc_return(&tr->data[cpu]->disabled) != 1)
goto out; goto out;
...@@ -74,7 +74,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect) ...@@ -74,7 +74,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
out: out:
atomic_dec(&tr->data[cpu]->disabled); atomic_dec(&tr->data[cpu]->disabled);
raw_local_irq_restore(flags); local_irq_restore(flags);
} }
static inline static inline
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#define TRACE_GRAPH_PRINT_OVERRUN 0x1 #define TRACE_GRAPH_PRINT_OVERRUN 0x1
#define TRACE_GRAPH_PRINT_CPU 0x2 #define TRACE_GRAPH_PRINT_CPU 0x2
#define TRACE_GRAPH_PRINT_OVERHEAD 0x4 #define TRACE_GRAPH_PRINT_OVERHEAD 0x4
#define TRACE_GRAPH_PRINT_PROC 0x8
static struct tracer_opt trace_opts[] = { static struct tracer_opt trace_opts[] = {
/* Display overruns ? */ /* Display overruns ? */
...@@ -27,11 +28,13 @@ static struct tracer_opt trace_opts[] = { ...@@ -27,11 +28,13 @@ static struct tracer_opt trace_opts[] = {
{ TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) }, { TRACER_OPT(funcgraph-cpu, TRACE_GRAPH_PRINT_CPU) },
/* Display Overhead ? */ /* Display Overhead ? */
{ TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) }, { TRACER_OPT(funcgraph-overhead, TRACE_GRAPH_PRINT_OVERHEAD) },
/* Display proc name/pid */
{ TRACER_OPT(funcgraph-proc, TRACE_GRAPH_PRINT_PROC) },
{ } /* Empty entry */ { } /* Empty entry */
}; };
static struct tracer_flags tracer_flags = { static struct tracer_flags tracer_flags = {
/* Don't display overruns by default */ /* Don't display overruns and proc by default */
.val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD, .val = TRACE_GRAPH_PRINT_CPU | TRACE_GRAPH_PRINT_OVERHEAD,
.opts = trace_opts .opts = trace_opts
}; };
...@@ -104,23 +107,63 @@ print_graph_cpu(struct trace_seq *s, int cpu) ...@@ -104,23 +107,63 @@ print_graph_cpu(struct trace_seq *s, int cpu)
return TRACE_TYPE_HANDLED; return TRACE_TYPE_HANDLED;
} }
#define TRACE_GRAPH_PROCINFO_LENGTH 14
static enum print_line_t
print_graph_proc(struct trace_seq *s, pid_t pid)
{
int i;
int ret;
int len;
char comm[8];
int spaces = 0;
/* sign + log10(MAX_INT) + '\0' */
char pid_str[11];
strncpy(comm, trace_find_cmdline(pid), 7);
comm[7] = '\0';
sprintf(pid_str, "%d", pid);
/* 1 stands for the "-" character */
len = strlen(comm) + strlen(pid_str) + 1;
if (len < TRACE_GRAPH_PROCINFO_LENGTH)
spaces = TRACE_GRAPH_PROCINFO_LENGTH - len;
/* First spaces to align center */
for (i = 0; i < spaces / 2; i++) {
ret = trace_seq_printf(s, " ");
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
}
ret = trace_seq_printf(s, "%s-%s", comm, pid_str);
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
/* Last spaces to align center */
for (i = 0; i < spaces - (spaces / 2); i++) {
ret = trace_seq_printf(s, " ");
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
}
return TRACE_TYPE_HANDLED;
}
/* If the pid changed since the last trace, output this event */ /* If the pid changed since the last trace, output this event */
static int verif_pid(struct trace_seq *s, pid_t pid, int cpu) static enum print_line_t
verif_pid(struct trace_seq *s, pid_t pid, int cpu)
{ {
char *comm, *prev_comm;
pid_t prev_pid; pid_t prev_pid;
int ret; int ret;
if (last_pid[cpu] != -1 && last_pid[cpu] == pid) if (last_pid[cpu] != -1 && last_pid[cpu] == pid)
return 1; return TRACE_TYPE_HANDLED;
prev_pid = last_pid[cpu]; prev_pid = last_pid[cpu];
last_pid[cpu] = pid; last_pid[cpu] = pid;
comm = trace_find_cmdline(pid);
prev_comm = trace_find_cmdline(prev_pid);
/* /*
* Context-switch trace line: * Context-switch trace line:
...@@ -130,11 +173,31 @@ static int verif_pid(struct trace_seq *s, pid_t pid, int cpu) ...@@ -130,11 +173,31 @@ static int verif_pid(struct trace_seq *s, pid_t pid, int cpu)
*/ */
ret = trace_seq_printf(s, ret = trace_seq_printf(s,
" ------------------------------------------\n"); "\n ------------------------------------------\n |");
ret += trace_seq_printf(s, " | %d) %s-%d => %s-%d\n", if (!ret)
cpu, prev_comm, prev_pid, comm, pid); TRACE_TYPE_PARTIAL_LINE;
ret += trace_seq_printf(s,
" ------------------------------------------\n\n"); ret = print_graph_cpu(s, cpu);
if (ret == TRACE_TYPE_PARTIAL_LINE)
TRACE_TYPE_PARTIAL_LINE;
ret = print_graph_proc(s, prev_pid);
if (ret == TRACE_TYPE_PARTIAL_LINE)
TRACE_TYPE_PARTIAL_LINE;
ret = trace_seq_printf(s, " => ");
if (!ret)
TRACE_TYPE_PARTIAL_LINE;
ret = print_graph_proc(s, pid);
if (ret == TRACE_TYPE_PARTIAL_LINE)
TRACE_TYPE_PARTIAL_LINE;
ret = trace_seq_printf(s,
"\n ------------------------------------------\n\n");
if (!ret)
TRACE_TYPE_PARTIAL_LINE;
return ret; return ret;
} }
...@@ -169,11 +232,50 @@ trace_branch_is_leaf(struct trace_iterator *iter, ...@@ -169,11 +232,50 @@ trace_branch_is_leaf(struct trace_iterator *iter,
} }
static inline int static enum print_line_t
print_graph_duration(unsigned long long duration, struct trace_seq *s) print_graph_duration(unsigned long long duration, struct trace_seq *s)
{ {
unsigned long nsecs_rem = do_div(duration, 1000); unsigned long nsecs_rem = do_div(duration, 1000);
return trace_seq_printf(s, "%4llu.%3lu us | ", duration, nsecs_rem); /* log10(ULONG_MAX) + '\0' */
char msecs_str[21];
char nsecs_str[5];
int ret, len;
int i;
sprintf(msecs_str, "%lu", (unsigned long) duration);
/* Print msecs */
ret = trace_seq_printf(s, msecs_str);
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
len = strlen(msecs_str);
/* Print nsecs (we don't want to exceed 7 numbers) */
if (len < 7) {
snprintf(nsecs_str, 8 - len, "%03lu", nsecs_rem);
ret = trace_seq_printf(s, ".%s", nsecs_str);
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
len += strlen(nsecs_str);
}
ret = trace_seq_printf(s, " us ");
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
/* Print remaining spaces to fit the row's width */
for (i = len; i < 7; i++) {
ret = trace_seq_printf(s, " ");
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
}
ret = trace_seq_printf(s, "| ");
if (!ret)
return TRACE_TYPE_PARTIAL_LINE;
return TRACE_TYPE_HANDLED;
} }
/* Signal a overhead of time execution to the output */ /* Signal a overhead of time execution to the output */
...@@ -210,10 +312,6 @@ print_graph_entry_leaf(struct trace_iterator *iter, ...@@ -210,10 +312,6 @@ print_graph_entry_leaf(struct trace_iterator *iter,
call = &entry->graph_ent; call = &entry->graph_ent;
duration = graph_ret->rettime - graph_ret->calltime; duration = graph_ret->rettime - graph_ret->calltime;
/* Must not exceed 8 characters: 9999.999 us */
if (duration > 10000000ULL)
duration = 9999999ULL;
/* Overhead */ /* Overhead */
if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) { if (tracer_flags.val & TRACE_GRAPH_PRINT_OVERHEAD) {
ret = print_graph_overhead(duration, s); ret = print_graph_overhead(duration, s);
...@@ -223,7 +321,7 @@ print_graph_entry_leaf(struct trace_iterator *iter, ...@@ -223,7 +321,7 @@ print_graph_entry_leaf(struct trace_iterator *iter,
/* Duration */ /* Duration */
ret = print_graph_duration(duration, s); ret = print_graph_duration(duration, s);
if (!ret) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Function */ /* Function */
...@@ -288,12 +386,23 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s, ...@@ -288,12 +386,23 @@ print_graph_entry(struct ftrace_graph_ent_entry *field, struct trace_seq *s,
struct trace_entry *ent = iter->ent; struct trace_entry *ent = iter->ent;
/* Pid */ /* Pid */
if (!verif_pid(s, ent->pid, cpu)) if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Cpu */ /* Cpu */
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
ret = print_graph_cpu(s, cpu); ret = print_graph_cpu(s, cpu);
if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE;
}
/* Proc */
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
ret = print_graph_proc(s, ent->pid);
if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE;
ret = trace_seq_printf(s, " | ");
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
...@@ -313,17 +422,24 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, ...@@ -313,17 +422,24 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
int ret; int ret;
unsigned long long duration = trace->rettime - trace->calltime; unsigned long long duration = trace->rettime - trace->calltime;
/* Must not exceed 8 characters: xxxx.yyy us */
if (duration > 10000000ULL)
duration = 9999999ULL;
/* Pid */ /* Pid */
if (!verif_pid(s, ent->pid, cpu)) if (verif_pid(s, ent->pid, cpu) == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Cpu */ /* Cpu */
if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) { if (tracer_flags.val & TRACE_GRAPH_PRINT_CPU) {
ret = print_graph_cpu(s, cpu); ret = print_graph_cpu(s, cpu);
if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE;
}
/* Proc */
if (tracer_flags.val & TRACE_GRAPH_PRINT_PROC) {
ret = print_graph_proc(s, ent->pid);
if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE;
ret = trace_seq_printf(s, " | ");
if (!ret) if (!ret)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
} }
...@@ -337,7 +453,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s, ...@@ -337,7 +453,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
/* Duration */ /* Duration */
ret = print_graph_duration(duration, s); ret = print_graph_duration(duration, s);
if (!ret) if (ret == TRACE_TYPE_PARTIAL_LINE)
return TRACE_TYPE_PARTIAL_LINE; return TRACE_TYPE_PARTIAL_LINE;
/* Closing brace */ /* Closing brace */
......
...@@ -48,7 +48,7 @@ static inline void check_stack(void) ...@@ -48,7 +48,7 @@ static inline void check_stack(void)
if (!object_is_on_stack(&this_size)) if (!object_is_on_stack(&this_size))
return; return;
raw_local_irq_save(flags); local_irq_save(flags);
__raw_spin_lock(&max_stack_lock); __raw_spin_lock(&max_stack_lock);
/* a race could have already updated it */ /* a race could have already updated it */
...@@ -78,6 +78,7 @@ static inline void check_stack(void) ...@@ -78,6 +78,7 @@ static inline void check_stack(void)
* on a new max, so it is far from a fast path. * on a new max, so it is far from a fast path.
*/ */
while (i < max_stack_trace.nr_entries) { while (i < max_stack_trace.nr_entries) {
int found = 0;
stack_dump_index[i] = this_size; stack_dump_index[i] = this_size;
p = start; p = start;
...@@ -86,17 +87,19 @@ static inline void check_stack(void) ...@@ -86,17 +87,19 @@ static inline void check_stack(void)
if (*p == stack_dump_trace[i]) { if (*p == stack_dump_trace[i]) {
this_size = stack_dump_index[i++] = this_size = stack_dump_index[i++] =
(top - p) * sizeof(unsigned long); (top - p) * sizeof(unsigned long);
found = 1;
/* Start the search from here */ /* Start the search from here */
start = p + 1; start = p + 1;
} }
} }
i++; if (!found)
i++;
} }
out: out:
__raw_spin_unlock(&max_stack_lock); __raw_spin_unlock(&max_stack_lock);
raw_local_irq_restore(flags); local_irq_restore(flags);
} }
static void static void
...@@ -162,11 +165,11 @@ stack_max_size_write(struct file *filp, const char __user *ubuf, ...@@ -162,11 +165,11 @@ stack_max_size_write(struct file *filp, const char __user *ubuf,
if (ret < 0) if (ret < 0)
return ret; return ret;
raw_local_irq_save(flags); local_irq_save(flags);
__raw_spin_lock(&max_stack_lock); __raw_spin_lock(&max_stack_lock);
*ptr = val; *ptr = val;
__raw_spin_unlock(&max_stack_lock); __raw_spin_unlock(&max_stack_lock);
raw_local_irq_restore(flags); local_irq_restore(flags);
return count; return count;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment