Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic/random-tracing into perf/core
This commit is contained in:
commit
23c2875725
10 changed files with 15 additions and 541 deletions
|
@ -508,9 +508,6 @@ extern void tracing_start(void);
|
|||
extern void tracing_stop(void);
|
||||
extern void ftrace_off_permanent(void);
|
||||
|
||||
extern void
|
||||
ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3);
|
||||
|
||||
static inline void __attribute__ ((format (printf, 1, 2)))
|
||||
____trace_printk_check_format(const char *fmt, ...)
|
||||
{
|
||||
|
@ -586,8 +583,6 @@ __ftrace_vprintk(unsigned long ip, const char *fmt, va_list ap);
|
|||
|
||||
extern void ftrace_dump(enum ftrace_dump_mode oops_dump_mode);
|
||||
#else
|
||||
static inline void
|
||||
ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3) { }
|
||||
static inline int
|
||||
trace_printk(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
|
||||
|
||||
|
|
|
@ -2434,18 +2434,6 @@ static inline void set_task_cpu(struct task_struct *p, unsigned int cpu)
|
|||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#ifdef CONFIG_TRACING
|
||||
extern void
|
||||
__trace_special(void *__tr, void *__data,
|
||||
unsigned long arg1, unsigned long arg2, unsigned long arg3);
|
||||
#else
|
||||
static inline void
|
||||
__trace_special(void *__tr, void *__data,
|
||||
unsigned long arg1, unsigned long arg2, unsigned long arg3)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
extern long sched_setaffinity(pid_t pid, const struct cpumask *new_mask);
|
||||
extern long sched_getaffinity(pid_t pid, struct cpumask *mask);
|
||||
|
||||
|
|
|
@ -194,15 +194,6 @@ config PREEMPT_TRACER
|
|||
enabled. This option and the irqs-off timing option can be
|
||||
used together or separately.)
|
||||
|
||||
config SYSPROF_TRACER
|
||||
bool "Sysprof Tracer"
|
||||
depends on X86
|
||||
select GENERIC_TRACER
|
||||
select CONTEXT_SWITCH_TRACER
|
||||
help
|
||||
This tracer provides the trace needed by the 'Sysprof' userspace
|
||||
tool.
|
||||
|
||||
config SCHED_TRACER
|
||||
bool "Scheduling Latency Tracer"
|
||||
select GENERIC_TRACER
|
||||
|
|
|
@ -30,7 +30,6 @@ 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
|
||||
obj-$(CONFIG_IRQSOFF_TRACER) += trace_irqsoff.o
|
||||
obj-$(CONFIG_PREEMPT_TRACER) += trace_irqsoff.o
|
||||
|
|
|
@ -1331,61 +1331,6 @@ static void __trace_userstack(struct trace_array *tr, unsigned long flags)
|
|||
|
||||
#endif /* CONFIG_STACKTRACE */
|
||||
|
||||
static void
|
||||
ftrace_trace_special(void *__tr,
|
||||
unsigned long arg1, unsigned long arg2, unsigned long arg3,
|
||||
int pc)
|
||||
{
|
||||
struct ftrace_event_call *call = &event_special;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_array *tr = __tr;
|
||||
struct ring_buffer *buffer = tr->buffer;
|
||||
struct special_entry *entry;
|
||||
|
||||
event = trace_buffer_lock_reserve(buffer, TRACE_SPECIAL,
|
||||
sizeof(*entry), 0, pc);
|
||||
if (!event)
|
||||
return;
|
||||
entry = ring_buffer_event_data(event);
|
||||
entry->arg1 = arg1;
|
||||
entry->arg2 = arg2;
|
||||
entry->arg3 = arg3;
|
||||
|
||||
if (!filter_check_discard(call, entry, buffer, event))
|
||||
trace_buffer_unlock_commit(buffer, event, 0, pc);
|
||||
}
|
||||
|
||||
void
|
||||
__trace_special(void *__tr, void *__data,
|
||||
unsigned long arg1, unsigned long arg2, unsigned long arg3)
|
||||
{
|
||||
ftrace_trace_special(__tr, arg1, arg2, arg3, preempt_count());
|
||||
}
|
||||
|
||||
void
|
||||
ftrace_special(unsigned long arg1, unsigned long arg2, unsigned long arg3)
|
||||
{
|
||||
struct trace_array *tr = &global_trace;
|
||||
struct trace_array_cpu *data;
|
||||
unsigned long flags;
|
||||
int cpu;
|
||||
int pc;
|
||||
|
||||
if (tracing_disabled)
|
||||
return;
|
||||
|
||||
pc = preempt_count();
|
||||
local_irq_save(flags);
|
||||
cpu = raw_smp_processor_id();
|
||||
data = tr->data[cpu];
|
||||
|
||||
if (likely(atomic_inc_return(&data->disabled) == 1))
|
||||
ftrace_trace_special(tr, arg1, arg2, arg3, pc);
|
||||
|
||||
atomic_dec(&data->disabled);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_vbprintk - write binary msg to tracing buffer
|
||||
*
|
||||
|
@ -2393,6 +2338,7 @@ static const struct file_operations show_traces_fops = {
|
|||
.open = show_traces_open,
|
||||
.read = seq_read,
|
||||
.release = seq_release,
|
||||
.llseek = seq_lseek,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -2486,6 +2432,7 @@ static const struct file_operations tracing_cpumask_fops = {
|
|||
.open = tracing_open_generic,
|
||||
.read = tracing_cpumask_read,
|
||||
.write = tracing_cpumask_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static int tracing_trace_options_show(struct seq_file *m, void *v)
|
||||
|
@ -2652,6 +2599,7 @@ tracing_readme_read(struct file *filp, char __user *ubuf,
|
|||
static const struct file_operations tracing_readme_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_readme_read,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
|
@ -2702,6 +2650,7 @@ tracing_saved_cmdlines_read(struct file *file, char __user *ubuf,
|
|||
static const struct file_operations tracing_saved_cmdlines_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_saved_cmdlines_read,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
|
@ -3031,6 +2980,7 @@ static int tracing_open_pipe(struct inode *inode, struct file *filp)
|
|||
if (iter->trace->pipe_open)
|
||||
iter->trace->pipe_open(iter);
|
||||
|
||||
nonseekable_open(inode, filp);
|
||||
out:
|
||||
mutex_unlock(&trace_types_lock);
|
||||
return ret;
|
||||
|
@ -3589,18 +3539,21 @@ static const struct file_operations tracing_max_lat_fops = {
|
|||
.open = tracing_open_generic,
|
||||
.read = tracing_max_lat_read,
|
||||
.write = tracing_max_lat_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_ctrl_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_ctrl_read,
|
||||
.write = tracing_ctrl_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations set_tracer_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_set_trace_read,
|
||||
.write = tracing_set_trace_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_pipe_fops = {
|
||||
|
@ -3609,17 +3562,20 @@ static const struct file_operations tracing_pipe_fops = {
|
|||
.read = tracing_read_pipe,
|
||||
.splice_read = tracing_splice_read_pipe,
|
||||
.release = tracing_release_pipe,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_entries_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_entries_read,
|
||||
.write = tracing_entries_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_mark_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.write = tracing_mark_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static const struct file_operations trace_clock_fops = {
|
||||
|
@ -3925,6 +3881,7 @@ tracing_stats_read(struct file *filp, char __user *ubuf,
|
|||
static const struct file_operations tracing_stats_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_stats_read,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
|
@ -3961,6 +3918,7 @@ tracing_read_dyn_info(struct file *filp, char __user *ubuf,
|
|||
static const struct file_operations tracing_dyn_info_fops = {
|
||||
.open = tracing_open_generic,
|
||||
.read = tracing_read_dyn_info,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
@ -4114,6 +4072,7 @@ static const struct file_operations trace_options_fops = {
|
|||
.open = tracing_open_generic,
|
||||
.read = trace_options_read,
|
||||
.write = trace_options_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
|
@ -4165,6 +4124,7 @@ static const struct file_operations trace_options_core_fops = {
|
|||
.open = tracing_open_generic,
|
||||
.read = trace_options_core_read,
|
||||
.write = trace_options_core_write,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
struct dentry *trace_create_file(const char *name,
|
||||
|
@ -4354,9 +4314,6 @@ static __init int tracer_init_debugfs(void)
|
|||
trace_create_file("dyn_ftrace_total_info", 0444, d_tracer,
|
||||
&ftrace_update_tot_cnt, &tracing_dyn_info_fops);
|
||||
#endif
|
||||
#ifdef CONFIG_SYSPROF_TRACER
|
||||
init_tracer_sysprof_debugfs(d_tracer);
|
||||
#endif
|
||||
|
||||
create_trace_options_dir();
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ enum trace_type {
|
|||
TRACE_STACK,
|
||||
TRACE_PRINT,
|
||||
TRACE_BPRINT,
|
||||
TRACE_SPECIAL,
|
||||
TRACE_MMIO_RW,
|
||||
TRACE_MMIO_MAP,
|
||||
TRACE_BRANCH,
|
||||
|
@ -189,7 +188,6 @@ extern void __ftrace_bad_type(void);
|
|||
IF_ASSIGN(var, ent, struct userstack_entry, TRACE_USER_STACK);\
|
||||
IF_ASSIGN(var, ent, struct print_entry, TRACE_PRINT); \
|
||||
IF_ASSIGN(var, ent, struct bprint_entry, TRACE_BPRINT); \
|
||||
IF_ASSIGN(var, ent, struct special_entry, 0); \
|
||||
IF_ASSIGN(var, ent, struct trace_mmiotrace_rw, \
|
||||
TRACE_MMIO_RW); \
|
||||
IF_ASSIGN(var, ent, struct trace_mmiotrace_map, \
|
||||
|
@ -296,7 +294,6 @@ struct dentry *trace_create_file(const char *name,
|
|||
const struct file_operations *fops);
|
||||
|
||||
struct dentry *tracing_init_dentry(void);
|
||||
void init_tracer_sysprof_debugfs(struct dentry *d_tracer);
|
||||
|
||||
struct ring_buffer_event;
|
||||
|
||||
|
@ -333,11 +330,6 @@ void tracing_sched_wakeup_trace(struct trace_array *tr,
|
|||
struct task_struct *wakee,
|
||||
struct task_struct *cur,
|
||||
unsigned long flags, int pc);
|
||||
void trace_special(struct trace_array *tr,
|
||||
struct trace_array_cpu *data,
|
||||
unsigned long arg1,
|
||||
unsigned long arg2,
|
||||
unsigned long arg3, int pc);
|
||||
void trace_function(struct trace_array *tr,
|
||||
unsigned long ip,
|
||||
unsigned long parent_ip,
|
||||
|
@ -428,8 +420,6 @@ extern int trace_selftest_startup_nop(struct tracer *trace,
|
|||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_sched_switch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_sysprof(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
extern int trace_selftest_startup_branch(struct tracer *trace,
|
||||
struct trace_array *tr);
|
||||
#endif /* CONFIG_FTRACE_STARTUP_TEST */
|
||||
|
|
|
@ -150,23 +150,6 @@ FTRACE_ENTRY_DUP(wakeup, ctx_switch_entry,
|
|||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* Special (free-form) trace entry:
|
||||
*/
|
||||
FTRACE_ENTRY(special, special_entry,
|
||||
|
||||
TRACE_SPECIAL,
|
||||
|
||||
F_STRUCT(
|
||||
__field( unsigned long, arg1 )
|
||||
__field( unsigned long, arg2 )
|
||||
__field( unsigned long, arg3 )
|
||||
),
|
||||
|
||||
F_printk("(%08lx) (%08lx) (%08lx)",
|
||||
__entry->arg1, __entry->arg2, __entry->arg3)
|
||||
);
|
||||
|
||||
/*
|
||||
* Stack-trace entry:
|
||||
*/
|
||||
|
|
|
@ -1069,65 +1069,6 @@ static struct trace_event trace_wake_event = {
|
|||
.funcs = &trace_wake_funcs,
|
||||
};
|
||||
|
||||
/* TRACE_SPECIAL */
|
||||
static enum print_line_t trace_special_print(struct trace_iterator *iter,
|
||||
int flags, struct trace_event *event)
|
||||
{
|
||||
struct special_entry *field;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
if (!trace_seq_printf(&iter->seq, "# %ld %ld %ld\n",
|
||||
field->arg1,
|
||||
field->arg2,
|
||||
field->arg3))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t trace_special_hex(struct trace_iterator *iter,
|
||||
int flags, struct trace_event *event)
|
||||
{
|
||||
struct special_entry *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
SEQ_PUT_HEX_FIELD_RET(s, field->arg1);
|
||||
SEQ_PUT_HEX_FIELD_RET(s, field->arg2);
|
||||
SEQ_PUT_HEX_FIELD_RET(s, field->arg3);
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static enum print_line_t trace_special_bin(struct trace_iterator *iter,
|
||||
int flags, struct trace_event *event)
|
||||
{
|
||||
struct special_entry *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
SEQ_PUT_FIELD_RET(s, field->arg1);
|
||||
SEQ_PUT_FIELD_RET(s, field->arg2);
|
||||
SEQ_PUT_FIELD_RET(s, field->arg3);
|
||||
|
||||
return TRACE_TYPE_HANDLED;
|
||||
}
|
||||
|
||||
static struct trace_event_functions trace_special_funcs = {
|
||||
.trace = trace_special_print,
|
||||
.raw = trace_special_print,
|
||||
.hex = trace_special_hex,
|
||||
.binary = trace_special_bin,
|
||||
};
|
||||
|
||||
static struct trace_event trace_special_event = {
|
||||
.type = TRACE_SPECIAL,
|
||||
.funcs = &trace_special_funcs,
|
||||
};
|
||||
|
||||
/* TRACE_STACK */
|
||||
|
||||
static enum print_line_t trace_stack_print(struct trace_iterator *iter,
|
||||
|
@ -1161,9 +1102,6 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
|
|||
|
||||
static struct trace_event_functions trace_stack_funcs = {
|
||||
.trace = trace_stack_print,
|
||||
.raw = trace_special_print,
|
||||
.hex = trace_special_hex,
|
||||
.binary = trace_special_bin,
|
||||
};
|
||||
|
||||
static struct trace_event trace_stack_event = {
|
||||
|
@ -1194,9 +1132,6 @@ static enum print_line_t trace_user_stack_print(struct trace_iterator *iter,
|
|||
|
||||
static struct trace_event_functions trace_user_stack_funcs = {
|
||||
.trace = trace_user_stack_print,
|
||||
.raw = trace_special_print,
|
||||
.hex = trace_special_hex,
|
||||
.binary = trace_special_bin,
|
||||
};
|
||||
|
||||
static struct trace_event trace_user_stack_event = {
|
||||
|
@ -1314,7 +1249,6 @@ static struct trace_event *events[] __initdata = {
|
|||
&trace_fn_event,
|
||||
&trace_ctx_event,
|
||||
&trace_wake_event,
|
||||
&trace_special_event,
|
||||
&trace_stack_event,
|
||||
&trace_user_stack_event,
|
||||
&trace_bprint_event,
|
||||
|
|
|
@ -13,7 +13,6 @@ static inline int trace_valid_entry(struct trace_entry *entry)
|
|||
case TRACE_WAKE:
|
||||
case TRACE_STACK:
|
||||
case TRACE_PRINT:
|
||||
case TRACE_SPECIAL:
|
||||
case TRACE_BRANCH:
|
||||
case TRACE_GRAPH_ENT:
|
||||
case TRACE_GRAPH_RET:
|
||||
|
@ -690,38 +689,6 @@ trace_selftest_startup_sched_switch(struct tracer *trace, struct trace_array *tr
|
|||
}
|
||||
#endif /* CONFIG_CONTEXT_SWITCH_TRACER */
|
||||
|
||||
#ifdef CONFIG_SYSPROF_TRACER
|
||||
int
|
||||
trace_selftest_startup_sysprof(struct tracer *trace, struct trace_array *tr)
|
||||
{
|
||||
unsigned long count;
|
||||
int ret;
|
||||
|
||||
/* start the tracing */
|
||||
ret = tracer_init(trace, tr);
|
||||
if (ret) {
|
||||
warn_failed_init_tracer(trace, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Sleep for a 1/10 of a second */
|
||||
msleep(100);
|
||||
/* stop the tracing. */
|
||||
tracing_stop();
|
||||
/* check the trace buffer */
|
||||
ret = trace_test_buffer(tr, &count);
|
||||
trace->reset(tr);
|
||||
tracing_start();
|
||||
|
||||
if (!ret && !count) {
|
||||
printk(KERN_CONT ".. no entries found ..");
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_SYSPROF_TRACER */
|
||||
|
||||
#ifdef CONFIG_BRANCH_TRACER
|
||||
int
|
||||
trace_selftest_startup_branch(struct tracer *trace, struct trace_array *tr)
|
||||
|
|
|
@ -1,330 +0,0 @@
|
|||
/*
|
||||
* trace stack traces
|
||||
*
|
||||
* Copyright (C) 2004-2008, Soeren Sandmann
|
||||
* Copyright (C) 2007 Steven Rostedt <srostedt@redhat.com>
|
||||
* Copyright (C) 2008 Ingo Molnar <mingo@redhat.com>
|
||||
*/
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/hrtimer.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
#include "trace.h"
|
||||
|
||||
static struct trace_array *sysprof_trace;
|
||||
static int __read_mostly tracer_enabled;
|
||||
|
||||
/*
|
||||
* 1 msec sample interval by default:
|
||||
*/
|
||||
static unsigned long sample_period = 1000000;
|
||||
static const unsigned int sample_max_depth = 512;
|
||||
|
||||
static DEFINE_MUTEX(sample_timer_lock);
|
||||
/*
|
||||
* Per CPU hrtimers that do the profiling:
|
||||
*/
|
||||
static DEFINE_PER_CPU(struct hrtimer, stack_trace_hrtimer);
|
||||
|
||||
struct stack_frame_user {
|
||||
const void __user *next_fp;
|
||||
unsigned long return_address;
|
||||
};
|
||||
|
||||
static int
|
||||
copy_stack_frame(const void __user *fp, struct stack_frame_user *frame)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!access_ok(VERIFY_READ, fp, sizeof(*frame)))
|
||||
return 0;
|
||||
|
||||
ret = 1;
|
||||
pagefault_disable();
|
||||
if (__copy_from_user_inatomic(frame, fp, sizeof(*frame)))
|
||||
ret = 0;
|
||||
pagefault_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct backtrace_info {
|
||||
struct trace_array_cpu *data;
|
||||
struct trace_array *tr;
|
||||
int pos;
|
||||
};
|
||||
|
||||
static void
|
||||
backtrace_warning_symbol(void *data, char *msg, unsigned long symbol)
|
||||
{
|
||||
/* Ignore warnings */
|
||||
}
|
||||
|
||||
static void backtrace_warning(void *data, char *msg)
|
||||
{
|
||||
/* Ignore warnings */
|
||||
}
|
||||
|
||||
static int backtrace_stack(void *data, char *name)
|
||||
{
|
||||
/* Don't bother with IRQ stacks for now */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void backtrace_address(void *data, unsigned long addr, int reliable)
|
||||
{
|
||||
struct backtrace_info *info = data;
|
||||
|
||||
if (info->pos < sample_max_depth && reliable) {
|
||||
__trace_special(info->tr, info->data, 1, addr, 0);
|
||||
|
||||
info->pos++;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct stacktrace_ops backtrace_ops = {
|
||||
.warning = backtrace_warning,
|
||||
.warning_symbol = backtrace_warning_symbol,
|
||||
.stack = backtrace_stack,
|
||||
.address = backtrace_address,
|
||||
.walk_stack = print_context_stack,
|
||||
};
|
||||
|
||||
static int
|
||||
trace_kernel(struct pt_regs *regs, struct trace_array *tr,
|
||||
struct trace_array_cpu *data)
|
||||
{
|
||||
struct backtrace_info info;
|
||||
unsigned long bp;
|
||||
char *stack;
|
||||
|
||||
info.tr = tr;
|
||||
info.data = data;
|
||||
info.pos = 1;
|
||||
|
||||
__trace_special(info.tr, info.data, 1, regs->ip, 0);
|
||||
|
||||
stack = ((char *)regs + sizeof(struct pt_regs));
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
bp = regs->bp;
|
||||
#else
|
||||
bp = 0;
|
||||
#endif
|
||||
|
||||
dump_trace(NULL, regs, (void *)stack, bp, &backtrace_ops, &info);
|
||||
|
||||
return info.pos;
|
||||
}
|
||||
|
||||
static void timer_notify(struct pt_regs *regs, int cpu)
|
||||
{
|
||||
struct trace_array_cpu *data;
|
||||
struct stack_frame_user frame;
|
||||
struct trace_array *tr;
|
||||
const void __user *fp;
|
||||
int is_user;
|
||||
int i;
|
||||
|
||||
if (!regs)
|
||||
return;
|
||||
|
||||
tr = sysprof_trace;
|
||||
data = tr->data[cpu];
|
||||
is_user = user_mode(regs);
|
||||
|
||||
if (!current || current->pid == 0)
|
||||
return;
|
||||
|
||||
if (is_user && current->state != TASK_RUNNING)
|
||||
return;
|
||||
|
||||
__trace_special(tr, data, 0, 0, current->pid);
|
||||
|
||||
if (!is_user)
|
||||
i = trace_kernel(regs, tr, data);
|
||||
else
|
||||
i = 0;
|
||||
|
||||
/*
|
||||
* Trace user stack if we are not a kernel thread
|
||||
*/
|
||||
if (current->mm && i < sample_max_depth) {
|
||||
regs = (struct pt_regs *)current->thread.sp0 - 1;
|
||||
|
||||
fp = (void __user *)regs->bp;
|
||||
|
||||
__trace_special(tr, data, 2, regs->ip, 0);
|
||||
|
||||
while (i < sample_max_depth) {
|
||||
frame.next_fp = NULL;
|
||||
frame.return_address = 0;
|
||||
if (!copy_stack_frame(fp, &frame))
|
||||
break;
|
||||
if ((unsigned long)fp < regs->sp)
|
||||
break;
|
||||
|
||||
__trace_special(tr, data, 2, frame.return_address,
|
||||
(unsigned long)fp);
|
||||
fp = frame.next_fp;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Special trace entry if we overflow the max depth:
|
||||
*/
|
||||
if (i == sample_max_depth)
|
||||
__trace_special(tr, data, -1, -1, -1);
|
||||
|
||||
__trace_special(tr, data, 3, current->pid, i);
|
||||
}
|
||||
|
||||
static enum hrtimer_restart stack_trace_timer_fn(struct hrtimer *hrtimer)
|
||||
{
|
||||
/* trace here */
|
||||
timer_notify(get_irq_regs(), smp_processor_id());
|
||||
|
||||
hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));
|
||||
|
||||
return HRTIMER_RESTART;
|
||||
}
|
||||
|
||||
static void start_stack_timer(void *unused)
|
||||
{
|
||||
struct hrtimer *hrtimer = &__get_cpu_var(stack_trace_hrtimer);
|
||||
|
||||
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer->function = stack_trace_timer_fn;
|
||||
|
||||
hrtimer_start(hrtimer, ns_to_ktime(sample_period),
|
||||
HRTIMER_MODE_REL_PINNED);
|
||||
}
|
||||
|
||||
static void start_stack_timers(void)
|
||||
{
|
||||
on_each_cpu(start_stack_timer, NULL, 1);
|
||||
}
|
||||
|
||||
static void stop_stack_timer(int cpu)
|
||||
{
|
||||
struct hrtimer *hrtimer = &per_cpu(stack_trace_hrtimer, cpu);
|
||||
|
||||
hrtimer_cancel(hrtimer);
|
||||
}
|
||||
|
||||
static void stop_stack_timers(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
stop_stack_timer(cpu);
|
||||
}
|
||||
|
||||
static void stop_stack_trace(struct trace_array *tr)
|
||||
{
|
||||
mutex_lock(&sample_timer_lock);
|
||||
stop_stack_timers();
|
||||
tracer_enabled = 0;
|
||||
mutex_unlock(&sample_timer_lock);
|
||||
}
|
||||
|
||||
static int stack_trace_init(struct trace_array *tr)
|
||||
{
|
||||
sysprof_trace = tr;
|
||||
|
||||
tracing_start_cmdline_record();
|
||||
|
||||
mutex_lock(&sample_timer_lock);
|
||||
start_stack_timers();
|
||||
tracer_enabled = 1;
|
||||
mutex_unlock(&sample_timer_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stack_trace_reset(struct trace_array *tr)
|
||||
{
|
||||
tracing_stop_cmdline_record();
|
||||
stop_stack_trace(tr);
|
||||
}
|
||||
|
||||
static struct tracer stack_trace __read_mostly =
|
||||
{
|
||||
.name = "sysprof",
|
||||
.init = stack_trace_init,
|
||||
.reset = stack_trace_reset,
|
||||
#ifdef CONFIG_FTRACE_SELFTEST
|
||||
.selftest = trace_selftest_startup_sysprof,
|
||||
#endif
|
||||
};
|
||||
|
||||
__init static int init_stack_trace(void)
|
||||
{
|
||||
return register_tracer(&stack_trace);
|
||||
}
|
||||
device_initcall(init_stack_trace);
|
||||
|
||||
#define MAX_LONG_DIGITS 22
|
||||
|
||||
static ssize_t
|
||||
sysprof_sample_read(struct file *filp, char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char buf[MAX_LONG_DIGITS];
|
||||
int r;
|
||||
|
||||
r = sprintf(buf, "%ld\n", nsecs_to_usecs(sample_period));
|
||||
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sysprof_sample_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
{
|
||||
char buf[MAX_LONG_DIGITS];
|
||||
unsigned long val;
|
||||
|
||||
if (cnt > MAX_LONG_DIGITS-1)
|
||||
cnt = MAX_LONG_DIGITS-1;
|
||||
|
||||
if (copy_from_user(&buf, ubuf, cnt))
|
||||
return -EFAULT;
|
||||
|
||||
buf[cnt] = 0;
|
||||
|
||||
val = simple_strtoul(buf, NULL, 10);
|
||||
/*
|
||||
* Enforce a minimum sample period of 100 usecs:
|
||||
*/
|
||||
if (val < 100)
|
||||
val = 100;
|
||||
|
||||
mutex_lock(&sample_timer_lock);
|
||||
stop_stack_timers();
|
||||
sample_period = val * 1000;
|
||||
start_stack_timers();
|
||||
mutex_unlock(&sample_timer_lock);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
||||
static const struct file_operations sysprof_sample_fops = {
|
||||
.read = sysprof_sample_read,
|
||||
.write = sysprof_sample_write,
|
||||
};
|
||||
|
||||
void init_tracer_sysprof_debugfs(struct dentry *d_tracer)
|
||||
{
|
||||
|
||||
trace_create_file("sysprof_sample_period", 0644,
|
||||
d_tracer, NULL, &sysprof_sample_fops);
|
||||
}
|
Loading…
Reference in a new issue