You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'tracing-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'tracing-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: (413 commits) tracing, net: fix net tree and tracing tree merge interaction tracing, powerpc: fix powerpc tree and tracing tree interaction ring-buffer: do not remove reader page from list on ring buffer free function-graph: allow unregistering twice trace: make argument 'mem' of trace_seq_putmem() const tracing: add missing 'extern' keywords to trace_output.h tracing: provide trace_seq_reserve() blktrace: print out BLK_TN_MESSAGE properly blktrace: extract duplidate code blktrace: fix memory leak when freeing struct blk_io_trace blktrace: fix blk_probes_ref chaos blktrace: make classic output more classic blktrace: fix off-by-one bug blktrace: fix the original blktrace blktrace: fix a race when creating blk_tree_root in debugfs blktrace: fix timestamp in binary output tracing, Text Edit Lock: cleanup tracing: filter fix for TRACE_EVENT_FORMAT events ftrace: Using FTRACE_WARN_ON() to check "freed record" in ftrace_release() x86: kretprobe-booster interrupt emulation code fix ... Fix up trivial conflicts in arch/parisc/include/asm/ftrace.h include/linux/memory.h kernel/extable.c kernel/module.c
This commit is contained in:
+16
-5
@@ -15,11 +15,22 @@
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <linux/memory.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* mutex protecting text section modification (dynamic code patching).
|
||||
* some users need to sleep (allocating memory...) while they hold this lock.
|
||||
*
|
||||
* NOT exported to modules - patching kernel text is a really delicate matter.
|
||||
*/
|
||||
DEFINE_MUTEX(text_mutex);
|
||||
|
||||
extern struct exception_table_entry __start___ex_table[];
|
||||
extern struct exception_table_entry __stop___ex_table[];
|
||||
@@ -49,7 +60,7 @@ static inline int init_kernel_text(unsigned long addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
__notrace_funcgraph int core_kernel_text(unsigned long addr)
|
||||
int core_kernel_text(unsigned long addr)
|
||||
{
|
||||
if (addr >= (unsigned long)_stext &&
|
||||
addr <= (unsigned long)_etext)
|
||||
@@ -61,7 +72,7 @@ __notrace_funcgraph int core_kernel_text(unsigned long addr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
__notrace_funcgraph int __kernel_text_address(unsigned long addr)
|
||||
int __kernel_text_address(unsigned long addr)
|
||||
{
|
||||
if (core_kernel_text(addr))
|
||||
return 1;
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/hash.h>
|
||||
#include <trace/irq.h>
|
||||
#include <linux/bootmem.h>
|
||||
|
||||
#include "internals.h"
|
||||
@@ -338,6 +339,9 @@ irqreturn_t no_action(int cpl, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
DEFINE_TRACE(irq_handler_entry);
|
||||
DEFINE_TRACE(irq_handler_exit);
|
||||
|
||||
/**
|
||||
* handle_IRQ_event - irq action chain handler
|
||||
* @irq: the interrupt number
|
||||
@@ -356,7 +360,9 @@ irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)
|
||||
local_irq_enable_in_hardirq();
|
||||
|
||||
do {
|
||||
trace_irq_handler_entry(irq, action);
|
||||
ret = action->handler(irq, action->dev_id);
|
||||
trace_irq_handler_exit(irq, action, ret);
|
||||
if (ret == IRQ_HANDLED)
|
||||
status |= action->flags;
|
||||
retval |= ret;
|
||||
|
||||
+14
-5
@@ -43,6 +43,7 @@
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/kdebug.h>
|
||||
#include <linux/memory.h>
|
||||
|
||||
#include <asm-generic/sections.h>
|
||||
#include <asm/cacheflush.h>
|
||||
@@ -699,9 +700,10 @@ int __kprobes register_kprobe(struct kprobe *p)
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&text_mutex);
|
||||
ret = arch_prepare_kprobe(p);
|
||||
if (ret)
|
||||
goto out;
|
||||
goto out_unlock_text;
|
||||
|
||||
INIT_HLIST_NODE(&p->hlist);
|
||||
hlist_add_head_rcu(&p->hlist,
|
||||
@@ -710,6 +712,8 @@ int __kprobes register_kprobe(struct kprobe *p)
|
||||
if (kprobe_enabled)
|
||||
arch_arm_kprobe(p);
|
||||
|
||||
out_unlock_text:
|
||||
mutex_unlock(&text_mutex);
|
||||
out:
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
|
||||
@@ -746,8 +750,11 @@ valid_p:
|
||||
* enabled and not gone - otherwise, the breakpoint would
|
||||
* already have been removed. We save on flushing icache.
|
||||
*/
|
||||
if (kprobe_enabled && !kprobe_gone(old_p))
|
||||
if (kprobe_enabled && !kprobe_gone(old_p)) {
|
||||
mutex_lock(&text_mutex);
|
||||
arch_disarm_kprobe(p);
|
||||
mutex_unlock(&text_mutex);
|
||||
}
|
||||
hlist_del_rcu(&old_p->hlist);
|
||||
} else {
|
||||
if (p->break_handler && !kprobe_gone(p))
|
||||
@@ -912,10 +919,8 @@ static int __kprobes pre_handler_kretprobe(struct kprobe *p,
|
||||
ri->rp = rp;
|
||||
ri->task = current;
|
||||
|
||||
if (rp->entry_handler && rp->entry_handler(ri, regs)) {
|
||||
spin_unlock_irqrestore(&rp->lock, flags);
|
||||
if (rp->entry_handler && rp->entry_handler(ri, regs))
|
||||
return 0;
|
||||
}
|
||||
|
||||
arch_prepare_kretprobe(ri, regs);
|
||||
|
||||
@@ -1280,12 +1285,14 @@ static void __kprobes enable_all_kprobes(void)
|
||||
if (kprobe_enabled)
|
||||
goto already_enabled;
|
||||
|
||||
mutex_lock(&text_mutex);
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, node, head, hlist)
|
||||
if (!kprobe_gone(p))
|
||||
arch_arm_kprobe(p);
|
||||
}
|
||||
mutex_unlock(&text_mutex);
|
||||
|
||||
kprobe_enabled = true;
|
||||
printk(KERN_INFO "Kprobes globally enabled\n");
|
||||
@@ -1310,6 +1317,7 @@ static void __kprobes disable_all_kprobes(void)
|
||||
|
||||
kprobe_enabled = false;
|
||||
printk(KERN_INFO "Kprobes globally disabled\n");
|
||||
mutex_lock(&text_mutex);
|
||||
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
|
||||
head = &kprobe_table[i];
|
||||
hlist_for_each_entry_rcu(p, node, head, hlist) {
|
||||
@@ -1318,6 +1326,7 @@ static void __kprobes disable_all_kprobes(void)
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&text_mutex);
|
||||
mutex_unlock(&kprobe_mutex);
|
||||
/* Allow all currently running kprobes to complete */
|
||||
synchronize_sched();
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
#include <linux/hash.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/stringify.h>
|
||||
#include <trace/lockdep.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
|
||||
@@ -2923,6 +2924,8 @@ void lock_set_class(struct lockdep_map *lock, const char *name,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lock_set_class);
|
||||
|
||||
DEFINE_TRACE(lock_acquire);
|
||||
|
||||
/*
|
||||
* We are not always called with irqs disabled - do that here,
|
||||
* and also avoid lockdep recursion:
|
||||
@@ -2933,6 +2936,8 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
trace_lock_acquire(lock, subclass, trylock, read, check, nest_lock, ip);
|
||||
|
||||
if (unlikely(current->lockdep_recursion))
|
||||
return;
|
||||
|
||||
@@ -2947,11 +2952,15 @@ void lock_acquire(struct lockdep_map *lock, unsigned int subclass,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lock_acquire);
|
||||
|
||||
DEFINE_TRACE(lock_release);
|
||||
|
||||
void lock_release(struct lockdep_map *lock, int nested,
|
||||
unsigned long ip)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
trace_lock_release(lock, nested, ip);
|
||||
|
||||
if (unlikely(current->lockdep_recursion))
|
||||
return;
|
||||
|
||||
@@ -3100,10 +3109,14 @@ found_it:
|
||||
lock->ip = ip;
|
||||
}
|
||||
|
||||
DEFINE_TRACE(lock_contended);
|
||||
|
||||
void lock_contended(struct lockdep_map *lock, unsigned long ip)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
trace_lock_contended(lock, ip);
|
||||
|
||||
if (unlikely(!lock_stat))
|
||||
return;
|
||||
|
||||
@@ -3119,10 +3132,14 @@ void lock_contended(struct lockdep_map *lock, unsigned long ip)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lock_contended);
|
||||
|
||||
DEFINE_TRACE(lock_acquired);
|
||||
|
||||
void lock_acquired(struct lockdep_map *lock, unsigned long ip)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
trace_lock_acquired(lock, ip);
|
||||
|
||||
if (unlikely(!lock_stat))
|
||||
return;
|
||||
|
||||
|
||||
+1
-1
@@ -2777,7 +2777,7 @@ bool is_module_address(unsigned long addr)
|
||||
* Must be called with preempt disabled or module mutex held so that
|
||||
* module doesn't get freed during this.
|
||||
*/
|
||||
__notrace_funcgraph struct module *__module_address(unsigned long addr)
|
||||
struct module *__module_address(unsigned long addr)
|
||||
{
|
||||
struct module *mod;
|
||||
|
||||
|
||||
+1
-3
@@ -677,9 +677,7 @@ int relay_late_setup_files(struct rchan *chan,
|
||||
*/
|
||||
for_each_online_cpu(i) {
|
||||
if (unlikely(!chan->buf[i])) {
|
||||
printk(KERN_ERR "relay_late_setup_files: CPU %u "
|
||||
"has no buffer, it must have!\n", i);
|
||||
BUG();
|
||||
WARN_ONCE(1, KERN_ERR "CPU has no buffer!\n");
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
+4
-4
@@ -4773,10 +4773,7 @@ void scheduler_tick(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
|
||||
defined(CONFIG_PREEMPT_TRACER))
|
||||
|
||||
static inline unsigned long get_parent_ip(unsigned long addr)
|
||||
unsigned long get_parent_ip(unsigned long addr)
|
||||
{
|
||||
if (in_lock_functions(addr)) {
|
||||
addr = CALLER_ADDR2;
|
||||
@@ -4786,6 +4783,9 @@ static inline unsigned long get_parent_ip(unsigned long addr)
|
||||
return addr;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PREEMPT) && (defined(CONFIG_DEBUG_PREEMPT) || \
|
||||
defined(CONFIG_PREEMPT_TRACER))
|
||||
|
||||
void __kprobes add_preempt_count(int val)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_PREEMPT
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
* consistent between cpus (never more than 2 jiffies difference).
|
||||
*/
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/hardirq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/ktime.h>
|
||||
@@ -154,6 +155,17 @@ u64 sched_clock_cpu(int cpu)
|
||||
return sched_clock();
|
||||
|
||||
scd = cpu_sdc(cpu);
|
||||
|
||||
/*
|
||||
* Normally this is not called in NMI context - but if it is,
|
||||
* trying to do any locking here is totally lethal.
|
||||
*/
|
||||
if (unlikely(in_nmi()))
|
||||
return scd->clock;
|
||||
|
||||
if (unlikely(!sched_clock_running))
|
||||
return 0ull;
|
||||
|
||||
WARN_ON_ONCE(!irqs_disabled());
|
||||
now = sched_clock();
|
||||
|
||||
|
||||
+25
-3
@@ -21,8 +21,10 @@
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/tick.h>
|
||||
#include <trace/irq.h>
|
||||
|
||||
#include <asm/irq.h>
|
||||
/*
|
||||
@@ -52,6 +54,11 @@ static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp
|
||||
|
||||
static DEFINE_PER_CPU(struct task_struct *, ksoftirqd);
|
||||
|
||||
char *softirq_to_name[NR_SOFTIRQS] = {
|
||||
"HI", "TIMER", "NET_TX", "NET_RX", "BLOCK",
|
||||
"TASKLET", "SCHED", "HRTIMER", "RCU"
|
||||
};
|
||||
|
||||
/*
|
||||
* we cannot loop indefinitely here to avoid userspace starvation,
|
||||
* but we also don't want to introduce a worst case 1/HZ latency
|
||||
@@ -79,13 +86,23 @@ static void __local_bh_disable(unsigned long ip)
|
||||
WARN_ON_ONCE(in_irq());
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
add_preempt_count(SOFTIRQ_OFFSET);
|
||||
/*
|
||||
* The preempt tracer hooks into add_preempt_count and will break
|
||||
* lockdep because it calls back into lockdep after SOFTIRQ_OFFSET
|
||||
* is set and before current->softirq_enabled is cleared.
|
||||
* We must manually increment preempt_count here and manually
|
||||
* call the trace_preempt_off later.
|
||||
*/
|
||||
preempt_count() += SOFTIRQ_OFFSET;
|
||||
/*
|
||||
* Were softirqs turned off above:
|
||||
*/
|
||||
if (softirq_count() == SOFTIRQ_OFFSET)
|
||||
trace_softirqs_off(ip);
|
||||
raw_local_irq_restore(flags);
|
||||
|
||||
if (preempt_count() == SOFTIRQ_OFFSET)
|
||||
trace_preempt_off(CALLER_ADDR0, get_parent_ip(CALLER_ADDR1));
|
||||
}
|
||||
#else /* !CONFIG_TRACE_IRQFLAGS */
|
||||
static inline void __local_bh_disable(unsigned long ip)
|
||||
@@ -169,6 +186,9 @@ EXPORT_SYMBOL(local_bh_enable_ip);
|
||||
*/
|
||||
#define MAX_SOFTIRQ_RESTART 10
|
||||
|
||||
DEFINE_TRACE(softirq_entry);
|
||||
DEFINE_TRACE(softirq_exit);
|
||||
|
||||
asmlinkage void __do_softirq(void)
|
||||
{
|
||||
struct softirq_action *h;
|
||||
@@ -195,12 +215,14 @@ restart:
|
||||
if (pending & 1) {
|
||||
int prev_count = preempt_count();
|
||||
|
||||
trace_softirq_entry(h, softirq_vec);
|
||||
h->action(h);
|
||||
|
||||
trace_softirq_exit(h, softirq_vec);
|
||||
if (unlikely(prev_count != preempt_count())) {
|
||||
printk(KERN_ERR "huh, entered softirq %td %p"
|
||||
printk(KERN_ERR "huh, entered softirq %td %s %p"
|
||||
"with preempt_count %08x,"
|
||||
" exited with %08x?\n", h - softirq_vec,
|
||||
softirq_to_name[h - softirq_vec],
|
||||
h->action, prev_count, preempt_count());
|
||||
preempt_count() = prev_count;
|
||||
}
|
||||
|
||||
+108
-15
@@ -9,6 +9,9 @@ config USER_STACKTRACE_SUPPORT
|
||||
config NOP_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FTRACE_NMI_ENTER
|
||||
bool
|
||||
|
||||
config HAVE_FUNCTION_TRACER
|
||||
bool
|
||||
|
||||
@@ -31,12 +34,20 @@ config HAVE_FTRACE_MCOUNT_RECORD
|
||||
config HAVE_HW_BRANCH_TRACER
|
||||
bool
|
||||
|
||||
config HAVE_FTRACE_SYSCALLS
|
||||
bool
|
||||
|
||||
config TRACER_MAX_TRACE
|
||||
bool
|
||||
|
||||
config RING_BUFFER
|
||||
bool
|
||||
|
||||
config FTRACE_NMI_ENTER
|
||||
bool
|
||||
depends on HAVE_FTRACE_NMI_ENTER
|
||||
default y
|
||||
|
||||
config TRACING
|
||||
bool
|
||||
select DEBUG_FS
|
||||
@@ -44,13 +55,29 @@ config TRACING
|
||||
select STACKTRACE if STACKTRACE_SUPPORT
|
||||
select TRACEPOINTS
|
||||
select NOP_TRACER
|
||||
select BINARY_PRINTF
|
||||
|
||||
#
|
||||
# Minimum requirements an architecture has to meet for us to
|
||||
# be able to offer generic tracing facilities:
|
||||
#
|
||||
config TRACING_SUPPORT
|
||||
bool
|
||||
# PPC32 has no irqflags tracing support, but it can use most of the
|
||||
# tracers anyway, they were tested to build and work. Note that new
|
||||
# exceptions to this list aren't welcomed, better implement the
|
||||
# irqflags tracing for your architecture.
|
||||
depends on TRACE_IRQFLAGS_SUPPORT || PPC32
|
||||
depends on STACKTRACE_SUPPORT
|
||||
default y
|
||||
|
||||
if TRACING_SUPPORT
|
||||
|
||||
menu "Tracers"
|
||||
|
||||
config FUNCTION_TRACER
|
||||
bool "Kernel Function Tracer"
|
||||
depends on HAVE_FUNCTION_TRACER
|
||||
depends on DEBUG_KERNEL
|
||||
select FRAME_POINTER
|
||||
select KALLSYMS
|
||||
select TRACING
|
||||
@@ -82,7 +109,6 @@ config IRQSOFF_TRACER
|
||||
default n
|
||||
depends on TRACE_IRQFLAGS_SUPPORT
|
||||
depends on GENERIC_TIME
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACE_IRQFLAGS
|
||||
select TRACING
|
||||
select TRACER_MAX_TRACE
|
||||
@@ -105,7 +131,6 @@ config PREEMPT_TRACER
|
||||
default n
|
||||
depends on GENERIC_TIME
|
||||
depends on PREEMPT
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACING
|
||||
select TRACER_MAX_TRACE
|
||||
help
|
||||
@@ -126,13 +151,13 @@ config SYSPROF_TRACER
|
||||
bool "Sysprof Tracer"
|
||||
depends on X86
|
||||
select TRACING
|
||||
select CONTEXT_SWITCH_TRACER
|
||||
help
|
||||
This tracer provides the trace needed by the 'Sysprof' userspace
|
||||
tool.
|
||||
|
||||
config SCHED_TRACER
|
||||
bool "Scheduling Latency Tracer"
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACING
|
||||
select CONTEXT_SWITCH_TRACER
|
||||
select TRACER_MAX_TRACE
|
||||
@@ -142,16 +167,30 @@ config SCHED_TRACER
|
||||
|
||||
config CONTEXT_SWITCH_TRACER
|
||||
bool "Trace process context switches"
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACING
|
||||
select MARKERS
|
||||
help
|
||||
This tracer gets called from the context switch and records
|
||||
all switching of tasks.
|
||||
|
||||
config EVENT_TRACER
|
||||
bool "Trace various events in the kernel"
|
||||
select TRACING
|
||||
help
|
||||
This tracer hooks to various trace points in the kernel
|
||||
allowing the user to pick and choose which trace point they
|
||||
want to trace.
|
||||
|
||||
config FTRACE_SYSCALLS
|
||||
bool "Trace syscalls"
|
||||
depends on HAVE_FTRACE_SYSCALLS
|
||||
select TRACING
|
||||
select KALLSYMS
|
||||
help
|
||||
Basic tracer to catch the syscall entry and exit events.
|
||||
|
||||
config BOOT_TRACER
|
||||
bool "Trace boot initcalls"
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACING
|
||||
select CONTEXT_SWITCH_TRACER
|
||||
help
|
||||
@@ -164,13 +203,11 @@ config BOOT_TRACER
|
||||
representation of the delays during initcalls - but the raw
|
||||
/debug/tracing/trace text output is readable too.
|
||||
|
||||
( Note that tracing self tests can't be enabled if this tracer is
|
||||
selected, because the self-tests are an initcall as well and that
|
||||
would invalidate the boot trace. )
|
||||
You must pass in ftrace=initcall to the kernel command line
|
||||
to enable this on bootup.
|
||||
|
||||
config TRACE_BRANCH_PROFILING
|
||||
bool "Trace likely/unlikely profiler"
|
||||
depends on DEBUG_KERNEL
|
||||
select TRACING
|
||||
help
|
||||
This tracer profiles all the the likely and unlikely macros
|
||||
@@ -223,7 +260,6 @@ config BRANCH_TRACER
|
||||
|
||||
config POWER_TRACER
|
||||
bool "Trace power consumption behavior"
|
||||
depends on DEBUG_KERNEL
|
||||
depends on X86
|
||||
select TRACING
|
||||
help
|
||||
@@ -235,7 +271,6 @@ config POWER_TRACER
|
||||
config STACK_TRACER
|
||||
bool "Trace max stack"
|
||||
depends on HAVE_FUNCTION_TRACER
|
||||
depends on DEBUG_KERNEL
|
||||
select FUNCTION_TRACER
|
||||
select STACKTRACE
|
||||
select KALLSYMS
|
||||
@@ -265,11 +300,66 @@ config HW_BRANCH_TRACER
|
||||
This tracer records all branches on the system in a circular
|
||||
buffer giving access to the last N branches for each cpu.
|
||||
|
||||
config KMEMTRACE
|
||||
bool "Trace SLAB allocations"
|
||||
select TRACING
|
||||
help
|
||||
kmemtrace provides tracing for slab allocator functions, such as
|
||||
kmalloc, kfree, kmem_cache_alloc, kmem_cache_free etc.. Collected
|
||||
data is then fed to the userspace application in order to analyse
|
||||
allocation hotspots, internal fragmentation and so on, making it
|
||||
possible to see how well an allocator performs, as well as debug
|
||||
and profile kernel code.
|
||||
|
||||
This requires an userspace application to use. See
|
||||
Documentation/vm/kmemtrace.txt for more information.
|
||||
|
||||
Saying Y will make the kernel somewhat larger and slower. However,
|
||||
if you disable kmemtrace at run-time or boot-time, the performance
|
||||
impact is minimal (depending on the arch the kernel is built for).
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config WORKQUEUE_TRACER
|
||||
bool "Trace workqueues"
|
||||
select TRACING
|
||||
help
|
||||
The workqueue tracer provides some statistical informations
|
||||
about each cpu workqueue thread such as the number of the
|
||||
works inserted and executed since their creation. It can help
|
||||
to evaluate the amount of work each of them have to perform.
|
||||
For example it can help a developer to decide whether he should
|
||||
choose a per cpu workqueue instead of a singlethreaded one.
|
||||
|
||||
config BLK_DEV_IO_TRACE
|
||||
bool "Support for tracing block io actions"
|
||||
depends on SYSFS
|
||||
depends on BLOCK
|
||||
select RELAY
|
||||
select DEBUG_FS
|
||||
select TRACEPOINTS
|
||||
select TRACING
|
||||
select STACKTRACE
|
||||
help
|
||||
Say Y here if you want to be able to trace the block layer actions
|
||||
on a given queue. Tracing allows you to see any traffic happening
|
||||
on a block device queue. For more information (and the userspace
|
||||
support tools needed), fetch the blktrace tools from:
|
||||
|
||||
git://git.kernel.dk/blktrace.git
|
||||
|
||||
Tracing also is possible using the ftrace interface, e.g.:
|
||||
|
||||
echo 1 > /sys/block/sda/sda1/trace/enable
|
||||
echo blk > /sys/kernel/debug/tracing/current_tracer
|
||||
cat /sys/kernel/debug/tracing/trace_pipe
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config DYNAMIC_FTRACE
|
||||
bool "enable/disable ftrace tracepoints dynamically"
|
||||
depends on FUNCTION_TRACER
|
||||
depends on HAVE_DYNAMIC_FTRACE
|
||||
depends on DEBUG_KERNEL
|
||||
default y
|
||||
help
|
||||
This option will modify all the calls to ftrace dynamically
|
||||
@@ -295,7 +385,7 @@ config FTRACE_SELFTEST
|
||||
|
||||
config FTRACE_STARTUP_TEST
|
||||
bool "Perform a startup test on ftrace"
|
||||
depends on TRACING && DEBUG_KERNEL && !BOOT_TRACER
|
||||
depends on TRACING
|
||||
select FTRACE_SELFTEST
|
||||
help
|
||||
This option performs a series of startup tests on ftrace. On bootup
|
||||
@@ -305,7 +395,7 @@ config FTRACE_STARTUP_TEST
|
||||
|
||||
config MMIOTRACE
|
||||
bool "Memory mapped IO tracing"
|
||||
depends on HAVE_MMIOTRACE_SUPPORT && DEBUG_KERNEL && PCI
|
||||
depends on HAVE_MMIOTRACE_SUPPORT && PCI
|
||||
select TRACING
|
||||
help
|
||||
Mmiotrace traces Memory Mapped I/O access and is meant for
|
||||
@@ -327,3 +417,6 @@ config MMIOTRACE_TEST
|
||||
Say N, unless you absolutely know what you are doing.
|
||||
|
||||
endmenu
|
||||
|
||||
endif # TRACING_SUPPORT
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o
|
||||
obj-$(CONFIG_RING_BUFFER) += ring_buffer.o
|
||||
|
||||
obj-$(CONFIG_TRACING) += trace.o
|
||||
obj-$(CONFIG_TRACING) += trace_clock.o
|
||||
obj-$(CONFIG_TRACING) += trace_output.o
|
||||
obj-$(CONFIG_TRACING) += trace_stat.o
|
||||
obj-$(CONFIG_TRACING) += trace_printk.o
|
||||
obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
|
||||
obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o
|
||||
@@ -33,5 +37,14 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o
|
||||
obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o
|
||||
obj-$(CONFIG_HW_BRANCH_TRACER) += trace_hw_branches.o
|
||||
obj-$(CONFIG_POWER_TRACER) += trace_power.o
|
||||
obj-$(CONFIG_KMEMTRACE) += kmemtrace.o
|
||||
obj-$(CONFIG_WORKQUEUE_TRACER) += trace_workqueue.o
|
||||
obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o
|
||||
obj-$(CONFIG_EVENT_TRACER) += trace_events.o
|
||||
obj-$(CONFIG_EVENT_TRACER) += events.o
|
||||
obj-$(CONFIG_EVENT_TRACER) += trace_export.o
|
||||
obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
|
||||
obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o
|
||||
obj-$(CONFIG_EVENT_TRACER) += trace_events_filter.o
|
||||
|
||||
libftrace-y := ftrace.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* This is the place to register all trace points as events.
|
||||
*/
|
||||
|
||||
#include <linux/stringify.h>
|
||||
|
||||
#include <trace/trace_events.h>
|
||||
|
||||
#include "trace_output.h"
|
||||
|
||||
#include "trace_events_stage_1.h"
|
||||
#include "trace_events_stage_2.h"
|
||||
#include "trace_events_stage_3.h"
|
||||
|
||||
+876
-257
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,339 @@
|
||||
/*
|
||||
* Memory allocator tracing
|
||||
*
|
||||
* Copyright (C) 2008 Eduard - Gabriel Munteanu
|
||||
* Copyright (C) 2008 Pekka Enberg <penberg@cs.helsinki.fi>
|
||||
* Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/dcache.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <trace/kmemtrace.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "trace_output.h"
|
||||
|
||||
/* Select an alternative, minimalistic output than the original one */
|
||||
#define TRACE_KMEM_OPT_MINIMAL 0x1
|
||||
|
||||
static struct tracer_opt kmem_opts[] = {
|
||||
/* Default disable the minimalistic output */
|
||||
{ TRACER_OPT(kmem_minimalistic, TRACE_KMEM_OPT_MINIMAL) },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct tracer_flags kmem_tracer_flags = {
|
||||
.val = 0,
|
||||
.opts = kmem_opts
|
||||
};
|
||||
|
||||
|
||||
static bool kmem_tracing_enabled __read_mostly;
|
||||
static struct trace_array *kmemtrace_array;
|
||||
|
||||
static int kmem_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
kmemtrace_array = tr;
|
||||
|
||||
for_each_cpu_mask(cpu, cpu_possible_map)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
kmem_tracing_enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kmem_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
kmem_tracing_enabled = false;
|
||||
}
|
||||
|
||||
static void kmemtrace_headers(struct seq_file *s)
|
||||
{
|
||||
/* Don't need headers for the original kmemtrace output */
|
||||
if (!(kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL))
|
||||
return;
|
||||
|
||||
seq_printf(s, "#\n");
|
||||
seq_printf(s, "# ALLOC TYPE REQ GIVEN FLAGS "
|
||||
" POINTER NODE CALLER\n");
|
||||
seq_printf(s, "# FREE | | | | "
|
||||
" | | | |\n");
|
||||
seq_printf(s, "# |\n\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* The two following functions give the original output from kmemtrace,
|
||||
* or something close to....perhaps they need some missing things
|
||||
*/
|
||||
static enum print_line_t
|
||||
kmemtrace_print_alloc_original(struct trace_iterator *iter,
|
||||
struct kmemtrace_alloc_entry *entry)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int ret;
|
||||
|
||||
/* Taken from the old linux/kmemtrace.h */
|
||||
ret = trace_seq_printf(s, "type_id %d call_site %lu ptr %lu "
|
||||
"bytes_req %lu bytes_alloc %lu gfp_flags %lu node %d\n",
|
||||
entry->type_id, entry->call_site, (unsigned long) entry->ptr,
|
||||
(unsigned long) entry->bytes_req, (unsigned long) entry->bytes_alloc,
|
||||
(unsigned long) entry->gfp_flags, entry->node);
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
kmemtrace_print_free_original(struct trace_iterator *iter,
|
||||
struct kmemtrace_free_entry *entry)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int ret;
|
||||
|
||||
/* Taken from the old linux/kmemtrace.h */
|
||||
ret = trace_seq_printf(s, "type_id %d call_site %lu ptr %lu\n",
|
||||
entry->type_id, entry->call_site, (unsigned long) entry->ptr);
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/* The two other following provide a more minimalistic output */
|
||||
static enum print_line_t
|
||||
kmemtrace_print_alloc_compress(struct trace_iterator *iter,
|
||||
struct kmemtrace_alloc_entry *entry)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int ret;
|
||||
|
||||
/* Alloc entry */
|
||||
ret = trace_seq_printf(s, " + ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Type */
|
||||
switch (entry->type_id) {
|
||||
case KMEMTRACE_TYPE_KMALLOC:
|
||||
ret = trace_seq_printf(s, "K ");
|
||||
break;
|
||||
case KMEMTRACE_TYPE_CACHE:
|
||||
ret = trace_seq_printf(s, "C ");
|
||||
break;
|
||||
case KMEMTRACE_TYPE_PAGES:
|
||||
ret = trace_seq_printf(s, "P ");
|
||||
break;
|
||||
default:
|
||||
ret = trace_seq_printf(s, "? ");
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Requested */
|
||||
ret = trace_seq_printf(s, "%4zu ", entry->bytes_req);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Allocated */
|
||||
ret = trace_seq_printf(s, "%4zu ", entry->bytes_alloc);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Flags
|
||||
* TODO: would be better to see the name of the GFP flag names
|
||||
*/
|
||||
ret = trace_seq_printf(s, "%08x ", entry->gfp_flags);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Pointer to allocated */
|
||||
ret = trace_seq_printf(s, "0x%tx ", (ptrdiff_t)entry->ptr);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Node */
|
||||
ret = trace_seq_printf(s, "%4d ", entry->node);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Call site */
|
||||
ret = seq_print_ip_sym(s, entry->call_site, 0);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
if (!trace_seq_printf(s, "\n"))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t
|
||||
kmemtrace_print_free_compress(struct trace_iterator *iter,
|
||||
struct kmemtrace_free_entry *entry)
|
||||
{
|
||||
struct trace_seq *s = &iter->seq;
|
||||
int ret;
|
||||
|
||||
/* Free entry */
|
||||
ret = trace_seq_printf(s, " - ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Type */
|
||||
switch (entry->type_id) {
|
||||
case KMEMTRACE_TYPE_KMALLOC:
|
||||
ret = trace_seq_printf(s, "K ");
|
||||
break;
|
||||
case KMEMTRACE_TYPE_CACHE:
|
||||
ret = trace_seq_printf(s, "C ");
|
||||
break;
|
||||
case KMEMTRACE_TYPE_PAGES:
|
||||
ret = trace_seq_printf(s, "P ");
|
||||
break;
|
||||
default:
|
||||
ret = trace_seq_printf(s, "? ");
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Skip requested/allocated/flags */
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Pointer to allocated */
|
||||
ret = trace_seq_printf(s, "0x%tx ", (ptrdiff_t)entry->ptr);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Skip node */
|
||||
ret = trace_seq_printf(s, " ");
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
/* Call site */
|
||||
ret = seq_print_ip_sym(s, entry->call_site, 0);
|
||||
if (!ret)
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
if (!trace_seq_printf(s, "\n"))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t kmemtrace_print_line(struct trace_iterator *iter)
|
||||
{
|
||||
struct trace_entry *entry = iter->ent;
|
||||
|
||||
switch (entry->type) {
|
||||
case TRACE_KMEM_ALLOC: {
|
||||
struct kmemtrace_alloc_entry *field;
|
||||
trace_assign_type(field, entry);
|
||||
if (kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL)
|
||||
return kmemtrace_print_alloc_compress(iter, field);
|
||||
else
|
||||
return kmemtrace_print_alloc_original(iter, field);
|
||||
}
|
||||
|
||||
case TRACE_KMEM_FREE: {
|
||||
struct kmemtrace_free_entry *field;
|
||||
trace_assign_type(field, entry);
|
||||
if (kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL)
|
||||
return kmemtrace_print_free_compress(iter, field);
|
||||
else
|
||||
return kmemtrace_print_free_original(iter, field);
|
||||
}
|
||||
|
||||
default:
|
||||
return TRACE_TYPE_UNHANDLED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Trace allocations */
|
||||
void kmemtrace_mark_alloc_node(enum kmemtrace_type_id type_id,
|
||||
unsigned long call_site,
|
||||
const void *ptr,
|
||||
size_t bytes_req,
|
||||
size_t bytes_alloc,
|
||||
gfp_t gfp_flags,
|
||||
int node)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct kmemtrace_alloc_entry *entry;
|
||||
struct trace_array *tr = kmemtrace_array;
|
||||
|
||||
if (!kmem_tracing_enabled)
|
||||
return;
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_KMEM_ALLOC,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
|
||||
entry->call_site = call_site;
|
||||
entry->ptr = ptr;
|
||||
entry->bytes_req = bytes_req;
|
||||
entry->bytes_alloc = bytes_alloc;
|
||||
entry->gfp_flags = gfp_flags;
|
||||
entry->node = node;
|
||||
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(kmemtrace_mark_alloc_node);
|
||||
|
||||
void kmemtrace_mark_free(enum kmemtrace_type_id type_id,
|
||||
unsigned long call_site,
|
||||
const void *ptr)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct kmemtrace_free_entry *entry;
|
||||
struct trace_array *tr = kmemtrace_array;
|
||||
|
||||
if (!kmem_tracing_enabled)
|
||||
return;
|
||||
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_KMEM_FREE,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->type_id = type_id;
|
||||
entry->call_site = call_site;
|
||||
entry->ptr = ptr;
|
||||
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(kmemtrace_mark_free);
|
||||
|
||||
static struct tracer kmem_tracer __read_mostly = {
|
||||
.name = "kmemtrace",
|
||||
.init = kmem_trace_init,
|
||||
.reset = kmem_trace_reset,
|
||||
.print_line = kmemtrace_print_line,
|
||||
.print_header = kmemtrace_headers,
|
||||
.flags = &kmem_tracer_flags
|
||||
};
|
||||
|
||||
void kmemtrace_init(void)
|
||||
{
|
||||
/* earliest opportunity to start kmem tracing */
|
||||
}
|
||||
|
||||
static int __init init_kmem_tracer(void)
|
||||
{
|
||||
return register_tracer(&kmem_tracer);
|
||||
}
|
||||
|
||||
device_initcall(init_kmem_tracer);
|
||||
+535
-170
File diff suppressed because it is too large
Load Diff
+1702
-1358
File diff suppressed because it is too large
Load Diff
+267
-48
File diff suppressed because it is too large
Load Diff
+14
-22
@@ -11,6 +11,7 @@
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "trace_output.h"
|
||||
|
||||
static struct trace_array *boot_trace;
|
||||
static bool pre_initcalls_finished;
|
||||
@@ -27,13 +28,13 @@ void start_boot_trace(void)
|
||||
|
||||
void enable_boot_trace(void)
|
||||
{
|
||||
if (pre_initcalls_finished)
|
||||
if (boot_trace && pre_initcalls_finished)
|
||||
tracing_start_sched_switch_record();
|
||||
}
|
||||
|
||||
void disable_boot_trace(void)
|
||||
{
|
||||
if (pre_initcalls_finished)
|
||||
if (boot_trace && pre_initcalls_finished)
|
||||
tracing_stop_sched_switch_record();
|
||||
}
|
||||
|
||||
@@ -42,6 +43,9 @@ static int boot_trace_init(struct trace_array *tr)
|
||||
int cpu;
|
||||
boot_trace = tr;
|
||||
|
||||
if (!tr)
|
||||
return 0;
|
||||
|
||||
for_each_cpu(cpu, cpu_possible_mask)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
@@ -128,10 +132,9 @@ void trace_boot_call(struct boot_trace_call *bt, initcall_t fn)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_boot_call *entry;
|
||||
unsigned long irq_flags;
|
||||
struct trace_array *tr = boot_trace;
|
||||
|
||||
if (!pre_initcalls_finished)
|
||||
if (!tr || !pre_initcalls_finished)
|
||||
return;
|
||||
|
||||
/* Get its name now since this function could
|
||||
@@ -140,18 +143,13 @@ void trace_boot_call(struct boot_trace_call *bt, initcall_t fn)
|
||||
sprint_symbol(bt->func, (unsigned long)fn);
|
||||
preempt_disable();
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_BOOT_CALL,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, 0);
|
||||
entry->ent.type = TRACE_BOOT_CALL;
|
||||
entry->boot_call = *bt;
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
|
||||
trace_wake_up();
|
||||
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
@@ -160,27 +158,21 @@ void trace_boot_ret(struct boot_trace_ret *bt, initcall_t fn)
|
||||
{
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_boot_ret *entry;
|
||||
unsigned long irq_flags;
|
||||
struct trace_array *tr = boot_trace;
|
||||
|
||||
if (!pre_initcalls_finished)
|
||||
if (!tr || !pre_initcalls_finished)
|
||||
return;
|
||||
|
||||
sprint_symbol(bt->func, (unsigned long)fn);
|
||||
preempt_disable();
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_BOOT_RET,
|
||||
sizeof(*entry), 0, 0);
|
||||
if (!event)
|
||||
goto out;
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, 0, 0);
|
||||
entry->ent.type = TRACE_BOOT_RET;
|
||||
entry->boot_ret = *bt;
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
|
||||
trace_wake_up();
|
||||
|
||||
trace_buffer_unlock_commit(tr, event, 0, 0);
|
||||
out:
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
+171
-133
@@ -14,12 +14,17 @@
|
||||
#include <linux/hash.h>
|
||||
#include <linux/fs.h>
|
||||
#include <asm/local.h>
|
||||
|
||||
#include "trace.h"
|
||||
#include "trace_stat.h"
|
||||
#include "trace_output.h"
|
||||
|
||||
#ifdef CONFIG_BRANCH_TRACER
|
||||
|
||||
static struct tracer branch_trace;
|
||||
static int branch_tracing_enabled __read_mostly;
|
||||
static DEFINE_MUTEX(branch_tracing_mutex);
|
||||
|
||||
static struct trace_array *branch_tracer;
|
||||
|
||||
static void
|
||||
@@ -28,7 +33,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
struct trace_array *tr = branch_tracer;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_branch *entry;
|
||||
unsigned long flags, irq_flags;
|
||||
unsigned long flags;
|
||||
int cpu, pc;
|
||||
const char *p;
|
||||
|
||||
@@ -47,15 +52,13 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
if (atomic_inc_return(&tr->data[cpu]->disabled) != 1)
|
||||
goto out;
|
||||
|
||||
event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry),
|
||||
&irq_flags);
|
||||
pc = preempt_count();
|
||||
event = trace_buffer_lock_reserve(tr, TRACE_BRANCH,
|
||||
sizeof(*entry), flags, pc);
|
||||
if (!event)
|
||||
goto out;
|
||||
|
||||
pc = preempt_count();
|
||||
entry = ring_buffer_event_data(event);
|
||||
tracing_generic_entry_update(&entry->ent, flags, pc);
|
||||
entry->ent.type = TRACE_BRANCH;
|
||||
|
||||
/* Strip off the path, only save the file */
|
||||
p = f->file + strlen(f->file);
|
||||
@@ -70,7 +73,7 @@ probe_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
entry->line = f->line;
|
||||
entry->correct = val == expect;
|
||||
|
||||
ring_buffer_unlock_commit(tr->buffer, event, irq_flags);
|
||||
ring_buffer_unlock_commit(tr->buffer, event);
|
||||
|
||||
out:
|
||||
atomic_dec(&tr->data[cpu]->disabled);
|
||||
@@ -88,8 +91,6 @@ void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
|
||||
int enable_branch_tracing(struct trace_array *tr)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&branch_tracing_mutex);
|
||||
branch_tracer = tr;
|
||||
/*
|
||||
@@ -100,7 +101,7 @@ int enable_branch_tracing(struct trace_array *tr)
|
||||
branch_tracing_enabled++;
|
||||
mutex_unlock(&branch_tracing_mutex);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void disable_branch_tracing(void)
|
||||
@@ -128,11 +129,6 @@ static void stop_branch_trace(struct trace_array *tr)
|
||||
|
||||
static int branch_trace_init(struct trace_array *tr)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
tracing_reset(tr, cpu);
|
||||
|
||||
start_branch_trace(tr);
|
||||
return 0;
|
||||
}
|
||||
@@ -142,22 +138,53 @@ static void branch_trace_reset(struct trace_array *tr)
|
||||
stop_branch_trace(tr);
|
||||
}
|
||||
|
||||
struct tracer branch_trace __read_mostly =
|
||||
static enum print_line_t trace_branch_print(struct trace_iterator *iter,
|
||||
int flags)
|
||||
{
|
||||
struct trace_branch *field;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
if (trace_seq_printf(&iter->seq, "[%s] %s:%s:%d\n",
|
||||
field->correct ? " ok " : " MISS ",
|
||||
field->func,
|
||||
field->file,
|
||||
field->line))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
static struct trace_event trace_branch_event = {
|
||||
.type = TRACE_BRANCH,
|
||||
.trace = trace_branch_print,
|
||||
};
|
||||
|
||||
static struct tracer branch_trace __read_mostly =
|
||||
{
|
||||
.name = "branch",
|
||||
.init = branch_trace_init,
|
||||
.reset = branch_trace_reset,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_branch,
|
||||
#endif
|
||||
#endif /* CONFIG_FTRACE_SELFTEST */
|
||||
};
|
||||
|
||||
__init static int init_branch_trace(void)
|
||||
__init static int init_branch_tracer(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_ftrace_event(&trace_branch_event);
|
||||
if (!ret) {
|
||||
printk(KERN_WARNING "Warning: could not register "
|
||||
"branch events\n");
|
||||
return 1;
|
||||
}
|
||||
return register_tracer(&branch_trace);
|
||||
}
|
||||
device_initcall(init_branch_tracer);
|
||||
|
||||
device_initcall(init_branch_trace);
|
||||
#else
|
||||
static inline
|
||||
void trace_likely_condition(struct ftrace_branch_data *f, int val, int expect)
|
||||
@@ -183,65 +210,38 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect)
|
||||
}
|
||||
EXPORT_SYMBOL(ftrace_likely_update);
|
||||
|
||||
struct ftrace_pointer {
|
||||
void *start;
|
||||
void *stop;
|
||||
int hit;
|
||||
};
|
||||
extern unsigned long __start_annotated_branch_profile[];
|
||||
extern unsigned long __stop_annotated_branch_profile[];
|
||||
|
||||
static void *
|
||||
t_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
static int annotated_branch_stat_headers(struct seq_file *m)
|
||||
{
|
||||
const struct ftrace_pointer *f = m->private;
|
||||
struct ftrace_branch_data *p = v;
|
||||
|
||||
(*pos)++;
|
||||
|
||||
if (v == (void *)1)
|
||||
return f->start;
|
||||
|
||||
++p;
|
||||
|
||||
if ((void *)p >= (void *)f->stop)
|
||||
return NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void *t_start(struct seq_file *m, loff_t *pos)
|
||||
{
|
||||
void *t = (void *)1;
|
||||
loff_t l = 0;
|
||||
|
||||
for (; t && l < *pos; t = t_next(m, t, &l))
|
||||
;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void t_stop(struct seq_file *m, void *p)
|
||||
{
|
||||
}
|
||||
|
||||
static int t_show(struct seq_file *m, void *v)
|
||||
{
|
||||
const struct ftrace_pointer *fp = m->private;
|
||||
struct ftrace_branch_data *p = v;
|
||||
const char *f;
|
||||
long percent;
|
||||
|
||||
if (v == (void *)1) {
|
||||
if (fp->hit)
|
||||
seq_printf(m, " miss hit %% ");
|
||||
else
|
||||
seq_printf(m, " correct incorrect %% ");
|
||||
seq_printf(m, " Function "
|
||||
seq_printf(m, " correct incorrect %% ");
|
||||
seq_printf(m, " Function "
|
||||
" File Line\n"
|
||||
" ------- --------- - "
|
||||
" -------- "
|
||||
" ---- ----\n");
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline long get_incorrect_percent(struct ftrace_branch_data *p)
|
||||
{
|
||||
long percent;
|
||||
|
||||
if (p->correct) {
|
||||
percent = p->incorrect * 100;
|
||||
percent /= p->correct + p->incorrect;
|
||||
} else
|
||||
percent = p->incorrect ? 100 : -1;
|
||||
|
||||
return percent;
|
||||
}
|
||||
|
||||
static int branch_stat_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct ftrace_branch_data *p = v;
|
||||
const char *f;
|
||||
long percent;
|
||||
|
||||
/* Only print the file, not the path */
|
||||
f = p->file + strlen(p->file);
|
||||
@@ -252,11 +252,7 @@ static int t_show(struct seq_file *m, void *v)
|
||||
/*
|
||||
* The miss is overlayed on correct, and hit on incorrect.
|
||||
*/
|
||||
if (p->correct) {
|
||||
percent = p->incorrect * 100;
|
||||
percent /= p->correct + p->incorrect;
|
||||
} else
|
||||
percent = p->incorrect ? 100 : -1;
|
||||
percent = get_incorrect_percent(p);
|
||||
|
||||
seq_printf(m, "%8lu %8lu ", p->correct, p->incorrect);
|
||||
if (percent < 0)
|
||||
@@ -267,76 +263,118 @@ static int t_show(struct seq_file *m, void *v)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations tracing_likely_seq_ops = {
|
||||
.start = t_start,
|
||||
.next = t_next,
|
||||
.stop = t_stop,
|
||||
.show = t_show,
|
||||
static void *annotated_branch_stat_start(void)
|
||||
{
|
||||
return __start_annotated_branch_profile;
|
||||
}
|
||||
|
||||
static void *
|
||||
annotated_branch_stat_next(void *v, int idx)
|
||||
{
|
||||
struct ftrace_branch_data *p = v;
|
||||
|
||||
++p;
|
||||
|
||||
if ((void *)p >= (void *)__stop_annotated_branch_profile)
|
||||
return NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int annotated_branch_stat_cmp(void *p1, void *p2)
|
||||
{
|
||||
struct ftrace_branch_data *a = p1;
|
||||
struct ftrace_branch_data *b = p2;
|
||||
|
||||
long percent_a, percent_b;
|
||||
|
||||
percent_a = get_incorrect_percent(a);
|
||||
percent_b = get_incorrect_percent(b);
|
||||
|
||||
if (percent_a < percent_b)
|
||||
return -1;
|
||||
if (percent_a > percent_b)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct tracer_stat annotated_branch_stats = {
|
||||
.name = "branch_annotated",
|
||||
.stat_start = annotated_branch_stat_start,
|
||||
.stat_next = annotated_branch_stat_next,
|
||||
.stat_cmp = annotated_branch_stat_cmp,
|
||||
.stat_headers = annotated_branch_stat_headers,
|
||||
.stat_show = branch_stat_show
|
||||
};
|
||||
|
||||
static int tracing_branch_open(struct inode *inode, struct file *file)
|
||||
__init static int init_annotated_branch_stats(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &tracing_likely_seq_ops);
|
||||
ret = register_stat_tracer(&annotated_branch_stats);
|
||||
if (!ret) {
|
||||
struct seq_file *m = file->private_data;
|
||||
m->private = (void *)inode->i_private;
|
||||
printk(KERN_WARNING "Warning: could not register "
|
||||
"annotated branches stats\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations tracing_branch_fops = {
|
||||
.open = tracing_branch_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
fs_initcall(init_annotated_branch_stats);
|
||||
|
||||
#ifdef CONFIG_PROFILE_ALL_BRANCHES
|
||||
|
||||
extern unsigned long __start_branch_profile[];
|
||||
extern unsigned long __stop_branch_profile[];
|
||||
|
||||
static const struct ftrace_pointer ftrace_branch_pos = {
|
||||
.start = __start_branch_profile,
|
||||
.stop = __stop_branch_profile,
|
||||
.hit = 1,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_PROFILE_ALL_BRANCHES */
|
||||
|
||||
extern unsigned long __start_annotated_branch_profile[];
|
||||
extern unsigned long __stop_annotated_branch_profile[];
|
||||
|
||||
static const struct ftrace_pointer ftrace_annotated_branch_pos = {
|
||||
.start = __start_annotated_branch_profile,
|
||||
.stop = __stop_annotated_branch_profile,
|
||||
};
|
||||
|
||||
static __init int ftrace_branch_init(void)
|
||||
static int all_branch_stat_headers(struct seq_file *m)
|
||||
{
|
||||
struct dentry *d_tracer;
|
||||
struct dentry *entry;
|
||||
|
||||
d_tracer = tracing_init_dentry();
|
||||
|
||||
entry = debugfs_create_file("profile_annotated_branch", 0444, d_tracer,
|
||||
(void *)&ftrace_annotated_branch_pos,
|
||||
&tracing_branch_fops);
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs "
|
||||
"'profile_annotatet_branch' entry\n");
|
||||
|
||||
#ifdef CONFIG_PROFILE_ALL_BRANCHES
|
||||
entry = debugfs_create_file("profile_branch", 0444, d_tracer,
|
||||
(void *)&ftrace_branch_pos,
|
||||
&tracing_branch_fops);
|
||||
if (!entry)
|
||||
pr_warning("Could not create debugfs"
|
||||
" 'profile_branch' entry\n");
|
||||
#endif
|
||||
|
||||
seq_printf(m, " miss hit %% ");
|
||||
seq_printf(m, " Function "
|
||||
" File Line\n"
|
||||
" ------- --------- - "
|
||||
" -------- "
|
||||
" ---- ----\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
device_initcall(ftrace_branch_init);
|
||||
static void *all_branch_stat_start(void)
|
||||
{
|
||||
return __start_branch_profile;
|
||||
}
|
||||
|
||||
static void *
|
||||
all_branch_stat_next(void *v, int idx)
|
||||
{
|
||||
struct ftrace_branch_data *p = v;
|
||||
|
||||
++p;
|
||||
|
||||
if ((void *)p >= (void *)__stop_branch_profile)
|
||||
return NULL;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static struct tracer_stat all_branch_stats = {
|
||||
.name = "branch_all",
|
||||
.stat_start = all_branch_stat_start,
|
||||
.stat_next = all_branch_stat_next,
|
||||
.stat_headers = all_branch_stat_headers,
|
||||
.stat_show = branch_stat_show
|
||||
};
|
||||
|
||||
__init static int all_annotated_branch_stats(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_stat_tracer(&all_branch_stats);
|
||||
if (!ret) {
|
||||
printk(KERN_WARNING "Warning: could not register "
|
||||
"all branches stats\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
fs_initcall(all_annotated_branch_stats);
|
||||
#endif /* CONFIG_PROFILE_ALL_BRANCHES */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user